C++获取含有中文字符的string长度

:前言

造车轮的时候要用到中文字符串的长度辨别,发现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

本文转载于网络 如有侵权请联系删除

相关文章

  • 数据结构-链表

    链表结构链表结构五花八门,今天我重点给你介绍三种最常见的链表结构,它们分别是:单链表、双向链表和循环链表。我们首先来看最简单、最常用的单链表。单链表我们习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址NULL,表示这是链表上最后一个结点。针对链表的插入和删除操作,我们只需要考虑相邻结点的指针改变,所以对应的时间复杂度是O(1)。链表随机访问的性能没有数组好,需要O(n)的时间复杂度。循环链表循环链表是一种特殊的单链表。实际上,循环链表也很简单。它跟单链表唯一的区别就在尾结点。我们知道,单链表的尾结点指针指向空地址,表示这就是最后的结点了。而循环链表的尾结点指针是指向链表的头结点。从我画的循环链表图中,你应该可以看出来,它像一个环一样首尾相连,所以叫作“循环”链表。从我画的图中可以看出来,双向链表需要额外的两个空间来存储后继结点和前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以

  • 分布式NoSQL列存储数据库Hbase_列族的设计(五)

    文章目录分布式NoSQL列存储数据库Hbase_列族的设计(五)知识点01:课程回顾知识点02:课程目标知识点03:Hbase设计:列族的设计知识点04:聊天系统案例:需求分析知识点05:聊天系统案例:Hbase表设计知识点06:聊天系统案例:环境准备知识点07:聊天系统案例:模拟生成数据知识点08:聊天系统案例:构建Rowkey知识点09:聊天系统案例:测试写入代码知识点10:聊天系统案例:查询需求分析知识点11:聊天系统案例:测试查询代码知识点12:聊天系统案例:查询问题知识点13:二级索引附录一:Maven依赖分布式NoSQL列存储数据库Hbase_列族的设计(五)知识点01:课程回顾Hbase存储原理 存储架构Hbase:对外提供分布式内存Master:集群管理RegionServer:数据管理HDFS:提供分布式磁盘DataNodeZookeeper:实现辅助选举、实现元数据存储存储结构Table:分布式表,一张表划分了多个RegionRegionServer:分布式集群节点,管理所有表的regionRegion:每张表的每个分区,对表的数据进行划分region的划分规则:按

  • React学习(十)-React中编写样式CSS(styled-components)

    撰文|川川VX-ID:suibichuanji点击文末左下方阅读原文,可看更多内容前言React是一个构建用户界面的js库,从UI=render()这个等式中就很好的映射了这一点,UI的显示取决于等式右边的render函数的返回值.而编写React应用,就是在编写React组件,组件中最重要的数据就是props和state,有了数据,怎么让其以什么样的显示,那就是CSS做的事情了在React中,一切皆可以是Js,也就是说在js里面可以写css,这相比传统的内容(html),层叠样式(css),行为动作(js)进行分离,这种分离仅仅是把三个不同的技术进行了物理上的分离,进行分开管理,如果从另一个视觉角度上讲,并没有实现高内聚的特点既然前端本身就是页面的展示,那么把js和css放在一起,也是一种细粒度的组合,css也可以和Js一样,通过模块化的形式嵌入到js里面去CSSmodules很好的解决了样式冲突,利用了分而治之的理念,在如今组件化开发大行其道上,同样css也在不断的进化,如同js一样,也有变量,函数等具备Js一样的活力,那么在React中是怎么实现样式的模块化的?通过单独的*.cs

  • MySQL数据库备份实操

    本文基于mysql(8.0.20)及xtrabackup(8.0.13)最新版本,实现了完整的mysqldump逻辑备份、binlog增量备份、xtrabackup物理备份恢复,帮你快速掌握操作要点TOC基础环境搭建本文相关环境均基于docker实现,下面是启动一个最基本的mysql数据库:dockerrun--namemysql-dump-test-p3306:3306-eMYSQL_ROOT_PASSWORD=admin-eMYSQL_DATABASE=user-dmysql复制mysqldump逻辑备份及恢复在开展备份之前,需要先准备如下的测试数据:数据准备createtableifnotexistst_user1 ( idbigint(20)auto_incrementprimarykeycomment'主键', namevarchar(64)comment'用户名称', birth_datetimestampcomment'生日', assertdecimal(10,2)comment'资产' )

  • 阅读源码学设计模式-单例模式

    有些编码套路是公认的,大家都参照其编写符合可观赏性的代码,那就是设计模式 现在.NETcore默认提供了DI功能,那我想设计一个全局的引擎类,进行注入服务、解析服务、配置中间件。并且要求该引擎类全局唯一,其他地方不能进行实例化。那单例模式就派上用场了。单例模式官方定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类成为单例类,它提供全局访问的方法。复制伪代码实现需求publicclassAAEngine { privatestaticAAEngineaAEngine=null; privateAAEngine(){} //获取实例 publicstaticAAEngineGetInstance() { if(aAEngine==null) { aAEngine=newAAEngine(); } returnaAEngine; } //添加服务到容器 publicvoidConfigureService() { Console.WriteLine("添加服务到容器"); } //添加中间件到请求管道 publicvoidConfigureRe

  • 如何选择消息队列?

    在高并发业务场景下,消息队列在流量削峰、解耦上有不可替代的作用。当前使用较多的消息队列有RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、Pulsar等。消息队列这么多,到底该选择哪款消息队列呢?选择消息队列的基本标准虽然这些消息队列在功能和特性方面各有优劣,但我们在选择的时候要有一个基本标准。首先,必须是开源的产品。开源意味着,如果有一天你使用的消息队列遇到了一个影响你系统业务的Bug,至少还有机会通过修改源代码来迅速修复或规避这个Bug,解决你的系统的问题,而不是等待开发者发布的下一个版本来解决。其次,这个产品必须是近年来比较流行并且有一定社区活跃度的产品。流行的好处是,只要使用场景不太冷门,遇到Bug的概率会非常低,因为大部分遇到的Bug,其他人早就遇到并且修复了。在使用过程中遇到的一些问题,也比较容易在网上搜索到类似的问题,然后很快的找到解决方案。还有一个优势就是,流行的产品与周边生态系统会有一个比较好的集成和兼容。最后,作为一款及格的消息队列,必须具备的几个特性包括:•消息的可靠传递:确保不丢消息;•Cluster:支持集群,确保不会因为某个节点

  • Ubuntu 环境下 Nginx 的安装部署

    image.png引言Perfect是一个完整和强大的工具箱,框架和应用程序服务器为Linux,iOS和MacOS(OSX)。它提供了Swift工程师为开发面向客户端和服务器端应用程序的Swift编程语言开发轻量级,可维护,可扩展的应用程序和其他REST服务所需的一切。 接下来几篇我对Perfect框架进行记录。感兴趣的可以看: SwiftPerfect-Ubuntu服务器部署 SwiftPerfect-七牛上传文件 SwiftPerfect-iOS消息推送(APNs)之获取APNs授权码 SwiftPerfect-使用systemd命令服务器项目后台运行 Ubuntu环境下Nginx的安装部署 SwiftPerfect-Nginx配置HTTPS服务器 Nginx安装sudoapt-getinstallnginx 默认的服务器配置是80端口,如果你没有其他的WebServer在运行,打开服务器域名或IP应该可以看到Welcometonginx!Nginx修改配置文件:vi/etc/nginx/nginx.conf 找到http{...}区域里面添加以下server配置:server{

  • AI 赋能安全,腾讯云发布三大安全新品和三大行业安全解决方案

    6月22日,腾讯“云+未来”峰会云安全专场在深圳举行。会上,腾讯云隆重介绍了三款重磅云安全新品,分别为主机安全、反诈骗云、网站安全,同时发布腾讯云在车联网、移动、直播三大领域的安全行业解决方案。腾讯智慧安全的能力,正借助“AI即服务”的方式全面对外开放。 本次发布的系列新品,均为云安全领域的创新突破。比如由“腾讯云云鼎实验室”全新发布的“云镜”,凭借基于IT管理,而不是简单反病毒的新思路,全面管理软硬件资产,既保障安全,又提高运维效率。云镜重点关注云平台遇到的Top5风险,落实下来,60%的主机安全问题都能得到有效解决。搭配其它云安全产品与解决方案,能够全面提升安全能力与运维水平。 本次智慧安全新品的集中发布,是腾讯针对安全的基础设施、计算力、算法、海量安全大数据,以及腾讯安全联合实验室研究成果和近二十年安全经验积累的进一步开放。借此机会,腾讯云更对其安全生态进行了全面介绍:已联手50家合作伙伴,推出超过100款云端安全产品,涵盖主机、网络、移动等七大安全领域。 腾讯云副总裁、腾讯社交网络与腾讯云安全负责人黎巍指出,安全攻防是一场技术赛跑,过去安全界的法宝,无论是“修长城”(找到边界和

  • 当我们在谈免费游戏时

    技术改变思想本来不想用“当我们在谈XXX的时候,我们在谈什么”这种俗气的标题,但这个文章的内容,确实在一些人的想法里,还是有那么一点俗气的。所以用这个标题,也算文题对应吧。免费游戏,道具收费(FreeToPlay)作为一种游戏类型的存在,似乎是一个最近10年才开始的事情,但在中国,这种类型几乎成为了唯一的游戏类型。一切产品,都是因为有用户的市场需求才会存在,但是免费游戏这个市场,又是如何被挖掘出来的呢?——这对于看清楚免费游戏背后的用户需求,应该是有很多好处的。 2006年的某天,我的老板给我打了个电话:“你们去好好研究一下《征途》!”。原来,通过从制卡商(以前的网游都靠印刷点卡卖钱)处的信息得知,有一款叫《征途》的游戏正在快速的崛起,他们的制卡数正在疯狂的增加。于是我们就硬着头皮去玩了一下这个游戏,这个画面粗糙的游戏,玩下来之后,给人一种奇怪的感觉,就好像最近看到的“金拱门”这样的感觉。整个游戏充满着一种违和感,但又说不出是什么,处处又那么的合理。这种感觉让我想起大学时玩MUD的一位同学。这位同学在玩MUD之前,并不喜欢玩电子游戏的。但是当我们一起玩MUD的之后,他是最疯狂最投入的一

  • 解密假冒“10086”诈骗

    .

  • 分布式监控系统Zabbix-3.0.3-完整安装记录-新报微信报警(企业微信)

    一般来说,Zabbix可以通过多种方式把告警信息发送到指定人,常用的有邮件,短信报警方式,但是现在越来越多的企业开始使用zabbix结合微信作为主要的告警方式,这样可以及时有效的把告警信息推送到接收人,方便告警的及时处理。之前介绍了分布式监控系统Zabbix-3.0.3-完整安装记录(6)-微信报警部署,然而新版微信已取消了企业号,改用企业微信。使用微信号发短信一般会有条数限制,企业微信没有这个限制,而且成员分组也方便。比起之前的微信企业号,企业微信方式在zabbix报警设置上还是有一点不一样的。废话不多说了,下面简单记录下:一、企业微信注册地址:http://work.weixin.qq.com/ 注册步骤没有什么可说的,按照提示信息填入信息即可。如下,可以选择"没有营业执照,继续注册"提示:这里简单的说一下,微信企业号和微信公众号是不一样的。具体按照新版本的注册信息进行即可,在此就不多做截图了~~~~~二、配置微信企业号当设置完微信号的信息之后,请继续下面操作。中间有些步骤不是特别全,省略掉了,就是设置企业的信息。可以参考分布式监控系统Zabbix-3.0.3-

  • CentOS添加开机重启自启动脚本

    环境操作系统:CentOS这里以开启重启自启动tomcat服务为例1.准备autoStart.sh脚本#!/bin/sh #chkconfig:23458090 #description:开机自启脚本 echo"###############开机自启脚本###############" #以后台挂起的方式执行 nohup/root/tomcat/bin/start.sh&复制注意:脚本前三行一定要有,不然会出现“autoStart.sh不支持chkconfig”第二行chkconfig:23458090不要随便填写,规则去网上查一下2.将start.sh脚本放到服务器/etc/rc.d/init.d/目录下3.添加脚本权限cd/etc/rc.d/init.d/ chmod+xautoStart.sh复制4.添加脚本到开机服务chkconfig--addautoStart.sh chkconfigautoStart.shon复制查看开机启动服务chkconfig--list复制

  • 腾讯云互动白板常见问题

    互动白板是否支持云端录制?是否支持与实时音视频混流录制?互动白板支持云端实时录制功能,可以录下白板画面。同时也支持混流录制,将实时音视频与白板画面混流录制。实时录制用户不合法该怎么解决?为了将录制后台的UserId与普通用户进行区分,我们约定UserId必须为tic_record_user_{roomid}_{随机数}的形式。假如课堂的音视频房间100241,一个合法的实时录制UserId为tic_record_user_100241_100。请将{roomid}替换成您真实的音视频房间号,并检查您提供的UserId是否符合如上规则。实时录制为什么会自动结束了?房间内5分钟没有音视频上行及白板操作,以及暂停超过90分钟会导致实时录制自动停止。如果只是暂时停止推流,请调用暂停接口,如果录制暂停时间超过90分钟,请调用停止录制接口,在需要恢复录制的时候再次开始录制。实时录制没有录制到白板操作?这种情况一般是以下问题导致:1.发起录制时提供的录制用户ID与其他用户ID重复,且在录制期间在其他设备登录了这个录制用户,导致录制服务被踢。解决办法:确保录制用户ID是唯一的,且不会被其他人使用。2.

  • 局部敏感哈希之E^2LSH

      需要代码联系作者,不做义务咨询。需要代码联系我QQ:1198552415,本人不做义务咨询。复制 一.算法实现   基于p-stable分布,并以‘哈希技术分类’中的分层法为使用方法,就产生了E2LSH算法。   E2LSH中的哈希函数定义如下:           其中,v为d维原始数据,a为随机变量,由正态分布产生;w为宽度值,因为a∙v+b得到的是一个实数,如果不加以处理,那么起不到桶的效果,w是E2LSH中最重要的参数,调得过大,数据就被划分到一个桶中去了,过小就起不到局部敏感的效果。b使用均匀分布随机产生,均匀分布的范围在[0,w]。   但是这样,得到的结果是(N1,N2,…,Nk),其中N1,N2,…,Nk在整数域而不是只有0,1两个值,这样的k元组就代表一个桶。但将k元组直接当做桶标号存入哈希表,占用内存且不便于查找,为了方便存储,设计者又将其分层,使用数组+链表的方式。   对每个形式为k元组的桶标号,使用如下h1函数和h2函数计算得到两个值,其中h1的结果是数组中的位置,数组的大小也相当于哈希

  • 初次见面,你好

      这是我博客的介绍,也请你务必来看一下,这会使你更好的来参观和汲取某些你所想要的知识:   First,如果你有什么建议的话,请尽情地在你所看的文章(随笔)下留言(本人蒟蒻,大佬轻喷)。如果有什么不明白的地方欢迎随时提问,我会尽我最大的可能来回答你们的问题,或是通过下面这个链接来联系我:Contact。   Second,如果你想看一些好看的图片(如果大的话,请缩小后食用),那么请点击This   Then,如果你是C++的初学者,欢迎你从我的博客中寻找你想找到的答案。我的博客分成了几个模块,如若在这个找不到的话,就在去别的模块找找吧。   那么,就这些,请多指教了。

  • 加载静态文件,父模板的继承和扩展

    用url_for加载静态文件 <scriptsrc="{{url_for('static',filename='js/login.js')}}"></script> flask从static文件夹开始寻找 可用于加载css,js,image文件 继承和扩展 把一些公共的代码放在父模板中,避免每个模板写同样的内容。base.html 子模板继承父模板  {%extends'base.html’%} 父模板提前定义好子模板可以实现一些自己需求的位置及名称。block <title>{%blocktitle%}{%endblock%}-MIS问答平台</title> {%blockhead%}{%endblock%} {%blockmain%}{%endblock%} 子模板中写代码实现自己的需求。block  {%blocktitle%}登录{%endblock%} 首页、登录页、注册页都按上述步骤改写。 <!DOCTYPEhtml> <htmllang="en"> <head&

  • IPv6与IPv4最主要的不同

    IP第6个版本(IPv6),是互联网协议的新版本,设计为IP第4版本(IPv4,RFC-791)的继任。从IPv4升级到IPv6主要的改变有以下几类: 扩展地址容量 IPv6将IP地址的位址从32位提升到128位,支持更多级别的地址继承,极大量的地址节点,更简单的自动地址分配。通过在多播地址中添加的一个“SCOPE”域,多播路由的可扩展性获得提高。引入了一个新的地址类型,叫做任意地址(anycastaddress),用于将包发送给一组节点中的任意一个地址。 IP头简化  一些IPv4下头部内容已经被削减或者变成可选的了,这是为了减少常用处理的包和限制IPv6头所占用的带宽。 更好地支持扩展和选项功能   流量标签功能   认证和隐私功能   原文如下: IPversion6(IPv6)isanewversionoftheInternetProtocol, designedasthesuccessortoIPversion4(IPv4)[RFC-791].The changesfromIPv4toIPv6f

  • sublime text 前端插件安装

    PackageControl安装 打开sublime编辑器,ctrl+`打开安装PackageControl界面: sublimetext3: importurllib.request,os,hashlib;h='6f4c264a24d933ce70df5dedcf1dcaee'+'ebe013ee18cced0ef93d5f746d80ef60';pf='PackageControl.sublime-package';ipp=sublime.installed_packages_path();urllib.request.install_opener(urllib.request.build_opener(urllib.request.ProxyHandler()));by=urllib.request.urlopen('http://packagecontrol.io/'+pf.replace('','%20')).read();dh=hashlib.sha256(by).hexdigest();print('Errorvalidatingdownload(got%sinstea

  • C. Circular Local MiniMax #794 div2

    Problem-C-Codeforces 题意给你序列a,看看能不能重新排列,严格满足一个数小于两边或者大于两边 奇数必不可能:大小大小大(第一个和最后一个两个大重复了,所以不可能的) 然后我又片面的想了个数,其实用不着,只需要按照大小排好最后检查一下即可 #include<iostream> #include<map> #include<cstring> #include<vector> #include<algorithm> #include<cmath> usingnamespacestd; constintN=2e5+10; inta[N],b[N]; map<int,int>mp; intmain(){ intT; cin>>T; while(T--) { mp.clear(); intn; cin>>n; for(inti=1;i<=n;i++) { cin>>a[i]; mp[a[i]]++; } if(n%2==1) { cout<<

  • JS 一张图理解prototype、proto和constructor的关系

    转载于原文地址:https://www.cnblogs.com/xiaohuochai/p/5721552.html(感谢大神的总结) 前面的话   javascript里的关系又多又乱。作用域链是一种单向的链式关系,还算简单清晰;this机制的调用关系,稍微有些复杂;而关于原型,则是prototype、proto和constructor的三角关系。本文先用一张图开宗明义,然后详细解释原型的三角关系   图示   概念   上图中的复杂关系,实际上来源就两行代码 functionFoo(){}; varf1=newFoo;复制 【构造函数】   用来初始化新创建的对象的函数是构造函数。在例子中,Foo()函数是构造函数 【实例对象】   通过构造函数的new操作创建的对象是实例对象。可以用一个构造函数,构造多个实例对象 functionFoo(){}; varf1=newFoo; varf2=newFoo; console.log(f1===f2);//false复制 【原型对象及prototype】   构造函数有一个prototype属性,指向实例

  • Ignoring Provides line with DepCompareOp for package gdb-minimal

    运行sudoapt-getupdate时出现。 参考 https://askubuntu.com/questions/946402/apt-get-update-warning-ignoring-provides-line-with-depcompareop-for-package

相关推荐

推荐阅读