C ++子串多字节字符

时间:2012-06-01 08:34:38

标签: c++ character-encoding wstring

我有这个std :: string,其中包含一些跨越多个字节的字符。

当我对此字符串执行子字符串时,输出无效,因为当然,这些字符计为2个字符。在我看来,我应该使用wstring,因为它将这些字符存储为一个元素而不是更多。

所以我决定将字符串复制到wstring中,但是当然这没有意义,因为字符仍然分为2个字符。这只会使情况变得更糟。

将字符串转换为wstring是否有一个很好的解决方案,将特殊字符合并为1个元素而不是2个。

由于

6 个答案:

答案 0 :(得分:6)

更简单的版本。 基于Marcelo Cantos提供的解决方案Getting the actual length of a UTF-8 encoded std::string?

std::string substr(std::string originalString, int maxLength)
{
    std::string resultString = originalString;

    int len = 0;
    int byteCount = 0;

    const char* aStr = originalString.c_str();

    while(*aStr)
    {
        if( (*aStr & 0xc0) != 0x80 )
            len += 1;

        if(len>maxLength)
        {
            resultString = resultString.substr(0, byteCount);
            break;
        }
        byteCount++;
        aStr++;
    }

    return resultString;
}

答案 1 :(得分:5)

std::string对象不是字符串,而是一串字节。它根本没有所谓的“encoding”的概念。 std::wstring也是如此,除了它是一个16位值的字符串。

为了对需要处理不同字符的文本执行操作(例如,当您想要获取子字符串时),您需要知道std :: string对象使用的编码。

更新:既然您已澄清输入字符串是UTF-8编码,您仍需要决定用于输出std::wstring的编码。我想到了UTF-16,但它实际上取决于您将传递std::wstring个对象所期望的API。假设UTF-16可以接受,您有多种选择:

  1. 在Windows上,您可以使用MultiByteToWideChar功能;不需要额外的依赖。
  2. UTF8-CPP库声称为处理UTF- *编码字符串提供了轻量级解决方案。从来没有尝试过,但我一直听到很好的事情。
  3. 在Linux系统上,使用libiconv库非常常见。
  4. 如果你需要处理各种疯狂的编码,并希望在编码时使用完整的alpha-and-omega字,请查看ICU

答案 2 :(得分:1)

实际上只有两种可能的解决方案。如果你这样做了 很多,在很远的距离,你最好转换你的 使用wchar_t(或int32_t进行单个元素编码的字符, 或者什么是最合适的。这是不是一个简单的副本 会将每个char转换为目标类型,但为true 转换函数,它将识别多字节字符,以及 将它们转换为单个元素。

对于偶尔使用或较短的序列,可以自己编写 用于推进n个字节的函数。对于UTF-8,我使用以下内容:

inline size_t
size(
    Byte                ch )
{
    return byteCountTable[ ch ] ;
}

template< typename InputIterator >
InputIterator
succ(
    InputIterator       begin,
    size_t              size,
    std::random_access_iterator_tag )
{
    return begin + size ;
}

template< typename InputIterator >
InputIterator
succ(
    InputIterator       begin,
    size_t              size,
    std::input_iterator_tag )
{
    while ( size != 0 ) {
        ++ begin ;
        -- size ;
    }
    return begin ;
}

template< typename InputIterator >
InputIterator
succ(
    InputIterator       begin,
    InputIterator       end )
{
    if ( begin != end ) {
        begin = succ( begin, end, size( *begin ),
                      std::::iterator_traits< InputIterator >::iterator_category() ) ;
    }
    return begin ;
}

template< typename InputIterator >
size_t
characterCount(
    InputIterator       begin,
    InputIterator       end )
{
    size_t              result = 0 ;
    while ( begin != end ) {
        ++ result ;
        begin = succ( begin, end ) ;
    }
    return result ;
}

答案 3 :(得分:1)

Unicode很难。

  1. std::wstring不是代码点列表,它是wchar_t的列表,它们的宽度是实现定义的(通常是VC ++的16位和gcc和clang的32位)。是的,这意味着它对便携式代码毫无用处......
  2. 单个字符可能会在多个代码点上编码(因为diacritics
  3. 在某种语言中,两个不同的字符组合在一起形成一个不可分离的“单位”(例如,LL被认为是西班牙语中的一个字母)。
  4. 所以...这有点难。

    解决3)可能代价高昂(需要特定的语言/用法注释);解决1)和2)绝对必要......并且需要使用Unicode感知库或编写自己的库(并且可能会出错)。

    • 1)简单地解决了:编写从UTF-8转换到CodePoint的例程是微不足道的(CodePoint可以用uint32_t来表示)
    • 2)更难,它需要一个变音符号列表,子程序必须知道在变音符号之前永远不要削减(他们遵循他们符合条件的字符)

    否则,您可能会在ICU中寻找。祝你好好找到它。

答案 4 :(得分:0)

让我假设您的编码是UTF-8。在这种情况下,我们会有一些字符占用多个字节,就像你的情况一样。 然后你有std :: string,其中存储了那些UTF-8编码的字符。 现在你想用chars而不是字节来表示substr()。 我会编写一个将字符长度转换为字节长度的函数。对于utf 8案例,它看起来像:

#define UTF8_CHAR_LEN( byte ) (( 0xE5000000 >> (( byte >> 3 ) & 0x1e )) & 3 ) + 1

int32 GetByteCountForCharCount(const char* utf8Str, int charCnt)
{
    int ByteCount = 0;
    for (int i = 0; i < charCnt; i++)
    {
        int charlen = UTF8_CHAR_LEN(*utf8Str);
        ByteCount += charlen;
        utf8Str += charlen;
    }
    return ByteCount;
}

所以,假设你想从第7个字符串中删除字符串()。没问题:

int32 pos = GetByteCountForCharCount(str.c_str(), 7);
str.substr(pos); 

答案 5 :(得分:0)

基于this我写了我的utf8子串函数:

void utf8substr(std::string originalString, int SubStrLength, std::string& csSubstring)
{
    int len = 0, byteIndex = 0;
    const char* aStr = originalString.c_str();
    size_t origSize = originalString.size();

    for (byteIndex=0; byteIndex < origSize; byteIndex++)
    {
        if((aStr[byteIndex] & 0xc0) != 0x80)
            len += 1;

        if(len >= SubStrLength)
            break;
    }

    csSubstring = originalString.substr(0, byteIndex);
}