我正在尝试使用stdin
从fgets()
读取UTF-8字符串。控制台输入模式之前已设置为CP_UTF8
。我还在PowerShell中将控制台字体设置为Lucida Console。最后,我通过使用Ä
将德语printf()
(UTF-8:0xC3,0x84)打印到控制台,验证了UTF-8输出是否有效。这工作正常,但fgets()
似乎无法从控制台读取UTF-8。这是一个小测试程序:
#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
unsigned char s[64];
memset(s, 0, 64);
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
printf("UTF-8 Test: %c%c\n", 0xc3, 0x84); // print Ä
fgets(s, 64, stdin);
printf("Result: %d %d\n", s[0], s[1]);
return 0;
}
运行此程序并输入“Ä”然后点击ENTER时,它只打印以下内容:
Result: 0 0
即。没有写到s
。但是,当输入“A”时,我得到以下正确的结果:
Result: 65 10
那么如何让fgets()
在Windows上使用UTF-8字符呢?
修改
根据Barmak的解释,我现在更新了我的代码以使用wchar_t
函数而不是ANSI函数。但是,它仍然无效。这是我的代码:
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
int main(int argc, char *argv[])
{
wchar_t s[64];
memset(s, 0, 64 * sizeof(wchar_t));
_setmode(_fileno(stdin), _O_U16TEXT);
fgetws(s, 64, stdin);
wprintf(L"Result: %d\n", s[0]);
return 0;
}
输入A
时,程序会打印Result: 3393
,但我希望它是65
。输入Ä
时,程序会打印Result: 0
,但我希望它是196
。到底发生了什么事?为什么现在甚至没有为ASCII字符工作?我只使用fgets()
的旧程序可以正常使用A
之类的ASCII字符,但只有Ä
这样的非ASCII字符才会失败。但新版本甚至不适用于ASCII字符,或3393
A
的正确结果是65
?我希望它是var regex = new RegExp('^/(.+)/(.*)$')
function stringToRegex(s) {
var match = s.match(regex)
return new RegExp(match[1], match[2])
}
var test = new RegExp("weather", "gi")
console.log(stringToRegex(test.toString()))
console.log(stringToRegex(test.toString()).toString() === test.toString())
。我现在很困惑......请帮忙!
答案 0 :(得分:3)
Windows使用UTF16。最有可能是Windows&#39;控制台不支持UTF8。
使用UTF16和宽字符串函数(wcsxxx
而不是strxxx
)。然后,您可以使用WideCharToMultiByte
将UTF16转换为UTF8。例如:
#include <stdio.h>
#include <string.h>
#include <io.h> //for _setmode
#include <fcntl.h> //for _O_U16TEXT
int main()
{
_setmode(_fileno(stdout), _O_U16TEXT);
_setmode(_fileno(stdin), _O_U16TEXT);
wchar_t s[64];
fgetws(s, 64, stdin);
_putws(s);
return 0;
}
请注意,在调用_setmode(_fileno(stdout), _O_U16TEXT)
后您无法使用ANSI打印功能,必须重置它。您可以尝试下面的功能,重置文本模式。
char* mygets(int wlen)
{
//may require fflush here, see _setmode documentation
int save = _setmode(_fileno(stdin), _O_U16TEXT);
wchar_t *wstr = malloc(wlen * sizeof(wchar_t));
fgetws(wstr, wlen, stdin);
//make UTF-8:
int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, 0, 0, 0, 0);
if (!len) return NULL;
char* str = malloc(len);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, 0, 0);
free(wstr);
_setmode(_fileno(stdin), save);
return str;
}
void myputs(const char* str)
{
//may require fflush here, see _setmode documentation
int save = _setmode(_fileno(stdout), _O_U16TEXT);
//make UTF-16
int wlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0);
if (!wlen) return;
wchar_t* wstr = malloc(wlen * sizeof(wchar_t));
memset(wstr, 0, wlen * 2);
MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, wlen);
_putws(wstr);
_setmode(_fileno(stdout), save);
}
int main()
{
char* utf8 = mygets(100);
if (utf8)
{
myputs(utf8);
free(utf8);
}
return 0;
}
答案 1 :(得分:2)
所有Windows本机字符串操作(极少有例外)都在UNICODE(UTF-16)中 - 所以我们必须在任何地方使用unicode函数。使用ANSI变体 - 非常糟糕的做法。如果您将在您的示例中使用unicode函数 - 所有这些都将正常工作。用ANSI这不起作用.. windows bug! 我可以用所有细节(在win 8.1上研究)来涵盖这个:
1)在控制台服务器进程中存在2个全局变量:
UINT gInputCodePage, gOutputCodePage;
它可以通过GetConsoleCP / SetConsoleCP和GetConsoleOutputCP / SetConsoleOutputCP读/写。 当需要转换时,它们用作WideCharToMultiByte / MultiByteToWideChar的第一个参数。如果你只使用unicode函数 - 它们从未使用过
2.a)当您写入控制台UNICODE文本时 - 它将按原样写入而不进行任何转换。在服务器端,这在SB_DoSrvWriteConsole函数中完成。看图片: 2.b)当您写入控制台ANSI文本时 - SB_DoSrvWriteConsole也将被调用,但是还有一个额外的步骤--MultiByteToWideChar(gOutputCodePage,...) - 您的文本将首先转换为UNICODE。 但在这一刻。看: 在MultiByteToWideChar中调用cchWideChar == cbMultiByte。如果我们只使用'english'字符集(字符&lt; 0x80)UNICODE的长度和字符中的多字节字符串总是相等,但是使用其他语言 - 通常的多字节版本使用比UNICODE更多的字符但是这里没有问题,只是输出缓冲区的大小更多需要,但没关系。所以printf一般都会正常工作。仅限一个注释 - 如果您在源代码中硬编码多字节字符串 - 更快的是它将采用CP_ACP格式,并且使用CP_UTF8转换为UNICODE - 会给出不正确的结果。所以这取决于您的源文件保存在磁盘上的格式:)
3.a)当你从具有UNICODE功能的控制台读取时 - 你得到了完全UNICODE文本。这里没有任何问题。如果需要 - 你可以通过自我转换为多字节直接
3.b)当你从带有ANSI函数的控制台读取时 - 服务器首先将UNICODE字符串转换为ANSI,然后返回给你的ANSI表单。这是由功能
完成的int ConvertToOem(UINT CodePage /*=gInputCodePage*/, PCWSTR lpWideCharStr, int cchWideChar, PSTR lpMultiByteStr, int cbMultiByte)
{
if (CodePage == g_OEMCP)
{
ULONG BytesInOemString;
return 0 > RtlUnicodeToOemN(lpMultiByteStr, cbMultiByte, &BytesInOemString, lpWideCharStr, cchWideChar * sizeof(WCHAR)) ? 0 : BytesInOemString;
}
return WideCharToMultiByte(CodePage, 0, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, 0, 0);
}
但是让我们看看更接近,ConvertToOem如何调用: 这里再次cbMultiByte == cchWideChar,但这是100%的错误!多字节字符串可以比UNICODE更长(当然是chars)。例如“Ä” - 这是1个UNICODE字符和2个UTF8字符。结果WideCharToMultiByte 返回0.(ERROR_INSUFFICIENT_BUFFER)