作为介绍,我做Java并且过去做了很多C。
在Java中,String
文字可以包含任何一组字素,只要您可以在编辑环境中输入它们;然后,编辑环境会将源文件保存在当时使用的任何字符编码中。
在运行时,只要编译器支持编码,字节代码将所有String
文字表示为一组char
s,其中char
代表一个UTF-16代码单元。 (因此,BMP外部的Unicode代码点需要两个char
;您可以使用Character.toChars()
获取表示BMP外部的Unicode代码点所需的char
数组。
您有一个字符编码类(Charset
),即将char
s序列编码为字节序列(CharsetEncoder
)的过程,以及反向({{ 3}})。因此,无论您的源/目标使用哪种字符编码,无论是文件,套接字还是其他,您都可以根据需要进行编码/解码。
现在,让我们假设C ++ 11。它引入了std::u32string
,std::u16string
;据我所知,这些是&#34;别名&#34;到std::basic_string<char32_t>
和std::basic_string<char16_t>
,它们的净效果是在运行时,你声明的字符串常量(使用{{ 1}}和u""
)由16位或32位实体组成,分别代表UTF-16或UTF-32代码单元。还有U""
(后者的u8""
类型是什么,因为它没有固定的长度?)。
其他重点:UTF-16有两种变体,LE和BE; java确实是因为在字节码级别,所有东西都是BE。 basic_string
取决于代码中的字节顺序吗?
但即使经过几个小时的搜索,我也找不到答案:标准的C ++ 11可以做标准的JDK,即将任何字符串常量转换为合适的字节序列,反之,给定一个字符编码?我怀疑这会变得更加困难,因为在运行时基本上有三个字符串文字的表示,甚至没有char{16,32}_t
这基本上是一个字节数组...
(编辑:添加了相关javadoc的链接)
答案 0 :(得分:2)
您可以使用codecvt locale facet进行转换。
用法有点不直观,但这就是我所做的:
/** Convert utf8 stream to UCS-4 stream */
u32string decode(string utf8)
{
std::wstring_convert<std::codecvt_utf8<char32_t>,char32_t> convert;
return convert.from_bytes(utf8);
}
/** Convert UCS-4 stream to utf8 stream */
string encode(u32string ucs4)
{
std::wstring_convert<std::codecvt_utf8<char32_t>,char32_t> convert;
return convert.to_bytes(ucs4);
}
它需要一个不错的编译器,对我来说只有clang工作正常,gcc编译但生成了无效的结果(更新版本的gcc可能没问题。)
答案 1 :(得分:2)
C ++没有指定源文件编码。实际上,它支持EBCDIC。所有C ++ 11编译器都支持UTF-8,许多编译器通过传递适当的标志来支持其他编码。
该标准为基本源字符集之外的字符指定了转义码语法,该语法基本上包含该语言使用的字符。基本源字符集之外的字符称为“扩展字符”,在编译源代码之前,它们将被相应的代码替换,甚至是预处理的。这可以确保源代码的含义与其编码无关。
char32_t
和char16_t
没有内置的字节顺序。它们只相当于uint32_t
和uint16_t
。您可以说它们继承了本机字节序,但直接将对象表示序列化为字节是滥用。
要可靠地指定UTF-8文字,并覆盖任何相反的编译器设置,请使用准备进行序列化的u8""
。 u""
和U""
没有字节序,因为这些值已经烘焙到程序中。
要序列化,您可以使用codecvt_utf8
和codecvt_utf16
类模板,这些模板采用指定文件格式的编译时模板标志:
enum codecvt_mode {
consume_header = 4,
generate_header = 2,
little_endian = 1
};
要设置流file
(在二进制模式下)将char32_t
字符串编码为带字节顺序标记的UTF-16LE,您可以使用
std::basic_ofstream< char32_t > file( path, std::ios::binary );
file.imbue( std::locale( file.locale(), new std::codecvt_utf16<
char32_t,
std::codecvt_mode::generate_header | std::codecvt_mode::little_endian
>{} ) );
这比在输出之前进行翻译更可取。
答案 2 :(得分:1)
#include <string>
#include <codecvt>
#include <locale>
template<typename Facet>
struct usable_facet : Facet {
using Facet::Facet;
~usable_facet() = default;
};
int main() {
using utf16_codecvt = usable_facet<std::codecvt<char16_t, char, std::mbstate_t>>;
using utf32_codecvt = usable_facet<std::codecvt<char32_t, char, std::mbstate_t>>;
std::wstring_convert<utf16_codecvt, char16_t> u16convert; // bidirectional UTF-16/UTF-8 conversion
std::wstring_convert<utf32_codecvt, char32_t> u32convert; // bidirectional UTF-32/UTF-8
std::string utf8 = u16convert.to_bytes(u"UTF-16 data");
std::u16string utf16 = u16convert.from_bytes(u8"UTF-8 data");
utf8 = u32convert.to_bytes(U"UTF-32 data");
std::u32string utf32 = u32convert.from_bytes(u8"UTF-8 data");
}
你也可以使用其他方面,但要小心,因为它们并不都是听起来像他们应该做的那样。如果您使用codecvt_utf8
,则char16_t
不会转换为UTF-16,codecvt_utf16
会使用UTF-16作为 narrow 编码等。这些名称有意义它们的预期用途,但它们与wstring_convert
混淆。
您也可以wstring_convert
使用codecvt_byname
支持的区域设置使用的任何编码(但是,您只能在该区域设置的char
编码和自己的wchar_t
编码之间进行转换不在语言环境窄编码和固定Unicode编码之间。语言环境指定自己的wchar_t
编码,它不一定是Unicode编码,也不一定与其他语言环境使用的wchar_t
编码相同。)
using locale_codecvt = usable_facet<std::codecvt_byname<wchar_t, char, std::mbstate_t>>;
std::wstring_convert<locale_codecvt, wchar_t> legacy_russian(new locale_codecvt("ru_RU")); // non-portable locale name
std::string legacy_russian_data = /* ... some source of legacy encoded data */
std::wstring w = legacy_russian.from_bytes(legacy_russian_data);
在任意区域设置编码文本和任何Unicode编码之间进行转换的唯一标准方法是支持度较低的<cuchar>
header,其功能较低,如c16rtomb
和c32rtomb
。