4

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

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

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

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

谢谢

4

6 回答 6

7

更简单的版本。基于提供的解决方案获取 UTF-8 编码的 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;
}
于 2012-08-14T06:37:30.390 回答
5

对象不是一串std::string字符,而是一串字节。它根本没有所谓的“编码”的概念。也一样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 和 omega 词,请查看ICU
于 2012-06-01T08:38:31.660 回答
1

Unicode 很难。

  1. std::wstring不是代码点列表,它是 的列表wchar_t,并且它们的宽度是实现定义的(通常 16 位与 VC++ 和 32 位与 gcc 和 clang)。是的,这意味着它对可移植代码毫无用处......
  2. 单个字符可以在多个代码点上编码(因为变音符号
  3. 在某些语言中,两个不同的字符一起形成一个不可分离的“单元”(例如,LL在西班牙语中被视为一个字母)。

所以……有点难。

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

  • 1) 很容易解决:编写从 UTF-8 转换为 CodePoint 的例程很简单(CodePoint 可以用 表示uint32_t
  • 2)更难,它需要一个变音符号列表,并且子例程必须知道永远不要在变音符号之前剪切(它们遵循他们限定的字符)

否则,可能有你在ICU中寻找的东西。我祝你好运找到它。

于 2012-06-01T11:38:24.647 回答
1

实际上只有两种可能的解决方案。如果你在很远的距离上经常这样做,你最好将你的字符转换为单个元素编码,使用wchar_t(或int32_t,或任何最合适的。这不是一个简单的副本,它会转换每个人char转换为目标类型,而是一个真正的转换函数,它将识别多字节字符,并将它们转换为单个元素。

对于偶尔使用或更短的序列,可以编写自己的函数来推进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 ;
}
于 2012-06-01T09:53:06.653 回答
0

为简单起见,让我假设您的编码是 UTF-8。在这种情况下,我们会有一些字符占用一个以上的字节,就像你的情况一样。然后你就有了 std::string,这些 UTF-8 编码的字符被存储在其中。现在您想根据字符而不是字节来 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;
}

所以,假设你想 substr() 来自第 7 个字符的字符串。没问题:

int32 pos = GetByteCountForCharCount(str.c_str(), 7);
str.substr(pos); 
于 2012-06-01T09:04:33.277 回答
0

基于,我编写了我的 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);
}
于 2016-01-22T06:55:58.020 回答