我有一个std::string,其中包含跨越多个字节的一些字符。
当我在此字符串上执行子串操作时,输出无效,因为这些字符被计算为2个字符。我的观点是应该使用wstring,因为它将把这些字符存储为一个元素而不是多个元素。
所以我决定将字符串复制到wstring中,但这显然没有意义,因为字符仍然被分成两个字符。这只会使情况变得更糟。
是否有好的解决方案可以将字符串转换为wstring并将特殊字符合并为1个元素而不是2个?
谢谢
我有一个std::string,其中包含跨越多个字节的一些字符。
当我在此字符串上执行子串操作时,输出无效,因为这些字符被计算为2个字符。我的观点是应该使用wstring,因为它将把这些字符存储为一个元素而不是多个元素。
所以我决定将字符串复制到wstring中,但这显然没有意义,因为字符仍然被分成两个字符。这只会使情况变得更糟。
是否有好的解决方案可以将字符串转换为wstring并将特殊字符合并为1个元素而不是2个?
谢谢
简化版本。基于Marcelo Cantos提供的获取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;
}
std::string
对象不是字符的字符串,而是字节的字符串。它完全没有所谓“编码”的概念。对于std::wstring
也是如此,只不过它是16位值的字符串。
为了执行需要处理不同字符的文本操作(例如当您想要取子串时),您需要知道用于您的std::string对象的编码。
更新:现在您明确了您的输入字符串是UTF-8编码,您仍然需要决定用于输出std::wstring
的编码。UTF-16是个不错的选择,但这实际上取决于您将传递std::wstring
对象给哪个API。假设UTF-16是可接受的,您有各种选择:
MultiByteToWideChar
函数;不需要额外的依赖。std::wstring
是一个由 wchar_t
组成的字符串,它可能是 16 位或 32 位。 - Some programmer dudestd::wstring
,您仍然需要决定使用哪种编码)。 - Frerich Raabe根据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);
}
Unicode很难。
std::wstring
不是代码点列表,而是wchar_t
列表,其宽度是实现定义的(VC++通常为16位,gcc和clang为32位)。是的,这意味着它对于可移植代码是无用的...LL
被认为是一个字母)。所以...有点难。
解决第3个问题可能很昂贵(需要特定的语言/用法注释);解决第1和第2个问题是绝对必要的...并且需要支持Unicode的库或编写自己的库(并且可能会出错)。
uint32_t
表示)否则,您可能会在ICU中找到所需的内容。祝您好运。
istream
,可以在不取出字符的情况下查看下一个字符:) 不过我同意后来看来,前缀更好。 - Matthieu M.实际上只有两种可能的解决方案。如果您需要在较长距离上大量执行此操作,则最好将字符转换为单个元素编码,使用 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 ;
}
wchar_t
只有 16 位,因此无法用单个元素表示许多码点。相反,char32_t
是 C++11 提供的一种固定宽度类型,足以在单个元素中表示 Unicode 的所有内容。 - Lucien Greathouse为了简单起见,我假设您的编码是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;
}
假设你想要从第7个字符开始使用substr()函数截取字符串,没有问题:
int32 pos = GetByteCountForCharCount(str.c_str(), 7);
str.substr(pos);
char const*
而不是 std::string const&
?请在 C++ 问题中使用 C++ 成语。 - Matthieu M.
std::wstring
不幸地 取决于实现(在Windows上为16位宽字符,在Linux上为32位宽字符),因此它是不够的。 - Matthieu M.