C: 为 Windows 编译时无法将 Unicode 盲文写入 UTF-8 文档

C: Cannot write Unicode braille to UTF-8 doc when compiling for Windows

我有一些代码在 Linux 上工作得很好,但在 Windows 上它只有在使用模拟 Linux 环境的 Cygwin 编译时才能按预期工作。在 Windows 上,但不利于可移植性(您必须安装 Cygwin 才能使编译后的二进制文件工作。)该程序执行以下操作:

重要代码:

const char *brai[26] = {
    "⠁","⠃","⠉","⠙","⠑","⠋","⠛","⠓","⠊","⠚",
    "⠅","⠇","⠍","⠝","⠕","⠏","⠟","⠗","⠎","⠞",
    "⠥","⠧","⠭","⠽","⠵","⠺"
}

int main(void) {

    setlocale(LC_ALL, "es_MX.UTF-8");
    FILE *source = fopen(origen, "r, ccs=UTF-8");
    FILE *dest = fopen(destino, "w, ccs=UTF-8");

    unsigned int letra;
    while ((letra = fgetc(source)) != EOF) {

        // This next line is the problem, I guess:
        fwprintf(dest, L"%s", "⠷"); // Prints directly the braille sign as a char[]
        // OR prints it from an array that contains the exact same sign.
        fwprintf(dest, L"%s", brai[7]);

    }
}

代码每次都在 Linux 上按预期工作,但在 Windows 上却不行。我尝试了一切,但似乎没有什么能使输出正确。在 'dest' 文档中,我得到如下随机字符: 甥╩极肠─猀甥iꃢ¨.

到目前为止,在 Windows 上将盲文模式打印到文档的唯一方法是:

fwprintf(dest, L"⠷");

这不是很有用(需要为每个案例制作一个 'else if')。 如果您想查看完整代码,请访问 Github: https://github.com/oliver-almaraz/Texto_a_Braille

到目前为止我尝试了什么:

选项 1:使用 wchar_tfwprintf。确保将源代码保存为带 BOM 编码的 UTF-8 或使用 UTF-8 编码和 /utf-8 开关以强制假定 Microsoft 编译器采用 UTF-8 编码;否则,MSVS 假定源文件使用 ANSI 编码,您会得到 mojibake。

#include <stdio.h>

const wchar_t brai[] = L"⠁⠃⠉⠙⠑⠋⠛⠓⠊⠚⠅⠇⠍⠝⠕⠏⠟⠗⠎⠞⠥⠧⠭⠽⠵⠺";

int main(void) {
    FILE *dest = fopen("out.txt", "w, ccs=UTF-8");
    fwprintf(dest, L"%s", brai);
}

out.txt(编码为带 BOM 的 UTF-8):

⠁⠃⠉⠙⠑⠋⠛⠓⠊⠚⠅⠇⠍⠝⠕⠏⠟⠗⠎⠞⠥⠧⠭⠽⠵⠺

选项 2:使用 charfprintf,将源代码保存为 UTF-8 或带 BOM 的 UTF-8,并使用 /utf-8 Microsoft 编译开关。 char 字符串将采用源编码,因此它必须是 UTF-8 才能在输出文件中获得 UTF-8。

#include <stdio.h>

const char brai[] = "⠁⠃⠉⠙⠑⠋⠛⠓⠊⠚⠅⠇⠍⠝⠕⠏⠟⠗⠎⠞⠥⠧⠭⠽⠵⠺";

int main(void) {
    FILE *dest = fopen("out.csv","w");
    fprintf(dest, "%s", brai);
}

最新的编译器也可以使用u8""语法。这里的优点是您可以使用不同的源编码,只要您使用适当的编译器开关来指示源编码,char 字符串仍将是 UTF-8。

const char brai[] = u8"⠁⠃⠉⠙⠑⠋⠛⠓⠊⠚⠅⠇⠍⠝⠕⠏⠟⠗⠎⠞⠥⠧⠭⠽⠵⠺";

作为参考,这些是 Microsoft 编译器选项:

/source-charset:<iana-name>|.nnnn set source character set
/execution-charset:<iana-name>|.nnnn set execution character set
/utf-8 set source and execution character set to UTF-8

这是一个经过测试的(在 Windows 上使用 MSVC 和 mingw),semi-working 示例。

#include <stdio.h>
#include <ctype.h>

const char *brai[26] = {
    "⠁","⠃","⠉","⠙","⠑","⠋","⠛","⠓","⠊","⠚",
    "⠅","⠇","⠍","⠝","⠕","⠏","⠟","⠗","⠎","⠞",
    "⠥","⠧","⠭","⠽","⠵","⠺"
};

int main(void) {
    
    char* origen = "a.txt";
    char* destino = "b.txt";

    FILE *source = fopen(origen, "r");
    FILE *dest = fopen(destino, "w");
    
    int letra;
    while ((letra = fgetc(source)) != EOF) {
        
        if (isupper(letra))
            fprintf(dest, "%s", brai[letra - 'A']);
        else if (islower(letra))
            fprintf(dest, "%s", brai[letra - 'a']);
        else
            fprintf (dest, "%c", letra);
    }
}

注意这些事情。

  1. 看不到语言环境或宽字符或类似的东西。需要 None 个。
  2. 此代码仅翻译英文字母。没有标点符号或数字(我对盲文知之甚少,无法添加,但这应该很简单)。
  3. 由于代码只翻译英文字母而其他所有内容保持原样,因此可以为其提供 UTF-8 编码文件。它只会留下无法识别的字符未翻译。如果您需要翻译带重音符号的字母,则需要学习更多有关 Unicode 的知识。 Here is a good place to start.
  4. 为简洁起见省略了错误处理。
  5. 代码必须使用正确的字符集。对于 MSVC,UTF-8 with BOM 或 UTF16,或者使用不带 BOM 的 UTF-8 和 /utf-8 编译器开关(如果您的 MSVC 版本可以识别它)。对于 mingw,只需使用 UTF-8。
  6. 此方法不适用于 Windows 上的标准控制台输出。这不是什么大问题,因为 Windows 控制台默认不会输出盲文字符。但是,它适用于 msys 控制台和许多其他控制台。