我的std :: string是utf-8编码所以很明显,str.length()返回错误的结果。
我发现了这些信息,但我不确定如何使用它来执行此操作:
以下字节序列是 曾经代表一个角色。该 顺序是 used取决于字符的UCS代码:
0x00000000 - 0x0000007F: 0xxxxxxx 0x00000080 - 0x000007FF: 110xxxxx 10xxxxxx 0x00000800 - 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 0x00010000 - 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
如何找到UTF-8编码的std :: string的实际长度?感谢
答案 0 :(得分:56)
计算所有第一个字节(与10xxxxxx不匹配的字节)。
int len = 0;
while (*s) len += (*s++ & 0xc0) != 0x80;
答案 1 :(得分:18)
C ++对编码一无所知,所以你不能指望使用 执行此操作的标准功能。
标准库确实确实以区域设置的形式确认字符编码的存在。如果您的系统支持语言环境,则可以非常轻松地使用标准库来计算字符串的长度。在下面的示例代码中,我假设您的系统支持语言环境en_US.UTF-8。如果我编译代码并将其作为“./a.outソニーSony”执行,则输出结果是有13个char值和7个字符。所有这些都没有提及UTF-8字符代码的内部表示或者必须使用第三方库。
#include <clocale>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
string str(argv[1]);
unsigned int strLen = str.length();
cout << "Length (char-values): " << strLen << '\n';
setlocale(LC_ALL, "en_US.UTF-8");
unsigned int u = 0;
const char *c_str = str.c_str();
unsigned int charCount = 0;
while(u < strLen)
{
u += mblen(&c_str[u], strLen - u);
charCount += 1;
}
cout << "Length (characters): " << charCount << endl;
}
答案 2 :(得分:9)
我参与的其中一个项目有一个小功能,可以做到这一点:
寻找Utf8StringSize
。它取决于同一头文件中的另一个微小功能。
答案 3 :(得分:4)
你应该接受Omry的建议,并为此研究一个专门的库。也就是说,如果您只想了解算法,我将在下面发布。
基本上,您可以将字符串转换为更宽元素的格式,例如wchar_t
。请注意,wchar_t
存在一些可移植性问题,因为wchar_t
的大小各不相同,具体取决于您的平台。在Windows上,wchar_t
是2个字节,因此非常适合表示UTF-16。但是在UNIX / Linux上,它是四个字节,因此用于表示UTF-32。因此,对于Windows,只有在不包含0xFFFF以上的任何Unicode代码点时才能使用。对于Linux,您可以在wchar_t
中包含整个代码点范围。 (幸运的是,使用C ++ 0x Unicode字符类型可以缓解此问题。)
注意到这一点,您可以使用以下算法创建转换函数:
template <class OutputIterator>
inline OutputIterator convert(const unsigned char* it, const unsigned char* end, OutputIterator out)
{
while (it != end)
{
if (*it < 192) *out++ = *it++; // single byte character
else if (*it < 224 && it + 1 < end && *(it+1) > 127) {
// double byte character
*out++ = ((*it & 0x1F) << 6) | (*(it+1) & 0x3F);
it += 2;
}
else if (*it < 240 && it + 2 < end && *(it+1) > 127 && *(it+2) > 127) {
// triple byte character
*out++ = ((*it & 0x0F) << 12) | ((*(it+1) & 0x3F) << 6) | (*(it+2) & 0x3F);
it += 3;
}
else if (*it < 248 && it + 3 < end && *(it+1) > 127 && *(it+2) > 127 && *(it+3) > 127) {
// 4-byte character
*out++ = ((*it & 0x07) << 18) | ((*(it+1) & 0x3F) << 12) |
((*(it+2) & 0x3F) << 6) | (*(it+3) & 0x3F);
it += 4;
}
else ++it; // Invalid byte sequence (throw an exception here if you want)
}
return out;
}
int main()
{
std::string s = "\u00EAtre";
cout << s.length() << endl;
std::wstring output;
convert(reinterpret_cast<const unsigned char*> (s.c_str()),
reinterpret_cast<const unsigned char*>(s.c_str()) + s.length(), std::back_inserter(output));
cout << output.length() << endl; // Actual length
}
该算法不是完全通用的,因为InputIterator需要是无符号字符,因此您可以将每个字节解释为具有0到0xFF之间的值。 OutputIterator是通用的,(只是你可以使用std :: back_inserter而不用担心内存分配),但它作为泛型参数的用途是有限的:基本上,它必须输出到足够大的元素数组来表示UTF-16或UTF-32字符,例如wchar_t
,uint32_t
或C ++ 0x char32_t
类型。另外,我没有包含转换大于4个字节的字符字节序列的代码,但你应该从发布的内容中得到算法的工作原理。
此外,如果您只想计算字符数,而不是输出到新的宽字符缓冲区,则可以修改算法以包含计数器而不是OutputIterator。或者更好的是,只需使用Marcelo Cantos' answer来计算第一个字节。
答案 4 :(得分:4)
这是一个天真的实现,但它应该有助于您了解如何完成此操作:
std::size_t utf8_length(std::string const &s) {
std::size_t len = 0;
std::string::const_iterator begin = s.begin(), end = s.end();
while (begin != end) {
unsigned char c = *begin;
int n;
if ((c & 0x80) == 0) n = 1;
else if ((c & 0xE0) == 0xC0) n = 2;
else if ((c & 0xF0) == 0xE0) n = 3;
else if ((c & 0xF8) == 0xF0) n = 4;
else throw std::runtime_error("utf8_length: invalid UTF-8");
if (end - begin < n) {
throw std::runtime_error("utf8_length: string too short");
}
for (int i = 1; i < n; ++i) {
if ((begin[i] & 0xC0) != 0x80) {
throw std::runtime_error("utf8_length: expected continuation byte");
}
}
len += n;
begin += n;
}
return len;
}
答案 5 :(得分:2)
我建议您使用UTF8-CPP。它是一个仅用于在C ++中使用UTF-8的头文件库。使用这个lib,它看起来像这样:
int LenghtOfUtf8String( const std::string &utf8_string )
{
return utf8::distance( utf8_string.begin(), utf8_string.end() );
}
(代码来自我的头脑。)
答案 6 :(得分:1)
尝试使用像iconv这样的编码库。 它可能得到了你想要的api。
另一种方法是实现自己的utf8strlen,它确定每个代码点的长度并迭代代码点而不是字符。
答案 7 :(得分:1)
我的大多数个人C库代码都只经过了英语的真正测试,但这是我实现utf-8字符串长度函数的方式。我最初基于this wiki page table中描述的位模式。现在这不是最易读的代码,但是我的编译器更喜欢benchmark。同样为此感到遗憾的是C代码,它应该可以很容易地转换为C ++中的std :: string,尽管稍作修改:)。
size_t utf8len(const char* const str)
{
size_t len = 0;
unsigned char c = str[0];
for (size_t i = 1; c != 0; ++len, ++i)
{
if ((c & 0x80))
{
if (c < 0xC0) // Invalid increment
return 0;
c >>= 4;
if (c == 12)
c++;
i += c - 12;
}
c = str[i];
}
return len;
}
请注意,这不会验证任何字节(非常类似于此处所有其他建议的答案)。我个人将字符串验证与我的字符串长度函数分开,因为这不是责任。如果我们要将字符串验证移至另一个函数,则可以使验证完成如下操作。
bool utf8valid(const char* const str)
{
if (str == NULL)
return false;
unsigned char c = str[0];
for (size_t i = 1, inc = 0; c != 0; ++i)
{
if (inc > 1)
{
if ((c & 0xC0) != 0x80)
return false;
inc--;
}
else
{
inc = 1;
if ((c & 0x80))
{
if (c < 0xC0 || c >= 0xF8)
return false;
c >>= 4;
if (c == 12)
c++;
inc += c - 12;
}
}
c = str[i];
}
return true;
}
如果您出于可读性考虑,我会承认其他建议更具可读性哈哈!
答案 8 :(得分:0)
UTF-8 CPP库有一个功能就是这样。您可以将库包含到项目中(它很小)或只是查看函数。 http://utfcpp.sourceforge.net/
char* twochars = "\xe6\x97\xa5\xd1\x88";
size_t dist = utf8::distance(twochars, twochars + 5);
assert (dist == 2);
答案 9 :(得分:0)
这段代码我从php-iconv移植到c ++,你需要先使用iconv,希望有用:
// porting from PHP
// http://lxr.php.net/xref/PHP_5_4/ext/iconv/iconv.c#_php_iconv_strlen
#define GENERIC_SUPERSET_NBYTES 4
#define GENERIC_SUPERSET_NAME "UCS-4LE"
UInt32 iconvStrlen(const char *str, size_t nbytes, const char* encode)
{
UInt32 retVal = (unsigned int)-1;
unsigned int cnt = 0;
iconv_t cd = iconv_open(GENERIC_SUPERSET_NAME, encode);
if (cd == (iconv_t)(-1))
return retVal;
const char* in;
size_t inLeft;
char *out;
size_t outLeft;
char buf[GENERIC_SUPERSET_NBYTES * 2] = {0};
for (in = str, inLeft = nbytes, cnt = 0; inLeft > 0; cnt += 2)
{
size_t prev_in_left;
out = buf;
outLeft = sizeof(buf);
prev_in_left = inLeft;
if (iconv(cd, &in, &inLeft, (char **) &out, &outLeft) == (size_t)-1) {
if (prev_in_left == inLeft) {
break;
}
}
}
iconv_close(cd);
if (outLeft > 0)
cnt -= outLeft / GENERIC_SUPERSET_NBYTES;
retVal = cnt;
return retVal;
}
UInt32 utf8StrLen(const std::string& src)
{
return iconvStrlen(src.c_str(), src.length(), "UTF-8");
}
答案 10 :(得分:0)
这是另一种幼稚的实现,可以计算UTF-8字符串中的字符数
int utf8_strlen(const string& str)
{
int c,i,ix,q;
for (q=0, i=0, ix=str.length(); i < ix; i++, q++)
{
c = (unsigned char) str[i];
if (c>=0 && c<=127) i+=0;
else if ((c & 0xE0) == 0xC0) i+=1;
else if ((c & 0xF0) == 0xE0) i+=2;
else if ((c & 0xF8) == 0xF0) i+=3;
//else if (($c & 0xFC) == 0xF8) i+=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
//else if (($c & 0xFE) == 0xFC) i+=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
else return 0;//invalid utf8
}
return q;
}
答案 11 :(得分:0)
一种稍微懒惰的方法是只计算前导字节,但是访问每个字节。这样可以节省解码各种前导字节大小的复杂性,但显然您要付费访问所有字节,尽管通常没有那么多(2x-3x):
EventRepository > findById()
请注意,某些代码值作为前导字节是非法的,例如,那些表示比扩展unicode所需的20位更大的值,但其他方法无论如何都不知道如何处理该代码。