如何打印混合 ASCII 字符和 Unicode 的字符串的每个字符

how to print each character of strings that mix ascii character with unicode?

本文关键字:字符 Unicode 字符串 ASCII 何打印 打印 混合      更新时间:2023-10-16

例如,我想创建一些打字机效果,因此需要打印这样的字符串:

#include <string>
int main(){
    std::string st1="ab》cd《ef";
    for(int i=0;i<st1.size();i++){
        std::string st2=st1.substr(0,i).c_str();
        printf("%sn",st2.c_str());
    }
    return 0;
}

但输出是

a
ab
ab?
ab?
ab》
ab》c
ab》cd
ab》cd?
ab》cd?
ab》cd《
ab》cd《e

而不是:

a
ab
ab》
ab》c
ab》cd
ab》cd《
ab》cd《e

如何知道即将到来的字符是 Unicode?

类似的问题,打印每个字符也有问题:

#include <string>
int main(){
    std::string st1="ab》cd《ef";
    for(int i=0;i<st1.size();i++){
        std::string st2=st1.substr(i,1).c_str();
        printf("%sn",st2.c_str());
    }
    return 0;
}

输出为:

a
b
?
?
?
c
d
?
?
?
e
f

不:

a
b
》
c
d
《
e
f

我认为问题是编码。您的字符串可能采用具有可变大小字符的UTF-8编码。这意味着您不能一次迭代一个char,因为某些字符的宽度char

事实上,在 unicode 中,您一次只能使用 UTF-32 编码可靠地迭代一个固定字符。

因此,您可以做的是使用像 ICU 这样的UTF库来转换 vetween UTF-8UTF-32 .

如果你有C++11那么这里有一些工具可以帮助你,主要是std::u32string能够容纳UTF-32编码字符串的工具:

#include <string>
#include <iostream>
#include <unicode/ucnv.h>
#include <unicode/uchar.h>
#include <unicode/utypes.h>
// convert from UTF-32 to UTF-8
std::string to_utf8(std::u32string s)
{
    UErrorCode status = U_ZERO_ERROR;
    char target[1024];
    int32_t len = ucnv_convert(
        "UTF-8", "UTF-32"
        , target, sizeof(target)
        , (const char*)s.data(), s.size() * sizeof(char32_t)
        , &status);
    return std::string(target, len);
}
// convert from UTF-8 to UTF-32
std::u32string to_utf32(const std::string& utf8)
{
    UErrorCode status = U_ZERO_ERROR;
    char32_t target[256];
    int32_t len = ucnv_convert(
        "UTF-32", "UTF-8"
        , (char*)target, sizeof(target)
        , utf8.data(), utf8.size()
        , &status);
    return std::u32string(target, (len / sizeof(char32_t)));
}
int main()
{
    // UTF-8 input (needs UTF-8 editor)
    std::string utf8 = "ab》cd《ef"; // UTF-8
    // convert to UTF-32
    std::u32string utf32 = to_utf32(utf8);
    // Now it is safe to use string indexing
    // But i is for length so starting from 1
    for(std::size_t i = 1; i < utf32.size(); ++i)
    {
        // convert back to to UTF-8 for output
        // NOTE: i + 1 to include the BOM
        std::cout << to_utf8(utf32.substr(0, i + 1)) << 'n';
    }
}

输出:

a
ab
ab》
ab》c
ab》cd
ab》cd《
ab》cd《e
ab》cd《ef

注意:

ICU 库在其转换为 Unicode 的字符串的开头添加一个BOM(字节顺序标记)。因此,您需要处理UTF-32字符串的第一个字符是BOM的事实。这就是为什么子字符串使用 i + 1 作为其长度参数以包含 BOM 的原因。

您的C++代码只是将八位字节回显到您的终端,并且您的终端显示器正在将以默认字符集编码的八位字节转换为 unicode 字符。

根据您的示例,您的终端显示器似乎使用 UTF-8。将 UTF-8 编码字符转换为 unicode 的规则相当明确(Google 是你的朋友),所以你所要做的就是检查 UTF-8 序列的第一个字符,以确定有多少个八位字节组成下一个 unicode 字符。