C++ string 字符串 知识点 小结

【摘要】

字符串String - 要求字符串类型、字符数组类型,字符指针类型三大类知识点。

1.要求熟记字符串与数字的转换函数;循环移位函数;拷贝字符串函数strcpy的实现;最频繁字符串查找函数;最长复现字符串查找函数;strstr函数的实现;其中,字符指针特点在于存在移位操作和常量与变量两个属性。字符数组和字符指针可以采用C字符函数,而字符串对象则需要熟悉字符串方法。本章考察点主要有:字符类型转换,寻找字符串中高频子串,最长复现子串,字符串循环移位和字符串类方法与C方法的实现。

【正文】

字符串与数字的相互转换

  • 使用 string 类的时候,不仅仅是头文件,而且在main()之前一定要写入“using namespace std; ”。一定要写!!!重要的事情再说一遍!!!一定要写“using namespace std; ”!!!否则,声明一个 string 的变量就报错一个 !!!
  • string要经过转化为char *才可以用strlen( ),strlen是c语言的库,是不能直接用于string!或者这样也行 

string str = "123456789";

cout<<strlen(str.c_str());

  • str[i]-"0" 是会报错的,一般使用str[i]-'0'即可,可以理解成字符变量之间的运算。
  • 一般不要随便声明字符串(string)变量在声明的同时最好初始化一个常量或者变量给它,结合构造函数理解。字符串不知道长度的时候,等于是没有分配内存的指针,这个时候给指针赋值,将直接导致运行错误。

【勘误】

这里是之前理解错误造成的片面曲解,字符串变量是不能等价于字符指针变量的。

现给出两者比较如下:

  • 字符串变量与字符指针变量都可以采用字符数组形式读取,但是,如果字符指针指向的是常量,那么指针变量将不可随意更改。

char *w = "12345678";

// w[3] = '9'; // 运行报错

  • 如果指针变量指向的是变量,那么指针变量、字符串是相似的,都可以采用字符数组的形式改写变量。

string w = "12345678";

w[3] = '9'; 

char ch[ ] = {'1','2','3','4','5','6'};

char *c = ch;

c[3] = '9';

  • 如果指针变量指向的是变量,那么指针时可以采用移位操作对指针指向变量赋值的,但是,指针与移位符表示指针指向该位置及之后的元素,而不是位置元素本身。字符串移位是会报错的。

cout<<*c+2<<endl; // 输出 3456


  • size()和 length()适合结构体或者类对象,不对应一般数据类型。但是,一般采用length()函数,采用length()函数,采用length()函数!!!!!!重要的事情说 3 遍!!!字符数组的长度可以使用strlen()表示,sizeof()也可以表示。但是,请注意,如果是指针数组,sizeof()将给出的是地址值,如果是数组的话,那么输出的将是数组的长度。
  • itoa函数、atoi函数等 实现
常用库函数atoi,itoa,strcpy,strcmp的实现 http://blog.csdn.net/jianzhibeihang/article/details/5710722;

  • assert 的头文件与使用

#include <assert.h>
void assert( int expression ); // 其作用是如果它的条件返回错误,则终止程序执行
详见 assert()函数用法总结 http://www.cnblogs.com/ggzss/archive/2011/08/18/2145017.html

  • 为了实现链式表达式,字符串系返回值常常设置为指向字符的指针。

链式表达式就是相关表达更加方便,就像链子一样简化代码。如: int length = strlen( strcpy( strDest, “hello world”));省去了多行代码标识。

相关知识详见 关于链式表达式 http://www.cnblogs.com/hnrainll/archive/2011/04/29/2032868.html

  • 一般字符串结尾不是"\0"而是'\0'。这一点一定要引起重视,单个的就是字符,即便是字符串的成员,它也是字符。
  • 为了消除申请的动态内存大于使用内存,导致输出有效码字后有无效乱码干扰,可在字符串之后添加'\0',以示输出完毕。字符数组初始化的时候,字符数组的末尾请记得添'\0'。


字符串的循环右移

  • 虽然,string 就是 char* 数据类型得变式,书中也曾提到,字符串str 有 *str表示首字符,str表示全字符串,&str表示字符串首地址。但是,这并不能表示代码中就可以将 char* 与 string画上等号。比如,在strcpy()函数上。在循环右移中我们也将遇到 string mystr; 与 char *myptr; ,其中,mystr++就是报错,myptr++就是能运行!
  • char* 变量尽量指向变量,指向常量会引入很多不必要的麻烦!!!
  • memcpy、strcpy、memset函数

memcpy函数

  原型:extern void *memcpy(void *dest, void *src, unsigned int count);
  用法:#include <string.h>
  功能:由src所指内存区域复制count个字节到dest所指内存区域。
  说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
memcpy函数详见  memcpy函数  http://blog.csdn.net/fcrane/article/details/4390991

memcpy与strcpy的比较
memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。 
strcpy就只能拷贝字符串了,它遇到'/0'就结束拷贝;例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个‘/0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。也可能不报错,这要看编译器。不过,报不报错这样做都是不安全的。
这个时候数组要比“指针+分配动态内存”简洁。
最后,在字符数据之后一定要记得加上'\0',千万记得!!!不管是字符数组还是字符指针,都最好添上,而且,字符数组最好定义大于字符数目。最后,在强调一遍,字符指针在结束处不要忘记'\0' ,这是必须的!!!
【注】
面试宝典在strcpy函数上,认为指针拷贝到数组不需要考虑指针的'\0',数组拷贝到数组要保证源数组较目标数组至少小一位。换言之,面试宝典人为目标对象为数组时,必须考虑'\0'。
无符号字符 unsigned char ch;
char 型数据的强制整型为-128~127unsigned char 的强制整型转换为 0~255。
  • c++中cerr与cout区别

cerr:错误输出流,无缓冲,不可以重定向。输出的数据不经过缓冲区,直接放到指定的目标中,既然不经过缓冲区那么其它程序就无法把要输出的内容送到其他目标中,所以说它不能被重定向。 

cout:标准输出流,有缓冲,可重定向。把要输出的数据先放到缓冲区中,然后再从缓冲区到你指定的设备中。当向cout流插入一个endl,不论缓冲区是否满了,都立即输出流中所有数据,然后插入一个换行符.

cerr不被缓冲,也就说错误消息可以直接发送到显示器,而无需等到缓冲区或者新的换行符时,才被显示。而cout是一个有缓冲的输出。关于这一点,很多的资料上都有提到,但是cerr也可以通过 rdbuf 方法重定向到文件中。

C++标准流重定向及cout和cerr的区别 http://blog.csdn.net/FromHJ/article/details/8760763

字符串常见考题

求字符串中连续出现次数最多的子串
  • substr 函数
string str.substr (int val_1,int val_2)的两个参量分别表示起始值和子串长度;
  • 实现步骤
1)将字符串的同后缀字符串子串集合,可输出查看;
如:“12345”,同后缀集合为{“12345”;“2345”;“345”;“45”;“5”}。
2)固定第 i 个子串,以它为基准往后的第 j 个子串,判断它们是否有相同的前缀子串,子串长度为(j - i)个字符。基准串 str[i].substr(0, j - i )。
a)如果没有相同子串则继续滑动 j 直到找到,如果滑动到最后一个 j 子串还没有发现,那么,就让 i 滑动到下一个;
b)如果找到相同子串,则让 j 子串为基准,再往后滑动(j - i)个子串,判断与基准串的相似性。直到不同或者到子串集合末尾为止;
【注】
这里有个逻辑,就是连续的相同的子串。如果相同连续,那么,第 i 个子串的(j - i)字符之后,恰好就遇到第 j 个子串首字符。
3)记录每次的重复次数和子串,本次重复次数与之前最大的重复次数比较,最终输出最大重复次数。
  • 详细代码
1)求一个字符串中连续出现次数最多的子串(程序面试宝典)http://blog.csdn.net/sleeping_dog/article/details/9427075 (亮点在数据结构 pair < *,* > val ( fir,sec) );
http://blog.csdn.net/ysu108/article/details/7795479 (亮点在动态分配内存) ;
http://blog.csdn.net/phphot/article/details/2337739(亮点在接口的思想)。
  • pair类型概述

pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同,基本的定义如下:


pair<int, string> a;


表示a中有两个类型元素,第一个元素是int型的,第二个元素是string类型的,如果创建pair的时候没有对其进行初始化,则调用默认构造函数对其初始化。


pair<string, string> a("James", "Joy");


也可以像上面一样在定义的时候直接对其初始化。由于pair类型的使用比较繁琐,因为如果要定义多个形同的pair类型的时候,可以时候typedef简化声明


pair<int, string> a;


表示a中有两个类型元素,第一个元素是int型的,第二个元素是string类型的,如果创建pair的时候没有对其进行初始化,则调用默认构造函数对其初始化。


pair<string, string> a("James", "Joy");


也可以像上面一样在定义的时候直接对其初始化。由于pair类型的使用比较繁琐,因为如果要定义多个形同的pair类型的时候,可以时候typedef简化声明


typedef pair<string, string> author;

author pro("May", "Lily");

author joye("James", "Joyce");


  • pair对象的操作 
对于pair类,由于它只有两个元素,分别名为first和second,因此直接使用普通的点操作符即可访问其成员


pair<string, string> a("Lily", "Poly"); 

string name;

name = pair.second;


  • 生成新的pair对象

可以使用make_pair对已存在的两个数据构造一个新的pair类型:


int a = 8;

string m = "James";

pair<int, string> new_one;

new_one = make_pair(a, m);


pair两个成员变量的的名字就叫 first 和 second 。
pair变量可以通过pairval.first = ***;pairval.second = ***;
和 pairval = make_pair(firval,secval);两种方式赋值,不同的是make_pair的第一个变量不可以是常量,只能是变量名。
见到 vector 等变量,首先想到的不应该是 vecval[i] 这一种赋值方式,而是vecval的成员函数,诸如push,push_back,pop之列。
pair相关详细详见,C++ 10.1和10.2 关联容器-----pair 类型 

  http://blog.csdn.net/hlsdbd1990/article/details/46438003


字符串查找函数:

1)逆序查找函数rfind;

2)正序查找函数find。


查找复现的最长字符串子串

size_t,在C++中,设计 size_t 就是为了适应多个平台的 。size_t的引入增强了程序在不同平台上的可移植性size_t是针对系统定制的一种数据类型,一般是整型


不使用库函数实现strstr()函数

strstr()函数详见:实现strstr()函数 http://blog.csdn.net/tianmohust/article/details/7246587

在不使用库函数的时候,处理数据最保险的是使用指针和字符数组,尤其是字符数组,安全系数最高,不容易出现溢出等问题。当然,数组的局限性很高,但是,安全系数也较高。

注意,这里有一个标点的判断,落实多种情况在面试宝典里面没有体现,可自行约束修正;

注意,不论是字符串、字符数组还是指针都要 '\0'。


这里一定要明白字符串是可以像字符数组一样操作的,字符串元素的操作跟字符串数组可以一个道理。
string str = "******"; 
str[3]是可读写,可删改的,不过以面向对象的思想来处理数组是更提倡的一种方式,不过数组方式也勉强可行,只是不高级罢了。
但仅仅只是定义的字符串就不可以像这样操作了。
string str;
str[0] = '8';
这样操作是错误的!!!

sprintf (char* dst , "%format_a%format_b%format_c",val_a,val_b,val_c);//该函数,负责合成字符串,不负责输出!


固定范式编写字符串移位函数
【题】移动字符串内容,传入参数char *a和m,规则如下:将a中字符串的倒数m个字符移到字符串前面,其余依次像右移。例如:ABCDEFGHI,M=3,那么移到之后就是GHIABCDEF。注意不得修改原代码
指针数据也可以字符数组形式读写,可以字符数组删改。
看见异常点不要慌,比如题目中的 w[len-m] = '\0';
熟悉表达的执行顺序,从左至右,且一旦满足就不往右看;

【源码】

#include<stdio.h>  
#include<string.h>  
  
void fun(char *w,int m);  
  
void main()  
{  
    char w[30];  
    int m;  
    printf("请输入一个字符串\n");  
    gets(w);  
    printf("请输入移动的字符数\n");  
    scanf("%d",&m);  
    fun(w,m);  
    printf("移动后的结果为%s\n",w);  
}  
  
void fun(char *w,int m)  
{  
    int i = 0,len = strlen(w);  
    if(m > len)       // 此处对于输入的移动字符数超过字符串长度做了处理  
        m = len;        
    while(len-m > 0 || (m = 0) != 0) //(m=0)!=0的目的是给m赋值为0,并且不进入循环,还满足要求  
        for(i = 0,w[len] = w[0],++m;i < len;i++) w[i] = w[i+1];  
    w[len-m] = '\0';  //不要被表象迷惑,刚开始就以为是在中间加了'\0',然后蒙了~  
}  
msbd-4.0-P.240-5





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值