:前言

造车轮的时候要用到中文字符串的长度辨别,发现char的识别不准,进行了一番研究。

> 开始研究

在Windows下,中文字符在C++中的内存占用为2字节,此时采用字符串长度获取函数得到的结果会将一个中文字符识别为两个长度:

#include <stdio.h>
#include <string>
using namespace std;//string在std命名空间中
int main()
{
	string str = "abc中文def";
	printf("字符串为:%s\n", str.data());//data()函数回传字符串的指针,与c_str()相同
	int len;
	char str1[50];
	strcpy(str1, str.data());//赋值
	len = strlen(str1);
	printf("字符串的长度为(%d)\n", len);
	//使用strlen函数获取长度
	string str2 = str;//也可以用string str2.assign(str),这是string的赋值函数,不过和=没区别
	len = str2.length();//也可以用len = str2.size();
	printf("字符串的长度为(%d)\n", len);
	//使用string类的长度获取函数length()
	system("pause");
}
点击查看输出
字符串为:abc中文def
字符串的长度为(10)
字符串的长度为(10)
请按任意键继续. . .

而实际上,字符串的长度为8,并非上述方法的结果10。那么,如何获取真正的长度呢?

>> 上手尝试

其实,我们不妨试试中文字符的值:

char a = '中';
char b = '文';
char c = '字';
char d = '符';
printf("字符‘中’在编码中的值为%d\n",(int)a);
printf("字符‘文’在编码中的值为%d\n",(int)b);
printf("字符‘字’在编码中的值为%d\n",(int)c);
printf("字符‘符’在编码中的值为%d\n",(int)d);
system("pause");
点击查看输出
字符‘中’在编码中的值为-48
字符‘文’在编码中的值为-60
字符‘字’在编码中的值为-42
字符‘符’在编码中的值为-5
请按任意键继续. . .

试试其他中文字符,也都是负数。

>> 总结归纳

依据这一点,我们便可以做出一个获取含有中文的字符串长度的函数:

string版:

int getLength_str(string str)
{
	int count = 0;
	for (int i = 0; str[i]; i++)
	{
		if (str[i] < 0) i++;
        //负数说明该字符为中文字符,占用两个字节,跳过后一个字节(i++),不进行统计
		count++;
	}
    return count;
}

char版:虽然char数组也可以传入上面的函数,不过为了避免某些奇葩编译器,还是再写了一个函数,即拷即用:

int getLength_char(char str[])
{
	int count = 0;
	for (int i = 0; str[i]; i++)
	{
		if (str[i] < 0) i++;
		count++;
	}
	return count;
}

不过,char版不可以传string。

>> 试验验证

用前面的示例验证:

点击查看代码
#include <stdio.h>
#include <string>
using namespace std;
int getLength_str(string str)
{
	int count = 0;
	for (int i = 0; str[i]; i++)
	{
		if (str[i] < 0) i++;
        //负数说明该字符为中文字符,占用两个字节,跳过后一个字节(i++),不进行统计
		count++;
	}
    return count;
}
int getLength_char(char str[])
{
	int count = 0;
	for (int i = 0; str[i]; i++)
	{
		if (str[i] < 0) i++;
		count++;
	}
	return count;
}
int main()
{
	string str = "abc中文def";
	printf("字符串为:%s\n", str.data());//data()函数回传字符串的指针,与c_str()相同
	int len;
	char str1[50];
	strcpy(str1, str.data());//赋值
	len = strlen(str1);
	printf("字符串的长度为(%d)\n", len);
	//使用strlen函数获取长度
	len = getLength_char(str1);//len = getLength_str(str1);
	printf("字符串的长度为[%d]\n", len);
	//用上面的函数获取含有中文字符的字符串的真正长度
	string str2 = str;//也可以用string str2.assign(str),这是string的赋值函数,不过和=没区别
	len = str2.length();//也可以用len = str2.size();
	printf("字符串的长度为(%d)\n", len);
	//使用string类的长度获取函数length()
	len = getLength_str(str2);
	printf("字符串的长度为[%d]\n", len);
	//用上面的函数获取含有中文字符的字符串的真正长度
	system("pause");
}
点击查看输出
字符串为:abc中文def
字符串的长度为(10)
字符串的长度为[8]
字符串的长度为(10)
字符串的长度为[8]
请按任意键继续. . .

这个函数也可以获取没有中文字符的字符串长度.

总结

通过对中文字符数值的输出,从而找到char数组对中文字符串的长度处理解决方法。
当然处理中文字符串最好的方法是转换成宽字节,但会比较麻烦。
另外,新版的C++20string好像已经解决了这个长度问题。这篇文是之前在CSDN写的,当时是不可以的。

另:
字符串转宽字节后,采用wcslen(wchar_t*)方法可以准确的读出宽字节字符串的字符数(毕竟宽字节就是为了这事专门设计的)

The End
Yuito 2023

发表回复