如何将std::string写入UTF-8文本文件

57

我只想使用C++将几行简单的文本写入文本文件,但我希望它们以UTF-8编码。最简单和简便的方法是什么?


15
标准库无法处理 UTF-8 是荒谬的。这就是为什么我们不得不处理大量的宽字符串和字节字符串之间的转换,还要使用一些尴尬的区域设置。为什么这么多年过去了,仍然没有类似于 std::utf8string 的东西呢? - V-X
6
因为 C/C++ 必须与尚不存在的硬件兼容? :P - CoffeDeveloper
9个回答

57

唯一影响 std::string 的 UTF-8 方式是其 size()length() 和所有索引都是按字节而非字符计量的。

另外,正如 sbi 指出的那样,递增 std::string 提供的迭代器将按字节而非字符向前移动,因此它实际上可以指向多字节的 UTF-8 代码点中间。标准库中没有提供 UTF-8 意识的迭代器,但在互联网上有一些可用的迭代器。

如果您记得这一点,就可以以通常的方式(也就是不使用内部UTF-8的std::string的方式)将UTF-8放入std::string,将其写入文件等。

您可能希望在文件开头加上字节顺序标记,以便其他程序知道它是UTF-8编码。


2
为了完整性,将迭代器添加到您的第一句话中,它们与索引一样。 - sbi
14
很多程序在读取UTF-8时会因为BOM而出现故障,这会导致一些程序认为文本是UTF-16。 - Tim Seguine
2
是的,但这是一种常见的、非常具体的方式,如果遇到使用问题,了解它是值得的。 - Tim Seguine
6
BOM代码告诉你UTF16或UTF32流所使用的两种可能的字节顺序之一。对于UTF8流来说,它们甚至没有意义。 - seattlecpp
3
来自Unicode.org 的确切引用是:问: 无论底层处理器是小端还是大端,UTF-8编码方案是否相同? 答: 是的。由于UTF-8被解释为字节序列,因此与使用16位或32位代码单元的编码形式存在的字节顺序问题不同,不存在字节顺序问题。当BOM与UTF-8一起使用时,它仅用作编码签名,以将UTF-8与其他编码区分开来 - 它与字节顺序无关。我理解为 "表明这是UTF8编码"! - SlySven
显示剩余8条评论

24

有一个很好的小型库可以使用C++处理UTF8: utfcpp


2
哇,这是最酷的库了。只要你知道什么是UTF8,就不需要其他任何东西。 - CoffeDeveloper

10

libiconv 是一个非常出色的库,适合我们所有编码和解码需求。

如果你正在使用Windows系统,可以使用WideCharToMultiByte函数,并指定要使用UTF8编码。


10
什么是最简单和简单的方法呢?
在C++中,处理utf8最直观且最容易的方法当然是使用std::string的替代品。由于互联网上仍缺乏这样一个库,因此我自己实现了该功能: tinyutf8 (编辑:现在在Github上)。
该库提供了一个非常轻量级的std::string(或者如果您愿意,可以是std::u32string),因为它遍历代码点而不是字符。它成功地在快速访问和小内存消耗之间实现了平衡,并且非常健壮。对于“无效”的UTF8序列的这种健壮性使其(几乎完全)与ANSI(0-255)兼容。
希望这有所帮助!

你的库看起来很不错,但它的许可证非常限制。 - Cem Kalyoncu
1
它有哪些限制?您希望我以什么许可证进行发布? - Jakob Riedle
3
GPL的意思是,如果我在我的程序中包含了你的头文件,那么我也必须将我的程序开放为GPL。这相当具有限制性,不是吗?对于像这样的小型库,我建议采用BSD风格的许可证。 - Cem Kalyoncu
好的,我一旦找到时间就会把它改为BSD-3。现在,我特此授予您使用 tinyutf8 的权利,遵循 BSD-3,又称“New BSD License” :D 谢谢您的反馈,我非常感激! - Jakob Riedle
1
个人而言,我会保持GPL并为那些想从你的工作中赚钱的人提供额外的商业(收费)许可证。 - Adrian Maire

7
如果您所说的“简单”是指ASCII编码,那么无需进行任何编码,因为ASCII值为127或更低的字符在UTF-8中是相同的。

1
我猜他可能有一些其他字符需要编码,而这些字符存储在他的字符串中。但也有可能不是 :) - Brian R. Bondy

5
std::wstring text = L"Привет";
QString qstr = QString::fromStdWString(text);
QByteArray byteArray(qstr.toUtf8());    
std::string str_std( byteArray.constData(), byteArray.length());

0

使用Glib :: ustring来自glibmm

这是唯一广泛使用的UTF-8字符串容器(AFAIK)。虽然基于字符(不是字节),但它具有与std::string相同的方法签名,因此端口应该是简单的搜索和替换(只需确保在将数据加载到ustring之前,您的数据是有效的UTF-8)。


为什么会被踩?由于被用于 glibmmgtkmm 和所有依赖项目(包括 InkScape)中,这是一个广泛使用且经过充分测试的 UTF8 字符串类。为什么不值得一提呢? - underscore_d

0

我的偏好是将字符串转换为std::u32string并在内部处理代码点,然后使用我在github上发布的这些转换迭代器将其转换为utf8格式并写入文件。

#include <utf/utf.h>

int main()
{
    using namespace utf;

    u32string u32_text = U"ɦΈ˪˪ʘ";
    // do stuff with string
    // convert to utf8 string
    utf32_to_utf8_iterator<u32string::iterator> pos(u32_text.begin());
    utf32_to_utf8_iterator<u32string::iterator> end(u32_text.end());

    u8string u8_text(pos, end);

    // write out utf8 to file.
    // ...
}

-28

UTF-8 是一种多字节字符字符串,因此在使用时会遇到一些问题,这是一个不好的想法。相反,应使用普通的 Unicode。

所以我的意见是最好使用普通的 ASCII 字符文本与一些编码集。如果您在单个文本中使用了两个以上不同符号(语言)的集合,则需要使用 Unicode。

这是相当罕见的情况。在大多数情况下,2 个符号集就足够了。对于这种常见情况,请使用 ASCII 字符而不是 Unicode。

使用 UTF-8 等多字节字符的影响仅限于中国传统、阿拉伯或某些象形文字。这是非常非常罕见的情况!!!

我认为没有很多人需要这样做。因此,永远不要使用 UTF-8!!!这将避免操纵此类字符串带来的强烈头痛。


5
“normal Unicode”是什么意思?我会认为你指的是大多数Java和Windows程序员所理解的Unicode,即UTF16。这也不是一种固定宽度编码(并非每个字符都占用2个字节)。约有一半的互联网用户来自中国。非常罕见! - Tim Seguine
2
@Anatoly - 一些背景阅读:http://www.joelonsoftware.com/articles/Unicode.html,http://www.theregister.co.uk/2013/10/04/verity_stob_unicode/,http://www.utf8everywhere.org/。如果只读一篇,请读第一篇。你可能会改变你不使用UTF-8的建议! - Matt Wallis
2
使用utf-8的原因是它可以编码所有Unicode代码点,并且对于拉丁语言来说具有内存效率。缺点确实是你有可变长度的编码。请注意,utf-16和ucs-2之间存在区别。 ucs-2是您提到的那个:每个字符固定2个字节,但缺点是它无法编码所有代码点。 - gast128

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接