我尝试了解如何在C ++中处理基本的UTF-8操作。
假设我们有这样的场景:用户输入一个名称,它被限制为10个字母(用户语言中的符号,而不是字节),它被存储。
可以用ASCII方式完成。
// ASCII
char * input; // user's input
char buf[11] // 10 letters + zero
snprintf(buf,11,"%s",input); buf[10]=0;
int len= strlen(buf); // return 10 (correct)
现在,如何在UTF-8中完成?我们假设它最多有4个字节的字符集(就像中文一样)。
// UTF-8
char * input; // user's input
char buf[41] // 10 letters * 4 bytes + zero
snprintf(buf,41,"%s",input); //?? makes no sense, it limits by number of bytes not letters
int len= strlen(buf); // return number of bytes not letters (incorrect)
可以使用标准的sprintf / strlen吗?是否有任何替换这些函数与UTF-8一起使用(在PHP中有这样的函数IIRC的mb_前缀)?如果没有,我是否需要自己写这些?或者我可能需要以另一种方式接近它吗?
注意:我宁愿避免宽字符解决方案...
编辑:我们只限制为基本多语言平面。
答案 0 :(得分:1)
strlen
仅计算输入字符串中的字节,直到终止NUL
。
另一方面,您似乎对字形计数感兴趣(您称之为"用户语言中的符号" )。
UTF-8是可变长度编码(因为在较小程度上,也是UTF-16),这个过程很复杂,所以代码点可以用一个编码来编码四个字节。还有Unicode combining characters要考虑。
据我所知,在标准C ++库中没有类似的东西。但是,使用第三方库(如ICU)可能会更好。
答案 1 :(得分:1)
我宁愿避免宽字符解决方案...
宽字符是不够的,因为如果单个字形需要4个字节,那么该字形可能在this page之外,并且它不会由单个16位{{1字符(假设wchar_t是16位宽,这只是常见的大小)。
您必须使用真正的unicode库将输入转换为其正常形式C(规范组合)中的unicode字符的列表或兼容性等效(NFKC)(*),具体取决于例如,您是否要为连字wchar_t
(U + FB00)计算一个或两个字符。 AFAIK,你最好的选择应该是Basic Multilingual Plane。
(*)Unicode允许对同一个字形进行多次表示,特别是正常的组合形式(NFC)和正常的分解形式(NFD)。例如,法语ff
字符可以在NFC中表示为U + 00E9或LATIN SMALL LETTER E WITH ACUTE或U + 0065 U + 0301或LATIN SMALL LETTER E,然后是COMBINING ACUTE ACCENT(也显示为{{ 1}})。
答案 2 :(得分:0)
std::strlen
确实只考虑一个字节字符。要计算unicode NUL终止字符串的长度,可以使用std::wcslen
代替。
示例:
#include <iostream>
#include <cwchar>
#include <clocale>
int main()
{
const wchar_t* str = L"爆ぜろリアル!弾けろシナプス!パニッシュメントディス、ワールド!";
std::setlocale(LC_ALL, "en_US.utf8");
std::wcout.imbue(std::locale("en_US.utf8"));
std::wcout << "The length of \"" << str << "\" is " << std::wcslen(str) << '\n';
}
答案 3 :(得分:0)
如果您不想自己计算utf-8字符 - 您可以使用临时转换为widechar来剪切输入字符串。您不需要存储中间值
#include <iostream>
#include <codecvt>
#include <string>
#include <locale>
std::string cutString(const std::string& in, size_t len)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> cvt;
auto wstring = cvt.from_bytes(in);
if(len < wstring.length())
{
wstring = wstring.substr(0,len);
return cvt.to_bytes(wstring);
}
return in;
}
int main(){
std::string test = "你好世界這是演示樣本";
std::string res = cutString(test,5);
std::cout << test << '\n' << res << '\n';
return 0;
}
/****************
Output
$ ./test
你好世界這是演示樣本
你好世界這
*/