【问题标题】:EILSEQ with fgetws() and UTF-8带有 fgetws() 和 UTF-8 的 EILSEQ
【发布时间】:2018-05-02 06:44:53
【问题描述】:

以下 C 代码使用 fgetws()stdin 读取行并将它们写入 stdout

#include <stdio.h>
#include <locale.h>
#include <wchar.h>

#define STR_LEN 128

int main(int argc, char **argv)
{
    FILE *infile = stdin, *outfile = stdout;
    wchar_t str[STR_LEN];

    if (setlocale(LC_ALL, "en.UTF-8") == NULL) {
        fprintf(stderr, "Cannot set locale\n");
        return 1;
    }


    for (;;) {

        if (!fgetws(str, STR_LEN, infile)) {
            if (feof(infile)) {
                break;
            }
            perror("fgetws()");
            continue;
        }
        str[wcscspn(str, L"\r\n")] = L'\0';

        if (fwprintf(outfile, L"%ls\n", str) < 0) {
            perror("fwprintf()");
        }

    }

    return 0;
}

它总是与 ASCII 文件完美配合,但有时在读取 UTF-8 数据时会从 fgetws() 收到 EILSEQ 错误(非法字节序列),我不知道为什么。

在输出文件中,导致错误的行被截断,然后一些字符丢失,剩余部分在下一行。 奇怪的是,如果我只给出那一行,那么我不会得到任何错误。

例如,如果我读取一个只有几行 UTF-8 行的文件,没关系;如果我多次重复相同的行,那么我会得到EILSEQ

我几乎可以确定文件已正确编码。

我使用带有 musl-libc 的 Linux。

我的代码有什么问题?

编辑: 我收到几个EILSEQ 错误,具体取决于输入大小,但我不知道两者之间的确切关系。

使用相同的输入,我会在相同的行中得到相同的错误。

它似乎不是触发错误的特定偏移量或字符,但我可能错了。

编辑 2: 我也在 OpenBSD 上测试了代码,它可以工作。此时我怀疑这个问题与Linux或musl-libc有关。

【问题讨论】:

  • str[strcspn(str, "\r\n")] = 0; 可能是更好的方法。
  • 当然可以。谢谢。
  • 它总是在文件中的同一点失败吗?你会得到一两个 EILSEQ 错误吗?触发错误的字符的精确字节偏移量以及该字符的 utf-8 代码是多少?

标签: c utf-8 wchar


【解决方案1】:

UTF-8 使用char 来存储字符,它的工作方式与ANSI 相同。唯一的区别是语言字符可以长于一个字符。

wchar_t 和宽 c 字符串函数用于 Windows 中的 UTF-16。在 Linux 中,您可以使用 char16_t 来存储 UTF-16,但前提是您使用的是 UTF-16 文件。这显然不是这里的情况。

只需使用 char 函数来处理 UTF-8,与使用 ANSI 的方式完全相同:

char str[STR_LEN];
while(fgets(str, STR_LEN, infile))
{
    str[strcspn(str, "\r\n")];
    fprintf(outfile, "%s\n", str);
}

【讨论】:

  • 是的,即使使用char 字符串,上述程序也可以工作。但是如果我需要在阅读后操作单个字符,那么我将需要wchar_t,对吗?我不喜欢使用外部库(例如 ICU),因为我只需要做一些简单的事情。
  • 你不是在使用 Linux 吗? whcar_t 在 Windows 中使用,在 Linux 中很少使用。只需使用char。如果您只是搜索'\n'',' 等ANSI 字符,则可以解析文本,因为这些字符不会在其他地方重复。将其视为常规 ANSI 文本。但是如果你有"ελληνικά" 之类的东西,那么很难找到'η',因为'η' 是不同字符的组合,你很少需要这个选项。
  • 不幸的是,我需要对非 ASCII 字符执行操作。
  • 好的,那么您可能需要外部库。您的控制台输入是 UTF-8。您必须转换为 UTF-32。然后将其存储在char32_t(或wchar_t)中。现在每个字符占用 4 个字节。那就是如果你想在"ελληνικά" 中找到'η' 的位置。但是请注意,在"ελλ,ηνικά" 中很容易找到',' 的位置(它不会在索引3,但您只需遍历字符串即可找到它)
猜你喜欢
  • 1970-01-01
  • 2017-04-19
  • 2013-10-11
  • 1970-01-01
  • 2012-10-18
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 2014-04-02
相关资源
最近更新 更多