getline() 在读取 UTF-8 表情符号字符时到达文件末尾

getline() reaches end of file when reading UTF-8 emoji character

提问人:James P 提问时间:10/13/2023 最后编辑:James P 更新时间:10/14/2023 访问量:93

问:

我正在编写一个处理大型分隔文件的 C++ 程序。

我有一个UTF-8 csv文件,其中包含一行带有(emoji?)字符🌟。它看起来像这样:

123,"james","piotrj🌟","1996-01-28"

当我调用这一行时,它会读取表情符号,然后停止。因此,生成的字符串是 。我不确定为什么会这样。如果我不得不猜测,我不正确地使用了语言环境,并且这个表情符号(或其中的一部分)被读取为 .getline()getline()123,"james","piotrjEOF

我想按原样读取这一行,执行一些字符串操作,然后将其写出到另一个文件中。

我这里有一些示例代码:

locale loc("en_US.UTF8");
wifstream inFile;
inFile.imbue(loc);
inFile.open("MyFile.csv");
if(inFile.is_open()){
  wstring str;
  if (getline(inFile, str)) {
    wcout << str << endl;
  }
  if (getline(inFile, str)) {
    wcout << str << endl;
  }
  inFile.close();
}

此代码的输出为:。第二个 if 语句正文不执行,因为第二个语句没有抓取任何内容。123,"james","piotrjgetline()

为了尝试一些事情,我将语言环境更改为:

locale loc = locale();

语言环境的名称是“C”,这将得到整行。该程序的输出为:。这是朝着正确方向迈出的一步,但如果没有正确的语言环境,wstring 将无法正确存储它。在我的程序中,我做了一些单独的字符检查,看看字符串是否可以用ANSI表示,因此我真的希望wstring将该表情符号作为一个字符。123,"james","piotrj≡ƒîƒ","1996-01-28"

C++ 语言环境 emoji getline wstring

评论

0赞 273K 10/13/2023
你能把文件内容显示为十六进制值的char数组吗?

答:

2赞 n. m. could be an AI 10/13/2023 #1

看起来您正在使用 libc++。此实现中的宽流根本不支持 UTF-8

如果你改用 libstdc++,你的程序可以工作,除了你会在输出中得到音译文本。我得到

123,"james","piotrj?","1996-01-28"

那是因为语言环境没有注入 .要获取普通文本,您需要执行以下任一操作wcout

ios_base::sync_with_stdio(false);
wcout.imbue(loc);

(如果区域设置与 STDIO 同步,则无法将其注入标准流中)

或者,

locale::global(loc);

然后你的程序将完全工作。

如果您绑定到 libc++,您唯一的选择是使用窄字符流。

编辑:使用MSVC,此代码也不起作用。不知道为什么Microsoft声称在较新版本的Windows中支持UTF-8,显然它根本不存在。在 Windows 上,可以安装 gcc(几种风格之一,我推荐 MSYS2 提供的 UCRT 风格)。我不能保证它会起作用,因为最终控制流会通过 Microsoft 运行时库。正确的解决方案是永远不要使用任何 API,除了调用需要 .使用窄字符,从文件中读取 UTF-8,以 UTF-8 格式存储和操作字符串,以 UTF-8 格式输出。我已经测试了使用 MSVC 转换为窄字符的代码,它对我来说按预期工作。wchar_twchar_t

评论

0赞 James P 10/14/2023
谢谢!我最初是使用 Visual Studio 在 Windows 上开发这个。我切换到 WSL 并使用 libstdc++ 并且它起作用了!不过,我真的希望我的程序是一个 Windows 可执行文件。有没有一种方法可以在 Visual Studio 上使用 libstdc++?
0赞 n. m. could be an AI 10/14/2023
@JamesP 哦,我以为你在 Mac 上。 MSVC 有自己的怪癖。我稍后会修改答案。