c++字符串你怎么处理

2018-09-09 14:59:33 u013834525 阅读数 10242

0. 写在最前面

希望大家收藏:

本文持续更新地址:https://haoqchen.site/2018/09/09/string-and-char/

也欢迎收藏我的另一篇总结:编程常用数据结构与函数总结(vector、list、stack、deque、字符串)

C++字符串处理有最原始的char以及string两种方式,这里对两种方式常用的功能进行总结及对比。

如果觉得还不错就点个赞,点个关注呗,博主会长期更新自己的学习和收获。

  string char*、char[]
头文件 #include <string> 不需要

定义

与初始化

string s1("abc");

string s2(s1);

string s3(4, 's');//初始化为4个's'

char* a = "test";//数据存在静态存储区,不能修改

char a[] = "test";//开辟数组再存储,可以修改

char* a = new char[10];

memset(a, '0', sizeof(char)*10);

相互转化

char* p = "hello";

string s(p);

s = p;

string str("test");

const char* p = str.data();//记得要加const或者强制类型转换成(char*)

const char* p = str.c_str();

char p[10];

std::size_t length = str.copy(p,5,0);//从第0个开始复制5个,返回有效复制的数量,需要在p最后添加'\0'

char * cstr = new char [str.length()+1]; 
std::strcpy (cstr, str.c_str());

或者逐个复制

实际大小 str.size() std::strlen(p)//#include <cstring>(C++写法)或者<string.h>(C写法)
容器大小 str.capacity()

数组形式p[],可以使用sizeof(p)来获得数组大小

指针形式没有容器概念,除非是new的,对指针用sizeof将得到指针本身的大小,由系统位数决定

倒置

#include <algorithm> // std::reverse

std::reverse(str.begin(),str.end());

char* strrev(char* s);

查找字符

&字符串

find//从头开始找
rfind//从尾开始找

这四个函数都有四种重载:

size_t find (const string& str, size_t pos = 0);//查找子string,默认从父string的第0个字符开始,如果要查找多个相似的,则可以将pos设置为上次查找到的+1
size_t find (const char* s, size_t pos = 0);//查找字符串,默认从0开始
size_t find (const char* s, size_t pos, size_type n);//同上,但只比较n个
size_t find (char c, size_t pos = 0);//比较字符。

当然,形参初始化的值可能不一样,返回的都是地址索引,需要通过found!=std::string::npos判断是否有效。

find_first_of
find_last_of

find_first_not_of
find_last_not_of

也有上面四种重载,不过这里是返回第一个出现、没有出现在子str的字符的索引。

std::string str ("look for non-alphabetic characters...");
std::size_t found = str.find_first_not_of("abcdefghijklmnopqrstuvwxyz ");

将返回'-'的索引

char* strchr(char* s, char c);//查找字符串s中首次出现字符c的位置,返回c位置的指针,如不存在返回NULL

char * strrchr(const char *str, int c);//查找字符倒数第一次出现的位置

char *strstr(const char *s1,const char *s2);//查找第一次出现s2的位置,返回s2的位置指针,如不存在返回NULL

char *strrstr(const char *s1,const char *s2);//查找倒数第一个出现s2的位置

int strspn(const char *s,const char *accept);//作用同右侧find_first_not_of。返回s中第一个没有在accept出现的字符的索引。通过两个for循环来实现

int strcspn(const char *s,const char *reject);//范湖s中第一个在reject出现的字符的索引。

大小写

转换

两者都是不提供这个功能的,但是C++有两个库函数,头文件是#include <ctype.h>:

int tolower ( int c );

int toupper ( int c );

实现也很简单:

int tolower(int c)
{
    if ((c >= 'A') && (c <= 'Z'))
        return c + ('a' - 'A');
    return c;
}

比较字符串大小

一共五种重载形式

int compare (const string& str);
int compare (size_t pos, size_t len, const string& str);
int compare (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen);
int compare (const char* s) const;
int compare (size_t pos, size_t len, const char* s);
int compare (size_t pos, size_t len, const char* s, size_t n);

返回与右边一致,其中pos表示从str的第几个元素开始比较,一共比较len个字符,如果不够,则有多少比较多少。subpos、sublen是相对比较字符串而言的

string重载了==、<等符号,可以直接用符号比较

string没有提供不区分大小写的比较,感觉可以读取出数据后通过右侧的函数来进行比较。

int strcmp(char* s1, char* s2);//区分字母大小写比较
当s1 < s2时,返回值<0; //对于第一个不相等的字符,s1对应的小于s2对应的,或者全相等,但是s1的字符数量少于s2的字符数量
当s1 = s2时,返回值=0; 
当s1 > s2时,返回值>0。

int stricmp(char* s1, char* s2);//不区分字母大小写

int strncmp(char* s1, char* s2, int n);//只比较前n个字符,区分大小

int strnicmp(char* s1, char* s2, int n);//之比较前n,不区分大小写

 

数字转

字符串

1、stringstream(多次使用需要使用clear()清除)

int N = 10;

stringstream ss;//#include <sstream>

string str;

ss << N;

ss >> str;

2、string = std::to_string(N)方法

只需包含<string>头文件即可

1、使用sprintf:#include <stdio.h>

char c[100];
int k=255;
sprintf(c,"%d",k);//d是有符号10进制数,u是无符号10进制

double a=123.321;
sprintf(c,”%.3lf”,a);

sprintf(c,"%x",k);//转换成16进制,如果想要大写的话可以用X,8进制是o
//c包含"ff" c[0]='f' c[1]='f'

2、itoa貌似跟平台有关,不建议使用。

字符串转数字

1、N = stringToNum<int>(str);//需要#include <sstream>

2、在<string>中实现的

int stoi (const string& str, size_t* idx = 0, int base = 10);//idx返回第一个非数字的指针索引,base默认10进制

long stol、unsigned long stoul、long long stoll、unsigned long long stoull、float stof、double stod、long double stold

3、stringstream//#include <sstream>

string a = "123.32";

double res;

stringstream ss;

ss << a;

ss >> res;

char a[10] = {"255"};
int b;
sscanf(a,"%d",&b);

char a[10] = {"ff"};//十六进制

int b;
sscanf(a,"%x",&b);

char str[]=”123.321”; 
double a; 
sscanf(str,”%lf”,&a);

另外也可以用atoi(),atol(),atof()

拷贝与

合并

char p[10];

std::size_t length = str.copy(p,5,0);//从第0个开始复制5个,返回有效复制的数量,需要在p最后添加'\0'

合并直接用+号即可

或者用.append()

string& append (const string& str);
string& append (const string& str, size_t subpos, size_t sublen);
string& append (const char* s);
string& append (const char* s, size_t n);
string& append (size_t n, char c);

char* strcpy(char* dest, char* src);//把从src地址开始且含有 ‘\0’结束符的字符串复制到以dest开始的地址空间。返回指向dest的指针。

char* strncpy(char* dest, char* src, int size_tn);//复制n个,这个复制不保证NULL结尾,需要自己初始化。

char* strcat(char* dest, char* src);//把src所指字符串添加到dest结尾处(覆盖dest结尾处的 ‘\0’)并添加 ‘\0’。返回指向dest的指针。且内存不可重叠。

char* strncat(char* dest, char* src, int n);//部分合并

用sprintf也行,但是效率不高。

插入与

替换

string& insert (size_t pos, const string& str);
string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
string& insert (size_t pos, const char* s);
string& insert (size_t pos, const char* s, size_t n);
string& insert (size_t pos,   size_t n, char c);

 

string& replace (size_t pos,        size_t len,        const string& str);
string& replace (size_t pos,        size_t len,        const string& str,
                 size_t subpos, size_t sublen);    
string& replace (size_t pos,        size_t len,        const char* s);
string& replace (size_t pos,        size_t len,        const char* s, size_t n);
string& replace (size_t pos,        size_t len,        size_t n, char c);

不存在

字符串

切割

string substr (size_t pos = 0, size_t len = npos):

std::string str2 = str.substr (3,5);//返回str的第3个开始的5个元素。可以配合find函数来实现针对特殊元素的切割

char* strtok (char* str,constchar* delimiters );//以delimiters中的每个字符作为分割符,将str剪切,返回一个个字符串。传入NULL时代表继续切割后续的。下面的程序预计输出hello boy this is heima。第二个参数可以是" ,\t\n\\"等。(其实就是将被切割的换成'\0'?待分割字符串的完整性会被破坏,而且线程不安全。具体分析请看strtok的安全性探讨

IO函数

std::getline (std::cin,str,s);//这个不需要固定大小,但是速度慢,生成文件大。默认终止符是换行

cin.get(arrayname,size,s)//读取size-1个字符,在末尾添加'\0'遇终止符停止,返回是否成功。读取玩指针指向终止符。s默认换行

cin.getline(arrayname,size,s)//同上,只是指针指向终止符之后

 

 

2019-05-27 19:32:26 weixin_41256281 阅读数 2119


你编写的每个应用程序都会使用某种类型的字符串。使用老式C语言时,没有太多的选择,只
能使用普通的以null结尾的字符数组来表示字符串。遗憾的是,这种表示方式会导致很多问题,例 如会导致安全攻击的缓冲区溢出。C++ STL包含了一个安全易用的std::string类,这个类没有这些 缺点。

1. C风格的字符串

在C语言中,字符串表示为字符的数组。字符串中的最后一个字符是空字符(\0’)
C++包含一些来自C语言的字符串操作函数,它们在< cstring >头文件中定义。

函数名称 说明
strlen() 返回字符串长度,不包含空字符‘\0’的一字节,字符串长度不等于字符个数
strcpy() 字符串拷贝

C和C++中的sizeof操作符可用于获得给定数据类型或变量的大小。例如,sizeof(char)返回1,
因为char的大小是1字节。但是,在C风格的字符串中,sizeof()和strlen()是不同的。绝对不要通过 sizeof()获得字符串的大小。如果C风格的字符串存储为char[],则sizeof()返回字符串使用的实际内存,包括‘\0’字符。例如:

char text[]= "abcdef";
size_t s1= sizeof(text); // is 7
size_t s2 = strlen(text); // is 6

但是,如果C风格的字符串存储为char*, sizeof()就返回指针的大小!例如:

const char* text2 = "abcdef";
size_t s3 = sizeof(text2); // is platform-dependent
size_t s4 = strlen(text2); // is 6

在32位模式编译时,s3的值为4,而在64位编译时,s3的值为8,因为这返回的是指针const char*
的大小。可在<cstring>头文件中找到操作字符串的C函数的完整列表。

在Microsoft Visual Studio中使用C风格的字符串函数时,编译器可能会给 出安全相关的警告甚或错误,说明这些函数已经被废弃了。使用其他C标准库函数可以避免这些警告,例如
strcpy_s()和strcat_s(),这些函数是“安全C库” (ISO/IEC TR 24731)标准的一部分。然而,最好的解决方案是切换到C++的 string 类。

2. 字符串字面量

注意,C++程序中编写的字符串要用引号包围。例如,下面的代码输出字符串"hello",这段代码
包含这个字符串本身,而不是一个包含这个字符串的变量:

cout <<"hello" << endl;

在上面的代码中,"hello”是一个字符串字面量(string literal),因为这个字符串以值的形式写出,
而不是一个变量。与字符串字面量关联的真正内存在内存的只读部分中。通过这种方式,编译器可 以重用等价字符串字面量的引用,来优化内存的使用。也就是说,即使一个程序使用了 500次"hello"字符串字面量,编译器也只在内存中创建一个hello实例。这种技术称为字面量池
(literal pooling)。
字符串字面量可以赋值给变量,但因为字符串字面量位于内存的只读部分,且使用了字面量池,
所以这样做会产生风险。C++标准正式指出:字符串字面量的类型为“n个const char的数组”,然而为了向后兼容较老的不支持const的代码,大部分编译器不会强制程序将字符串字面量赋值给 const char类型的变量。这些编译器允许将字符串赋值给不带有const的char,而且整个程序可以 正常运行,除非试图修改字符串。一般情况下,试图修改字符串的行为是没有定义的。它可能会导 致程序崩溃;可能使程序继续执行,看起来没有什么莫名其妙的副作用;可能不加通告地忽略修改 行为;可能修改行为是有效的,这完全取决于编译器。例如,下面的代码展示了未定义的行为:

char* ptr = "hello"; // Assign the string literal to a variable.
ptr[1] = 'a ';       // Undefined behavior!

一种更安全的编码方法是在引用字符串常量时,使用指向 const 字符的指针。下面的代码包含
同样的 bug,但由于这段代码将字符串字面量赋值给 const char* 所以编译器会捕捉到任何写入只 读内存的企图。

const char* ptr = "hello";  // Assign the string literal to a variable.
ptr[ 1] = 'a';              // Error! Attempts to write to read-only memory

还可以将字符串字面量用作字符数组(char®的初始值。这种情况下,编译器会创建一个足以放 下这个字符串的数组,然后将字符串复制到这个数组。因此,编译器不会将字面量放在只读的内存中,也不会进行字面量的池操作。

char arr [] = "hello"; // Compiler takes care of creating appropriate sized 
					   // character array arr.
arr[ 1] = 'a ';        // The contents can be modified.

3.C++ std::string 类

C++提供了一个得到极大改善的字符串概念,并作为标准库的一部分提供了这个字符串的实现。 在C++中,std::string是一个类(实际上是basic string模板类的一个实例),这个类支持
<cstring >中提 供的许多功能,还能自动管理内存分配。string类在std名称空间的< string >头文件中定义。

3.1 C风格字符串的优势和劣势

优势 劣势
很简单,底层使用了基本的字符类型和数组结构 为了模拟一等字符串数据类型,需要付出很多努力
量级轻,如果使用得当,只会占用所需的内存 使用难度大,而且很容易产生难以找到的内存bug
很低级,因此可以按操作原始内存的方式轻松操作和复制字符串 没有利用C++的面向对象特性
能够很好地被C语言程序员理解 要求程序员了解底层的表示方式

3.2 使用string类

尽管string是一个类,但是几乎总是可以把string当做内建类型使用。事实上,把string想象为
简单类型更容易发挥string的作用。通过运算符重载的神奇作用,C++的string使用起来比C字符串容易得多。

3.2.1 std::string 字面量

源代码中的字符串字面量通常解释为const char*。使用用户定义的标准字面量”s”可以把字符串
字面量解释为std::string。例如:

auto stringl = "Hello World"; // stringl will be a const char*
auto string2 = "Hello World"s; // string2 will be an std::string

3.2.2 c++字符串的数值转换

数值转字符串 字符串转数值
to_string(int val) int stoi(const string& str, size_t *idx=0, int base=10)
to_string(unsigned val) long stol(const string& str, size_t *idx=0, int base=10)
to_string(long val) unsigned long stoul(const string& str, size_t *idx=0, int base=10)
(1)字符串转数值

int stoi(const string& str, size_t *idx=0, int base=10)

stoi将n进制字符串转为十进制,第二个参数是字符串起始位置,第三个参数表示n进制

也可以直接用重载的 int stoi(const string& str),默认字符串为十进制,起始位置为0,制

#include<string>
#include <iostream>
#include <exception>
using namespace std;
int main()
{
	int i=0;
	try
	{
		i = stoi("FEEF", 0, 16);
		//int i = stoi("我的"); 输入非法时,可以捕获异常"invalid stoi argument"
	}
	catch (exception e)
	{
		cout << e.what() << endl;
	}
	system("pause");
	return 0;
}

输出结果:
在这里插入图片描述

(2)数值转字符串

转十进制可以用to_string,但是低版本的编译器可能不支持

转多进制可以用_itoa_s,但似乎没有安全机制,导致无法捕获异常

	char cstr[20];
	_itoa_s(100,cstr,2);
	cout << cstr << endl;

转多进制还可以用 stringstream

#include<string>
#include <iostream>
#include <sstream>
#include <bitset>
using namespace std;
int main()
{

   int num = 1234;
   stringstream stream;

   //转二进制
   stream << bitset<16>(num);
   cout << stream.str() <<endl;
   stream.str(""); //清空缓存,如果注释掉,那么会输出所有的历史结果
   //转8进制
   stream << oct << num;
   cout << stream.str() << endl;
   stream.str("");
   //转十六进制
   stream << hex << num;
   cout << stream.str() << endl;
   stream.str("");
   system("pause");
   return 0;
}

结果:
在这里插入图片描述

如果需要把格式化后的字符串通过>>输出到字符串, 必须每次都调用clear()方法

3.2.3 c++常用字符串函数

函数 功能
append 将字符添加到字符串的末尾
at 返回字符串中的指定位置处的元素的引用
c_str 将字符串的内容转换为 C 样式空终止字符串
data 转换为字符数组的字符串的内容
empty 测试是否该字符串包含的字符
erase 从指定位置字符串中移除元素或某个范围的元素
find 与指定的字符序列匹配的子字符串的第一个匹配项的向前搜索字符串
find_first_not_of 搜索与指定任何的字符串元素相匹配的第一个字符的字符串
find_first_of 搜索与指定任何的字符串元素相匹配的第一个字符的字符串
rfind 向后方向中的首次出现的与指定的字符序列匹配的子字符串搜索字符串
pop_back 清除该字符串的最后一个元素
push_back 该字符串的末尾添加一个元素
insert 将某个元素的数目或某个范围的元素插入到指定位置的字符串
length 返回字符串中的元素的当前数目
replace 替换指定的字符或从其他区域或字符串或 C 字符串复制的字符在字符串中位于指定位置处的元素
substr 从指定位置的字符串开头的复制的子字符串的最大字符数

3.2.4 原始字符串字面量

原始字符串字面量(raw string literal)是可以横跨多行代码的字符串字面量,不需要转义嵌入的双引号,像\t和\1!这种转义序列不按照转义序列的方式处理,而是按照普通文本的方式处理。如果像下面这样编写普通的字符串字面量,那么会收到一个编译器错误,因为 字符串包含了未转义的引号:

string str = "Hello "World"!// Error!

对于普通的字符串,必须转义双引号,如下所示:

string str = "Hello \"World\"!";

对于原始字符串字面量,就不需要转义引号了。
原始字符串字面量的写法:

string str = R"(Hello "World"!)";

原始字符串字面量可以跨越多行代码。例如,如果像下面这样编写普通的字符串字面量,那么会收到一个编译器错误,因为普通的字符串字面量不能跨越多行:

string str = "Line 1
Line 2 with \t"; // Error!

可使用如下原始字符串字面量来替代:

string str = R"(Line 1
Line 2 with \t)";

这也说明,使用原始字符串字面量时,\t转义字符没有替换为实际的制表符字符,而是按照字面形式保存。将str写入控制台得到的输出如下所示:
Line 1
Line 2 with \t
因为原始字符串字面量以)“结尾,所以使用这个语法时,不能在字符串中嵌入)”。例如,下面的 字符串是不合法的,因为在这个字符串中间包含了一个)”:

string str = R"(The characters )"n are embedded in this string)" // Error!

如果需要嵌入)",则需要使用扩展的原始字符串字面量语法,如下所示:

R"d-char-sequence(r-char-sequence)d-char-sequence"

r-char-sequence是实际的原始字符串。d-char-sequence是可选的分隔符序列,原始字符串首尾的分隔符序列应该一致。分隔符序列最多能有16个字符。应选择未出现在原始字符串字面量中的序列作为分隔符序列。上面的例子可以改用唯一的分隔符序列:

string str = R"-(The characters )" are embedded in this string)-";

在操作数据库查询字符串和正则表达式等字符串时,原始字符串字面量可以令程序的编写更加方便。

2017-04-20 17:51:02 achangett 阅读数 5410

C++中字符串输入处理

1. cin

cin是C++中最常用的输入语句,当遇到空格或者回车键即停止

如:

#include <iostream>

#include <string>

using namespace std;

int main()

{

         chara[50];

         cin>>a;    

         cout<<a<<endl;

         return0;

}

输入:abcd遇回车输出abcd

缺点:只能输入没有空格的字符串,当输入中含有空格,则只能输出空格之前的字符

输入:I  love  China输入空格时输入并未停止,遇回车输入停止,输出I,空格后面的均未输出。

2. gets()

可以无限读取,以回车结束读取,C语言中的函数,在C++中运行会产生bug。

如:

#include <iostream>

#include <cstdio>

using namespace std;

int main()

{

         chara[50];

         cin>>a;

         gets(a);

         cout<<a<<endl;

         return0;

}

输入:I love China回车结束输入,输出结果为love China。首字符自动丢弃。

3. getline()

若定义变量为string类型,则要考虑getline()函数。用法如下:

#include <iostream>

#include <string>

using namespace std;

int main()

{

         stringa;

         getline(cin,a);

         cout<<a<<endl;

         return0;

}

输入:I love China回车并未结束输入,需回车两次才能结束输入,输出结果为:I love China.

4.cin.get  cin.getline

cin.get()函数可以接收空格,遇回车结束输入。

#include <iostream>

using namespace std;

int main()

{

         chara[50];

         cin.get(a,50);

         cout<<a<<endl;

         return0;

}

输入:I love China回车结束输入,输出结果为I love China。

5. cin.getline

cin.getline()函数可以同cin.get()函数类似,也可接收空格,遇回车结束输入。

#include <iostream>

using namespace std;

int main()

{

         chara[50];

         cin.getline(a,50);

         cout<<a<<endl;

         return0;

}

输入:I love China回车结束输入,输出结果为I love China。

1.cin>>

用法1:最基本,也是最常用的用法,输入一个数字:

注意:>> 是会过滤掉不可见的字符(如 空格 回车,TAB 等)
 cin>>noskipws>>input[j];//不想略过空白字符,那就使用 noskipws 流控制

用法2:接受一个字符串,遇“空格”、“TAB”、“回车”都結束

复制代码
#include <iostream>
using namespace std;
main ()
{
char a[20];
cin>>a;
cout<<a<<endl;
}
复制代码

输入:jkljkljkl
输出:jkljkljkl

输入:jkljkl jkljkl       //遇空格結束
输出:jkljkl

2、cin.get()

用法1: cin.get(字符变量名)可以用来接收字符

#include <iostream>
using namespace std;
main ()
{
char ch;
ch=cin.get();               //或者cin.get(ch);
cout<<ch<<endl;
}

输入:jljkljkl
输出:j

用法2:cin.get(字符数組名,接收字符数目)用來接收一行字符串,可以接收空格

#include <iostream>
using namespace std;
main ()
{
char a[20];
cin.get(a,20);
cout<<a<<endl;
}

输入:jkl jkl jkl
输出:jkl jkl jkl

输入:abcdeabcdeabcdeabcdeabcde (输入25个字符)
输出:abcdeabcdeabcdeabcd              (接收19个字符+1个’\0’)

用法3:cin.get(无参數)沒有参數主要是用于舍弃輸入流中的不需要的字符,或者舍弃回車,弥补cin.get(字符数組名,接收字符数目)的不足.

這个我还不知道怎么用

3、cin.getline()   // 接受一個字符串,可以接收空格并输出

#include <iostream>
using namespace std;
main ()
{
char m[20];
cin.getline(m,5);
cout<<m<<endl;
}

输入:jkljkljkl
输出:jklj

接受5個字符到m中,其中最後一个为\0’,所以只看到4個字符輸出;

如果把5改成20:
输入:jkljkljkl
输出:jkljkljkl

输入:jklf fjlsjf fjsdklf
输出:jklf fjlsjf fjsdklf

//延伸:
//cin.getline()实际上有三个参数,cin.getline(接受字符串是m,接受个数5,结束字符)
//当第三个参数省略时,系統默认为’\0’
//如果将例子中cin.getline()改为cin.getline(m,5,’a’);当输入jlkjkljkl時输出jklj,输入jkaljkljkl时,输出jk

当用在多维数组中的時候,也可以用cin.getline(m[i],20)之类的用法:

#include<iostream>
#include<string>
using namespace std;

main ()
{
char m[3][20];
for(int i=0;i<3;i++)
{
cout<<”\n请输入第”<<i+1<<”个字符串:”<<endl;
cin.getline(m[i],20);
}

cout<<endl;
for(int j=0;j<3;j++)
cout<<”输出m[“<<j<<”]的值:”<<m[j]<<endl;

}

请输入第1個字符串:
kskr1

请输入第2個字符串:
kskr2

请输入第3個字符串:
kskr3

输出m[0]的值:kskr1
输出m[1]的值:kskr2
輸出m[2]的值:kskr3

4、getline()     // 接受一個字符串,可以接收空格并输出,需包含“#include<string>”

#include<iostream>
#include<string>
using namespace std;
main ()
{
string str;
getline(cin,str);
cout<<str<<endl;
}

輸入:jkljkljkl
輸出:jkljkljkl

輸入:jkl jfksldfj jklsjfl
輸出:jkl jfksldfj jklsjfl

和cin.getline()類似,但是cin.getline()属于istream流,而getline()属于string流,是不一样的兩個函數

註意:在使用getline時有一個奇怪的現象,在vc6下, 要打两次回車才可以結束

5、gets()        // 接受一個字符串,可以接收空格并輸出,需包含“#include<string>”

#include<iostream>
#include<string>
using namespace std;
main ()
{
char m[20];
gets(m);                       //不能寫成m=gets();
cout<<m<<endl;
}

输入:jkljkljkl
输出:jkljkljkl

输入:jkl jkl jkl
输出:jkl jkl jkl

类似cin.getline()里面的一個例子,gets()同样可以用在多多维数组里面:

#include<iostream>
#include<string>
using namespace std;

main ()
{
char m[3][20];
for(int i=0;i<3;i++)
{
cout<<”\n请输入第”<<i+1<<”個字符串:”<<endl;
gets(m[i]);
}

cout<<endl;
for(int j=0;j<3;j++)
cout<<”輸出m[“<<j<<”]的值:”<<m[j]<<endl;

}

请输入第1個字符串:
kskr1

请输入第2個字符串:
kskr2

请输入第3個字符串:
kskr3

输出m[0]的值:kskr1
输出m[1]的值:kskr2
输出m[2]的值:kskr3

感觉gets()和cin.getline()的用法很类似,只不過cin.getline()多一個参数罢了;

这里顺带说明一下,對於本文中的這個kskr1,kskr2,kskr3的例子,对于cin>>也可以这样用,原因是这里輸入的沒有空格,如果输入了空格,比如“ks kr jkl[回車]”那麽cin就会已经接收到3個字符串,“ks,kr,jkl”;再如“kskr 1[回車]kskr 2[回車]”,那么則接收“kskr,1,kskr”;這不是我們所要的結果!而cin.getline()和gets()因為可以接收空格,所以不会产生这个错误;

6、getchar()   //接受一個字符,需包含“#include<string>”

#include<iostream>
#include<string>
using namespace std;
main ()
{
char ch;
ch=getchar();                        //不能写成getchar(ch);
cout<<ch<<endl;
}

输入:jkljkljkl
输出:j

//getchar()是C語言的函數,C++也可以兼容,但是尽量不用或少用;(转)

2019-07-09 11:52:51 qq_41510854 阅读数 133028

C++语言提供了比C语言更丰富的字符串处理功能。它可以在字符串上经行输入,输出,合并,修改,比较,转换,复制,搜索等操作。使用这些现成的功能可以大大减少我们的编程的负担。
输入和输出的字符串函数,如printfputscoutscanfgets,cout等,在使用时应包含头文件cstdio,并使用其他字符串函数包含头文件cstring
cstring是一个专门用于处理字符串的头文件。它包含许多字符串处理函数。由于篇幅限制,本节只能解释一些常见的内容。

字符串连接函数 strcat()

strcat 就是 string catenate 的缩写,意思为把两个字符串拼在一起,其格式为:

strcat(Str1, Str2);

Str1、Str2 为需要拼接的字符串。

strcat() 将把 Str2 连接到 Str1 后面,并删除原来 Str1 最后的结束标志\0这意味着,Str1 必须足够长,要能够同时容纳 Str1 和 Str2,否则字符数组会越界(超出字符串范围)。

strcat() 的返回值为 Str1 的地址。

这是一个简单的演示:

#include <cstdio>
#include <cstring>
int main(){
    char str1[100]="The URL is ";
    char str2[60];
    cout<<"Input a URL: ";
    gets(str2);
    strcat(str1, str2);
    puts(str1);
    return 0;
}

运行结果:
Input a URL: https://blog.yunhei.org/index.php/archives/13/↙(输入)
The URL is https://blog.yunhei.org/index.php/archives/13/

字符串复制函数 strcpy()

strcpy 是 string copy 的缩写,意思是字符串复制,也即将字符串从一个地方复制到另外一个地方,语法格式为:

strcat(Str1, Str2);

strcpy() 会把 Str2 中的字符串拷贝到 Str1 中,字符串结束标志\0也一同复制。下面是一个简单的演示:

#include <cstdio>
#include <cstring>
int main(){
    char str1[50] = "云黑系统";
    char str2[50] = "https://yunhei.org/";
    strcpy(str1, str2);
    printf("str1: %s\n", str1);

    return 0;
}

运行结果:
str1: https://yunhei.org/

你看,将 str2 复制到 str1 后,str1 中原来的内容就被覆盖了。

另外,strcpy() 要求 Str1 要有足够长的长度,否则不能全部装入所复制的字符串。

字符串比较函数 strcmp()

strcmp 是 string compare 的缩写,表示字符串比较。语法是:

strcmp(Str1, Str2);

Str1 和 Str2 是两个需要比较的字符串。
字符本身没有大小,strcmp()比较字符的ASCII值。
strcmp()开始比较两个字符串的第0个字符。如果它们相等,它们会继续比较下一个字符,直到它们遇到不同的字符或字符串的末尾。

返回值:如果 Str1 和 Str2 相同,则返回0;如果 Str1 大于 Str2,则返回大于 0 的值;若 Str1 小于 Str2,则返回小于0 的值。

比较4组字符串:

#include <cstdio>
#include <cstring>
int main(){
    char str1[] = "aBcDeF";
    char str2[] = "AbCdEf";
    char str3[] = "aacdef";
    char str4[] = "aBcDeF";
    printf("a VS b: %d\n", strcmp(a, b));
    printf("a VS c: %d\n", strcmp(a, c));
    printf("a VS d: %d\n", strcmp(a, d));

    return 0;
}

运行结果:
str1 VS str2: 32
str1 VS str3: -31
str1 VS str4: 0

PS:来源云黑博客

2018-03-03 19:28:22 ksws0292756 阅读数 58282

在C++中,有两种类型的字符串表示形式:

  • C-风格字符串
  • C++引入的string类

C-风格字符串

C 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持。字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。
下面的声明和初始化创建了一个 “Hello” 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 “Hello” 的字符数多一个。

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

其实,您不需要把 null 字符放在字符串常量的末尾。C++ 编译器会在初始化数组时,自动把 ‘\0’ 放在字符串的末尾。所以也可以利用下面的形式进行初始化

char greeting[] = "Hello";

以下是 C/C++ 中定义的字符串的内存表示:
这里写图片描述
C++ 中有大量的函数用来操作以 null 结尾的字符串:

序号 函数 功能
1 strcpy(s1,s2) 复制字符串 s2 到字符串 s1
2 strcat(s1,s2) 连接字符串 s2 到字符串 s1 的末尾
3 strlen(s1) 返回字符串 s1 的长度
4 strcmp(s1,s2) 返回s1与s2的比较结果
5 strchr(s1,ch) 返回一个指针,指向字符串s1中字符ch的第一次出现的位置
6 strstr(s1,s2) 返回一个指针,指向字符串s1中s2的第一次出现的位置

C++ 中的 String 类

C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。比如:

  • append() – 在字符串的末尾添加字符
  • find() – 在字符串中查找字符串
  • insert() – 插入字符
  • length() – 返回字符串的长度
  • replace() – 替换字符串
  • substr() – 返回某个子字符串

4种字符串类型

C++中的字符串一般有以下四种类型,

  • string

  • char*

  • const char*
  • char[]

下面分别做简单介绍,并说明其中的一些区别

string

string是一个C++类库中的一个类,它位于名称空间std中,因此必须使用using编译指令或者std::string来引用它。它包含了对字符串的各种常用操作,它较char*的优势是内容可以动态拓展,以及对字符串操作的方便快捷,用+号进行字符串的连接是最常用的操作

char*

char* 是指向字符串的指针(其实严格来说,它是指向字符串的首个字母),你可以让它指向一串常量字符串。

const char*

该声明指出,指针指向的是一个const char类型,即不能通过当前的指针对字符串的内容作出修改

注意这里有两个概念:

  • char * const [指向字符的静态指针]
  • const char * [指向静态字符的指针]

前者const修饰的是指针,代表不能改变指针
后者const修饰的是char,代表字符不能改变,但是指针可以变,也就是说该指针可以指针其他的const char。

char[]

与char*与许多相同点,代表字符数组,可以对应一个字符串,如

char * a="string1";
char b[]="string2";

这里a是一个指向char变量的指针,b则是一个char数组(字符数组)
也就是说:

二者的不同点

一,char*是变量,值可以改变, char[]是常量,值不能改变!
a是一个char型指针变量,其值(指向)可以改变;
b是一个char型数组的名字,也是该数组首元素的地址,是常量,其值不可以改变

二,char[]对应的内存区域总是可写,char*指向的区域有时可写,有时只读
比如:

char * a="string1";
char b[]="string2";
gets(a); //试图将读入的字符串保存到a指向的区域,运行崩溃! 
gets(b) //OK

解释: a指向的是一个字符串常量,即指向的内存区域只读;
b始终指向他所代表的数组在内存中的位置,始终可写!

注意,若改成这样gets(a)就合法了:

char * a="string1";
char b[]="string2";
a=b; //a,b指向同一个区域
gets(a) //OK
printf("%s",b) //会出现gets(a)时输入的结果

解释: a的值变成了是字符数组首地址,即&b[0],该地址指向的区域是char *或者说 char[8],习惯上称该类型为字符数组,其实也可以称之为“字符串变量”,区域可读可写。

总结:char *本身是一个字符指针变量,但是它既可以指向字符串常量,又可以指向字符串变量,指向的类型决定了对应的字符串能不能改变!

三,char * 和char[]的初始化操作有着根本区别:
测试代码:

char *a="Hello World"; 
char b[]="Hello World"; 
printf("%s, %d\n","Hello World", "Hello World"); 
printf("%s, %d %d\n", a, a,  &a);                           
printf("%s, %d %d\n", b,     b,  &b);

结果:

Hello World,13457308
Hello World,13457308    2030316
Hello World,2030316 2030316

结果可见:尽管都对应了相同的字符串,但”Hellow World”的地址 和 a对应的地址相同,与b指向的地址有较大差异;&a 、&b都是在同一内存区域,且&b==b
根据c内存区域划分知识,我们知道,局部变量都创建在栈区,而常量都创建在文字常量区,显然,a、b都是栈区的变量,但是a指向了常量(字符串常量),b则指向了变量(字符数组),指向了自己(&b==b==&b[0])。
说明以下问题:
char * a=”string1”;是实现了3个操作:

  1. 声明一个char*变量(也就是声明了一个指向char的指针变量);
  2. 在内存中的文字常量区中开辟了一个空间存储字符串常量”string1”
  3. 返回这个区域的地址,作为值,赋给这个字符指针变量a

最终的结果:指针变量a指向了这一个字符串常量“string1”
(注意,如果这时候我们再执行:char * c=”string1”;则,c==a,实际上,只会执行上述步骤的1和3,因为这个常量已经在内存中创建)

char b[]=”string2”;则是实现了2个操作:

  1. 声明一个char 的数组,
  2. 为该数组“赋值”,即将”string2”的每一个字符分别赋值给数组的每一个元素

最终的结果:“数组的值”(注意不是b的值)等于”string2”,而不是b指向一个字符串常量

实际上, char * a=”string1”; 的写法是不规范的!
因为a指向了即字符常量,一旦strcpy(a,”string2”)就糟糕了,试图向只读的内存区域写入,程序会崩溃的!尽管VS下的编译器不会警告,但如果你使用了语法严谨的Linux下的C编译器GCC,或者在windows下使用MinGW编译器就会得到警告。
所以,我们还是应当按照”类型相同赋值”的原则来写代码:

const char * a="string1";

保证意外赋值语句不会通过编译

另外,关于char*和char[]在函数参数中还有一个特殊之处,运行下面的代码

void fun1 ( char *p1,  char p2[] ) {
 printf("%s %d %d\n",p1,p1,&p1);
 printf("%s %d %d\n",p2,p2,&p2);
p2="asdf"; //通过! 说明p2不是常量! 
printf("%s %d %d\n",p2,p2,&p2);
}
void main(){
char a[]="Hello";
fun1(a,a);
}

运行结果:

Hello 3471628 3471332
Hello 3471628 3471336
asdf 10704764 3471336

结果出乎意料!上面结果表明p2这时候根本就是一个指针变量!
结论是:作为函数的形式参数,两种写法完全等效的!都是指针变量!

const char*与char[]的区别:
const char * a=”string1”
char b[]=”string2”;
二者的区别在于:

  1. a是const char 类型, b是char const类型
    ( 或者理解为 (const char)xx 和 char (const xx) )

  2. a是一个指针变量,a的值(指向)是可以改变的,但a只能指向(字符串)常量,指向的区域的内容不可改变;

  3. b是一个指针常量,b的值(指向)不能变;但b指向的目标(数组b在内存中的区域)的内容是可变的

  4. 作为函数的声明的参数的时候,char []是被当做char *来处理的!两种形参声明写法完全等效!

字符串类型之间的转换: string、const char*、 char* 、char[]相互转换

一、转换表格

源格式->目标格式 string char* const char* char[]
string NULL 直接赋值 直接赋值 直接赋值
char* strcpy NULL const_cast char*=char
const char* c_str() 直接赋值 NULL const char*=char;
char[] copy() strncpy_s() strncpy_s() NULL

二、总结方法:

  1. 变成string,直接赋值。
  2. char[]变成别的,直接赋值。
  3. char*变constchar*容易,const char*变char*麻烦。<const_cast><char*>(constchar*);
  4. string变char*要通过const char*中转。
  5. 变成char[]。string逐个赋值,char* const char* strncpy_s()。

三,代码示例

1、string转为其他类型

①、string转const char*

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    std::string str = "HelloWorld!";     //初始化string类型,并具体赋值
    const char* constc = nullptr;         //初始化const char*类型,并赋值为空
    constc= str.c_str();                 //string类型转const char*类型
    printf_s("%s\n", str.c_str());        //打印string类型数据 .c_str()
    printf_s("%s\n", constc);             //打印const char*类型数据
    return 0;
}

②、string转char*

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    std::string str = "HelloWorld!";     //初始化string类型,并具体赋值
    char* c = nullptr;                    //初始化char*类型,并赋值为空
    const char* constc = nullptr;         //初始化const char*类型,并赋值为空
    constc= str.c_str();                 //string类型转const char*类型
    c= const_cast<char*>(constc);        //const char*类型转char*类型
    printf_s("%s\n", str.c_str());        //打印string类型数据 .c_str()
    printf_s("%s\n",c);                  //打印char*类型数据
    return 0;

}

③、string转char[]

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    std::string str = "HelloWorld!";      //初始化string类型,并具体赋值
    char arrc[20] = {0};                   //初始化char[]类型,并赋值为空
    for (int i = 0; i < str.length(); i++) //string类型转char[]类型
    {
        arrc[i]=str[i];
    }
    printf_s("%s\n", str.c_str());         //打印string类型数据 .c_str()
    printf_s("%s\n", arrc);                //打印char[]类型数据
    return 0;
}

2、const char*转为其他类型

①const char*转string

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    const char* constc = "Hello World!";     //初始化const char* 类型,并具体赋值
    std::string str;                        //初始化string类型
    str= constc;                            //const char*类型转string类型
    printf_s("%s\n", constc);                //打印const char* 类型数据
    printf_s("%s\n", str.c_str());           //打印string类型数据
    return 0;
}

②const char*转char*

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    const char* constc = "Hello World!";     //初始化const char* 类型,并具体赋值
    char* c = nullptr;                       //初始化char*类型
    c= const_cast<char*>(constc);           //const char*类型转char*类型
    printf_s("%s\n", constc);                //打印const char* 类型数据
    printf_s("%s\n", c);                     //打印char*类型数据
    return 0;
}

③const char*转char[]

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    const char* constc = "Hello World!";     //初始化const char* 类型,并具体赋值
    char arrc[20] = { 0 };                   //初始化char[]类型,并赋值为空
    strncpy_s(arrc,constc,20);              //const char*类型转char[]类型
    printf_s("%s\n", constc);                //打印const char* 类型数据
    printf_s("%s\n", arrc);                  //打印char[]类型数据
    return 0;
}

3、char*转为其他类型

①char*转string

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    char* c = "HelloWorld!";           //初始化char* 类型,并具体赋值
    std::string str;                   //初始化string类型
    str= c;                            //char*类型转string类型
    printf_s("%s\n", c);                //打印char* 类型数据
    printf_s("%s\n", str.c_str());      //打印string类型数据
    return 0;
}

②char*转const char*

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    char* c = "HelloWorld!";         //初始化char* 类型,并具体赋值
    const char* constc = nullptr;     //初始化const char* 类型,并具体赋值
    constc= c;                       //char*类型转const char* 类型
    printf_s("%s\n", c);              //打印char* 类型数据
    printf_s("%s\n", constc);         //打印const char* 类型数据
    return 0;
}

③char*转char[]

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    char* c = "HelloWorld!";         //初始化char* 类型,并具体赋值
    char arrc[20] = { 0 };           //初始化char[] 类型,并具体赋值
    strncpy_s(arrc,c,20);             //char*类型转char[] 类型
    printf_s("%s\n", c);              //打印char* 类型数据
    printf_s("%s\n", arrc);           //打印char[]类型数据
    return 0;
}

4、char[]转为其他类型

#include "stdafx.h"
#include <iostream>
int _tmain(intargc, _TCHAR* argv[])
{
    char arrc[20] = "HelloWorld!";//初始化char[] 类型并具体赋值
    std::string str;                 //初始化string
    const char* constc = nullptr;   //初始化const char*
    char*c = nullptr;                //初始化char*
    str= arrc;                     //char[]类型转string类型
    constc= arrc;             //char[]类型转const char* 类型
    c= arrc;                        //char[]类型转char*类型
    printf_s("%s\n", arrc);         //打印char[]类型数据
    printf_s("%s\n", str.c_str());  //打印string类型数据
    printf_s("%s\n", constc);       //打印const char* 类型数据
    printf_s("%s\n", c);            //打印char*类型数据
    return 0;
}

C,C++字符串总结

阅读数 2936

C++字符串操作详解

阅读数 45368

C++字符串数组

阅读数 11448