C++中文字符求个数

目录

前言

遍历字符串(英文)的四种办法 

一、源代码

二、位掩码(BitMask)

1.前言

2.基本知识介绍

3.问题引用

4.结合实际问题

三、AND函数

四、strcpy()函数

1.源代码

运行结果

2. strcpy()函数定义

3.小知识

五、关于 &0xc0与 0x80

关于utf 8:UTF-8和Unicode,0xC0和0x80是什么? | 码农家园 (codenong.com)


前言

python遍历字符串很容易,for i in str:就可以直接遍历,那么C++呢?那么肯定要用到数组指针,但是一边学一边写了四种稍微不同的代码后,我发现这种办法只能遍历除了中文外的......英文吧...,那么怎么遍历中文呢?于是就开始探索,这就是开篇--对中文字符求个数、

遍历字符串(英文)的四种办法 

#include<iostream>
#include<cstring>
using namespace std;
int main(){ 
    char *a;
    a = "杜杜是笨蛋!";
    cout<<"这是地址&a:"<<" "<<&a; 
*******************
遍历字符串1:
笔记:无法遍历中文 
char a[]="abcd";
int len = strlen(a);
char *b=a;
for(int i=0;i<len;i++){
    cout<<*b++<<" ";
} 
*******************
遍历字符串2:
char a[]="abcd";  
int len = strlen(a);
for(int i=0;i<len;i++){
    cout<<*(a+i)<<" ";
} 
*******************
遍历字符串3:
char *a;
a = "abcd";
int len = strlen(a);
for(int i=0;i<len;i++){
    cout<<*a++<<" ";
} 
*******************
遍历字符串4:
char *a ="abcd";
int len = strlen(a);
for(int i=0;i<len;i++){
    cout<<*(a+i)<<" ";
    
return 0;
}

一、源代码

//遍历中文字符串
#include<iostream>
#include<cstring>
using namespace std;
int getnumber(const char *s){
    int i =0;
    int j =0;
    while(s[i]){
        if((s[i]&0xc0)!=0x80) j++;
            //cout<<(s[i]&0xc0)<<"  "<<0x80<<endl; 
        i++;
    }
    return j;
}
int main(){
    string a ="杜杜是笨蛋!";
    char *strc = new char[strlen(a.c_str())+1]; 
    strcpy(strc,a.c_str());
    int count = getnumber(strc);
    cout<<count; 
    return 0;
} 

二、位掩码(BitMask)

1.前言

位运算在我们实际开发中用得很少,主要原因还是它对于我们而言不好读、不好懂、也不好计算,如果不经常实践,很容易就生疏了。但实际上,位运算是一种很好的运算思想,它的优点自然是计算快,代码更少。

2.基本知识介绍

  • 二进制: 二进制是由1和0两个数字组成的,它可以表示两种状态,即开和关。所有输入电脑的任何信息最终都要转化为二进制。目前通用的是ASCII码。最基本的单位为bit。

  • 位运算: 程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。

3.问题引用

  • 老鼠试毒 有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,问至少要多少只小白鼠才能在24小时内鉴别出哪瓶水有毒?

这里,位掩码的使用就可以巧妙的解决此问题。

我们先将问题简化一下:假设只有8瓶水,其中1瓶有毒。

8杯水分别编号

将该矩阵转置,得:

水杯矩阵转置

依上述场景,取4只容器,转置后的矩阵数列配组合溶液: 取数位上为1的水,放入相应的容器,即: 第一杯:只包含8号水 第二杯:包含4、5、6、7号水 第三杯:包含2、3、6、7号水 第四杯:包含1、3、5、7号水

取4只老鼠,编号1、2、3、4,分别喝下第一杯...第四杯水, 4只老鼠的生死状态依次记为 w x y z,(w,x,y,z = {0,1}) 死亡记作1,非死亡记作0 将二进制数列wxyz转为十进制,则得到有毒水的号码。 假设6号水有毒,那么往回推算,不难看出,第2、3只老鼠会死亡, 得到的wxyz的数列就是0110,转十进制后就是6。

将1000瓶依次编号:1,2,3,4,...,1000; 且都记作二进制; 那我们要用多少位来表示呢? 总数是1000,2^9=512, 2^10=1024,于是至少要10位才够表示, 也就是:0000000001,0000000010,0000000011,...,1111101000; 道理同上。

笔记:2的3次就是8,但是在后面的计算中就会发现总会有两个药剂分不出来,所以可能每次的位数都要大一点点,才够。

4.结合实际问题

我们已经见识了二进制的厉害之处了,接下来我们结合代码来看看,在iOS开发中的应用(其实在任何开发中都一样)

  • 在实际开发中,我们常常遇到权限的判断的问题,比如说,不同的用户对系统有不同的操作权限,有的用户可能有多种权限,我们最常规的办法就是每一个权限定义一个BOOL值。 假设,某系统有4种权限,那么,就有了:

@interface BM_User : NSObject
​
@property (nonatomic, assign) BOOL permission1;
​
@property (nonatomic, assign) BOOL permission2;
​
@property (nonatomic, assign) BOOL permission3;
​
@property (nonatomic, assign) BOOL permission4;
​
@end

那用户A同时拥有permission1、permission2、permission4怎么表示呢?

BM_User *userA = [[BM_User alloc] init];
    userA.permission1 = YES;
    userA.permission2 = YES;
    userA.permission4 = YES;

这样的操作大家见多了吧?那我们来看看另一种写法:

@interface BM_User : NSObject
​
@property (nonatomic, assign) OptionPermission permission;
​
@end

有人就要问了,OptionPermission是什么鬼?来,继续。。。

/**
 权限枚举
​
 - 1: permission1,二进制第1位,0表示否,1表示是
 - 2: permission2,二进制第2位,0表示否,1表示是
 - 4: permission3,二进制第3位,0表示否,1表示是
 - 8: permission4,二进制第4位,0表示否,1表示是
 */
typedef NS_OPTIONS(NSUInteger, OptionPermission) {
    permission1 = 1 << 0,//0001,1
    permission2 = 1 << 1,//0010,2
    permission3 = 1 << 2,//0100,4
    permission4 = 1 << 3,//1000,8
};

那用户A同时拥有permission1、permission2、permission4怎么表示呢?

BM_User *userA = [[BM_User alloc] init];
    userA.permission = permission1 | permission2 | permission4;

是不是神清气爽?

现在我们就具体化4种权限,并给出基础位掩码的表达及运算:

#ifndef BM_Head_h
#define BM_Head_h
​
/**
 权限枚举
​
 - 1: 是否允许查询,二进制第1位,0表示否,1表示是
 - 2: 是否允许新增,二进制第2位,0表示否,1表示是
 - 4: 是否允许修改,二进制第3位,0表示否,1表示是
 - 8: 是否允许删除,二进制第4位,0表示否,1表示是
 */
typedef NS_OPTIONS(NSUInteger, OptionPermission) {
    ALLOW_SELECT = 1 << 0,//0001,1
    ALLOW_INSERT = 1 << 1,//0010,2
    ALLOW_UPDATE = 1 << 2,//0100,4
    ALLOW_DELETE = 1 << 3,//1000,8
};
​
#endif /* BM_Head_h */


#import "BM_Permission.h"
#import "BM_Head.h"
​
@interface BM_Permission ()
​
/** 存储目前的权限状态 */
@property (nonatomic, assign) OptionPermission flag;
​
@end
​
@implementation BM_Permission
​
​
/** 重新设置权限 */
- (void)setPermission:(OptionPermission)permission {
    self.flag = permission;
}
​
/** 添加一项或多项权限 */
- (void)enable:(OptionPermission)permission {
    self.flag |= permission;
}
​
/** 删除一项或多项权限 */
- (void)disable:(OptionPermission)permission {
    self.flag &= ~permission;
}
​
/** 是否拥某些权限 */
- (BOOL)siAllow:(OptionPermission)permission {
    return (self.flag & permission) == permission;
}
​
/** 是否禁用了某些权限 */
- (BOOL)isNotAllow:(OptionPermission)permission {
    return (self.flag & permission) == 0;
}
​
/** 是否仅仅拥有某些权限 */
- (BOOL)isOnlyAllow:(OptionPermission)permission {
    return self.flag == permission;
}

链接:位掩码(BitMask)的介绍与使用 - 简书

三、AND函数

定义:所有参数的逻辑值为真时,返回TRUE;只要有一个参数的逻辑值为假,即返回 FALSE。

笔记:类似于C++语言的while,如果while TRUE 继续执行,while FALSE 不再执行。

四、strcpy()函数

1.源代码

#include<iostream>
#include<cstring>
using namespace std;
int main(){
    string a="杜杜是笨蛋!";
    char *c = new char[strlen(a.c_str())];
    strcpy(c,a.c_str()); 
    cout<<c;
    cout<<a.c_str();
    cout<<endl;
    a = "廖廖也是笨蛋!";
    cout<<c;
    cout<<a.c_str(); 
    return 0;
} 

运行结果

杜杜是笨蛋!杜杜是笨蛋!
杜杜是笨蛋!廖廖也是笨蛋!
--------------------------------
Process exited after 0.3473 seconds with return value 0
请按任意键继续. . .

2. strcpy()函数定义

strcpy函数的作用是复制字符串,strcpy函数的声明是“char *strcpy(char *dest, const char *src)”,表示把src所指向的字符串复制到dest。(如下图)

char *copy;
string copyword = "杜杜是笨蛋!";
strcpy(copy,copyword.c_str())

定义一个char类型的指针用来复制字符串里的中文,再通过strcpy函数(可以这样记,str就是字符串的意思,cpy就是copy的缩写,连起来就是复制字符串)将字符串的值赋给指针。但是问题就来了,指针是char类型,但是字符串是string类型,类型不同的赋值会出错。那么C++里就有内置函数啦!字符串.c_str()这样就成功将string转为char类型了(反正我是这样理解的)。咱们再看c_str(),这不很明显嘛!c就是char,后面的str就是字符串,就不暗示着转换类型嘛!

3.小知识

通过运行结果可以发现,两个输出的后一个不一样!为什么呢?因为我们运用了strcpy()函数,代表着把当前的!此时此刻的字符串copy给了指针,后面再对字符串进行修改,跟指针都没有什么关系了,所谓的指针一旦复制完后就是"只读"状态存在了。

五、关于 &0xc00x80

这不是与0xc0的比较,它是与0xc0的逻辑AND操作。

位掩码0xc011 00 00 00所以AND正在做的是只提取最高两位:

ab cd ef gh AND 11 00 00 00 -- -- -- -- = ab 00 00 00 

然后将其与0x80 (二进制10 00 00 00 )进行比较。 换句话说, if语句正在检查该值的前两位是否不等于10

“为什么?”,我听到你问。 那么,这是一个很好的问题。 答案是,在UTF-8中,以位模式10开始的所有字节是多字节序列的后续字节:

UTF-8 Range Encoding Binary value ----------------- -------- -------------------------- 
  
  U+000000-U+00007f 0xxxxxxx 0xxxxxxx U+000080-U+0007ff 110yyyxx 00000yyy xxxxxxxx 10xxxxxx U+000800-U+00ffff 1110yyyy yyyyyyyy xxxxxxxx 10yyyyxx 10xxxxxx U+010000-U+10ffff 11110zzz 000zzzzz yyyyyyyy xxxxxxxx 10zzyyyy 10yyyyxx 10xxxxxx 

所以,这个小片段正在做的是遍历UTF-8string的每个字节,并计算所有不是连续字节的字节(即获得string的长度)。


旁边有一个有趣的事情。 可以按如下方式对UTF-8stream中的字节进行分类:

  • 高位设置为0 ,它是单字节值。

  • 将两个高位设置为10 ,这是一个连续字节。

  • 否则,它是多字节序列的第一个字节,前导1位的数目表示这个序列总共有多less字节( 110...表示两个字节, 1110...表示三个字节等)

关于utf 8:UTF-8和Unicode,0xC0和0x80是什么? | 码农家园 (codenong.com)

代码

cout<<(s[i]&0xc0)<<"  "<<0x80<<endl; 

当我们把源代码中的这一代码注释解开后会得到

运行结果

128  128
192  128
128  128
192  128
192  128
192  128
128  128
128  128
128  128
128  128
0  128
5
--------------------------------
Process exited after 0.5766 seconds with return value 0
请按任意键继续. . .

就很好的解释了上面那一段话。

二进制 1000 0000--> 十进制 128

二进制 1100 0000-->十进制 192

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值