用C语言从文件中读取unicode字符

用C语言从文件中读取unicode字符,c,windows,unicode,character-encoding,console,C,Windows,Unicode,Character Encoding,Console,我正在尝试从.csv文件中读取UTF-8字符串,然后将其写入控制台 a.csv内容: Gijón 在对这一主题进行了一整天的研究之后,我发现执行这种操作的合理方式应该类似于以下内容: int main(int argc, char *argv[]) { char *locale = setlocale(LC_ALL, ""); printf("locale: %s\n", locale); const int MAX_LINE_SIZE = 1024; cha

我正在尝试从.csv文件中读取UTF-8字符串,然后将其写入控制台

a.csv内容:

Gijón
在对这一主题进行了一整天的研究之后,我发现执行这种操作的合理方式应该类似于以下内容:

int main(int argc, char *argv[])
{
    char *locale = setlocale(LC_ALL, "");
    printf("locale: %s\n", locale);

    const int MAX_LINE_SIZE = 1024;
    char line[MAX_LINE_SIZE];
    wchar_t wline[MAX_LINE_SIZE];

    // Attempt 0: no special handling
    FILE* stream = fopen("a.csv", "r");
    fgets(line, MAX_LINE_SIZE, stream);
    printf("%s\n", line); // Expected to print "Gijón", prints "Gijón"
    fclose(stream);

    // Attempt 1: mbstowcs
    mbstowcs(wline, line, MAX_LINE_SIZE);
    wprintf(L"%ls\n", wline); // Expected to print "Gijón", prints "Gijón"

    // Attempt 2: fgetws
    stream = fopen("a.csv", "r");
    fgetws(wline, MAX_LINE_SIZE, stream);
    wprintf(L"%ls\n", wline); // Expected to print "Gijón", prints "Gijón"
    fclose(stream);

    // Attempt 3: _wfopen
    stream = _wfopen(L"a.csv", L"rb");
    fgetws(wline, MAX_LINE_SIZE, stream);
    wprintf(L"%ls\n", wline); // Expected to print "Gijón", prints ""
    fclose(stream);

    // Printing command line parameter
    mbstowcs(wline, argv[1], MAX_LINE_SIZE);
    wprintf(L"%ls\n", wline); // Properly prints "Gijón"
}
但运行此程序会导致:

.\myprogram.exe Gijón
locale: Spanish_Spain.1252
Gijón
Gijón

Gijón
我认为控制台本身没有问题,因为
argv[1]
转换工作正常


我遗漏了什么?

我认为您必须将宽字符转换为1252编码。1252编码是8位/字符编码,只支持一小部分unicode字符。
可能有转换函数/库可用。但是自己编写似乎并不太复杂(big switch/case子句)。

wchar\u t
和宽字符函数(
wfopen
等)主要在Windows中用于处理UTF16编码中的Unicode

UTF8使用
char
和相同的ASCII兼容C函数(
fopen
等)来读取UTF8,您可以对ASCII使用相同的C函数

Windows不完全支持读取和显示UTF8,因此必须在UTF8和UTF16之间进行转换才能正确显示文本。Windows 10不支持控制台窗口的UTF8,请参阅相关主题

#include <stdio.h>
#include <windows.h>

int main(void)
{
    const char* filename = "a.csv";
    FILE* fp = fopen(filename, "r");
    char buf[1000];
    fgets(buf, sizeof(buf), fp);

    if(strlen(buf) > 2)
        if(strncmp(buf, "\xFF\xFE", 2) == 0)
        {
            printf("UTF16-LE\n");
            fclose(fp);
            fp = fopen(filename, "rb");
            wchar_t wbuf[1000] = { 0 };
            fgets((char*)wbuf, sizeof(buf), fp);
            MessageBoxW(0, wbuf, L"UTF16-LE", 0);
            return 0;
        }

    if(strlen(buf) > 3)
        if(strncmp(buf, "\xEF\xBB\xBF", 3) == 0)
            printf("UTF8 with BOM\n");

    //assume UTF8 and convert to UTF16:
    int size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
    wchar_t *utf16 = malloc((size + 1) * sizeof(wchar_t));
    MultiByteToWideChar(CP_UTF8, 0, buf, -1, utf16, size);

    MessageBoxA(0, buf, "ANSI", 0);
    MessageBoxW(0, utf16, L"UTF8 converted", 0);
    return 0;
}

这是用于Windows还是Linux/Mac?我正在使用Windows什么是
\wfopen
?这不是标准C的一部分,是吗?它似乎是特定于微软的。我在这里找到了它:它似乎对我来说无论如何都不起作用,只是一次绝望的尝试,想让它在Mac上工作。在Mac上,我可以运行你的整个程序,除了特定于Windows的
\wfopen
,我每次都看到它打印
Gijón
。我希望这里有一位Windows专家。我知道默认情况下Mac终端程序是UTF-8,所以我怀疑,但不能确定,Windows中有一些控制台设置需要检查。抱歉,目前无法访问Windows。根据我使用的文本编辑器(notepad++),文件编码为UTF8。此代码使弹出窗口“UTF8转换”正确显示“Gijón”(+1)。我在记事本中发现了一个选项,可以将文件中的编码转换为ANSI(它还有一个字符表示ó),并使我的问题代码正确地显示“Gijón”。但我的目的是能够处理UTF8编码的文件.Q。MultiByteToWideChar是特定于windows平台的。多平台解决方案会是什么样子?linux中是否需要任何特殊处理。strncmp如何识别文件的编码?(如果这可能是一个不同的SO问题,最好在附近的某个地方有一个指向它的链接)。当您运行该程序时,它是否打印了
“带BOM的UTF8”
?UTF8文件有时有BOM表,有时没有。如果有BOM表,则跳过前3个字节。对于Linux/Mac,只需使用普通的C函数。Linux可以很好地读取/显示UTF8。对于Windows,这是一样的,只是当您想向用户显示它时,必须将其转换为UTF16。由于Windows 8,控制台允许通过
writefile
writeconolea
写入UTF-8。但即使是Windows 10(版本1803)也不支持通过
ReadFile
ReadConsoleA
从控制台读取UTF-8。它被限制为7位ASCII码。它无法使用非ASCII UTF-8处理每个代码2-4字节的可变大小编码,因此它将非ASCII字符替换为ASCII NUL(即
'\0'
)。也许这在1809版本中已修复。在Windows 10之前更糟糕的是,在这种情况下,尝试将非ASCII字符作为UTF-8读取会导致空读取,这通常被视为文件的结尾。
void printf_utf8(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    int len = _vscprintf(format, args) + 1; 
    char *buf = malloc(len);
    vsprintf(buf, format, args);

    //convert to UTF16 and print
    int wbuf_size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
    wchar_t *wbuf = malloc((wbuf_size + 1) * sizeof(wchar_t));
    MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, wbuf_size);

    DWORD temp;
    HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteConsoleW(h, wbuf, wcslen(wbuf), &temp, 0);

    free(wbuf);
    free(buf);
}

int main(void)
{
    FILE* fp = fopen("a.csv", "r");
    if(!fp)
        return 0;
    char buf[1000];
    fgets(buf, sizeof(buf), fp);
    printf_utf8("Test %s %d\n", buf, 123);
    return 0;
}