UTF-8,sprintf,strlen等

时间:2017-09-20 09:06:19

标签: c++ utf-8

我尝试了解如何在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_前缀)?如果没有,我是否需要自己写这些?或者我可能需要以另一种方式接近它吗?

注意:我宁愿避免宽字符解决方案...

编辑:我们只限制为基本多语言平面。

4 个答案:

答案 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)。例如,法语字符可以在NFC中表示为U + 00E9或LATIN SMALL LETTER E WITH ACUTE或U + 0065 U + 0301或LATIN SMALL LETTER E,然后是COMBINING ACUTE ACCENT(也显示为{{ 1}})。

ICU

上的参考文献和其他示例

答案 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
你好世界這是演示樣本
你好世界這
*/