BY ziyang
0 字符与整数的联系 ——ASCLL码
0.1 ASCLL需要记住的几个点
- 数字0~9对应48~57
- 26个英文字母
- 65为A
- 97为a
- 大小写之前差32(不知道与32进制是否有关系,哈哈)
0.2 字符与数字之间怎么进行转化?
注意:字符要用”进行分隔!!
- 强制类型转化
#include <iostream>
using namespace std;
int main (void)
{
char c = 'a';
cout << (int)c << endl;
cout << (char)97 << endl;
return 0;
}
//输出
//a
//97
//
附,利用此原理手搓的ASCLL码表:
由于有些符号电脑不能显示,故出现下图样式
#include <stdio.h>
int main (void)
{
int jus = 0;
for (int i = 0 ; i < 128 ; i ++)
{
jus ++;
if(jus % 8 == 0 && jus != 0){
printf("\n");
}
printf("%d = %c\t\t",i,(char)i);
}
return 0;
}
- 字符间的运算:
会把字符自动转化为数字,再进行数字间的运算,例如:
#include <iostream>
using namespace std;
int main (void)
{
printf("%d",'a'-'b');
printf("%c",'a'+1);//此项输出b
return 0;
}
//因为ASCLL表中“a”对应97,“b”对应98,再加上字符间运算字符与数字的转化,因为最后输出结果为-1
//加法同理
可以这样去理解——>char本身是储存一个整数,但是在输出的时候,对应占位符输出
1 字符数组
1.1 基本定义
字符串就是字符数组结尾加上一个空字符”\0″。
新概念:
空字符——”\0“我们可以用字符串来初始化字符数组,但是值得注意的是,每个字符串数组结尾都会暗含一个”\0″,也就意味着此时字符数组长度比“明面上的”字符长度要多1。
附:字符是用”引起来,而字符串要用“”引起来
例如:
char string [] = 'Hello World'
”Hello World“一共是11个字符。
用其初始化char string []之后,得到的字符串的长度为11个字节。
但是char string []的数组长度【不知道专业名是否叫长度】是12 = 11 + 1。
1.1.1字符串字面量
用双括号括起来的内容被称为字符串字面量,也叫做字符串常量,会自动添加“/0”
注意:
在字符串字面量之前如果没有间隔,或者用空白字符分隔,C语言会视为串联起来的字符串变量
1.2 字符数组的初始化
几种不同的初始化:
#include <stdio.h>
int main (void)
{
//列表/数组初始化,没有空字符
//不是字符串,因此无法使用字符串相关的函数进行运算(其实是可以用的,只是有些函数输出结果会出现问题,例如之前出现的结尾多输出一个特殊字符)
char a1[] = {'C','+','+'};
//列表初始化,含有显示的空字符
//能精确控制字符数组中每一个数组空间内的字符
char a2[] = {'C','+','+','\0'};
//自动添加表示字符串结尾的空字符
char a3[] = "C++";
char a4[5] = "ziyang";
return 0;
}
小思考:
下面是含有问题的图片:
是的
附:二维字符数组的初始化
在C语言中,二维字符数组通常用于存储字符串或字符矩阵。二维字符数组的初始化可以在声明时完成,也可以在声明后通过循环或显式赋值完成。以下是一些初始化二维字符数组的例子:
静态初始化(在声明时初始化)
在声明二维字符数组时,可以直接在大括号内初始化数组的元素。每个子数组(即每一行)的元素也应该用大括号括起来。
// 初始化一个3x3的二维字符数组
char arr[3][3] = {
{'a', 'b', 'c'}, // 第一行
{'d', 'e', 'f'}, // 第二行
{'g', 'h', 'i'} // 第三行
};
// 初始化一个包含字符串的二维字符数组
char str_arr[2][5] = {
"Kimi", // 第一行,自动添加字符串结束符 '\0'
"AI" // 第二行,自动添加字符串结束符 '\0'
};
动态初始化(声明后初始化)
如果你需要在声明后初始化二维字符数组,可以使用循环或显式赋值。
// 声明一个3x3的二维字符数组
char arr[3][3];
// 使用循环动态初始化
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
arr[i][j] = 'a' + i + j; // 举例:按某种规则赋值
}
}
// 使用显式赋值动态初始化
arr[0][0] = 'a';
arr[0][1] = 'b';
arr[0][2] = 'c';
arr[1][0] = 'd';
arr[1][1] = 'e';
arr[1][2] = 'f';
arr[2][0] = 'g';
arr[2][1] = 'h';
arr[2][2] = 'i';
注意事项
- 在静态初始化时,如果某个子数组的元素少于声明的列数,编译器会自动用空字符(
\0
)填充剩余的位置。 - 如果你初始化的字符串长度超过了数组的列数,编译器会截断多余的部分。
- 在C语言中,字符串以空字符(
\0
)结尾,所以在初始化包含字符串的二维字符数组时,不需要显式添加空字符,编译器会自动处理。 - 在动态初始化时,如果数组的某一行没有完全初始化,剩余的位置会被初始化为垃圾值(未定义的值),所以通常需要显式地初始化每一行的每一个元素。
二维字符数组在C语言中常用于处理文本数据,如字符串数组或简单的文本矩阵。
2 字符数组的使用
2.1 字符数组的输入输出
#include <iostream>
using namespace std;
int main (void)
{
char str[100];
//输入字符串时,遇到空格或者回车就会停止(停止一次输入)
cin >> str;
//输出字符串时,遇到空格或者回车不会停止
cout << str << endl;//C++的输出
printf("%s\n",str);//C的输出
return 0;
}
注意:
- 什么时候字符数组的输入需要加&?
附:puts函数
#include <stdio.h>
int main (void)
{
char s[100];
//输入一个字符储存在s[1]变量中
//要加取地址符&的原因是s[1]是一个变量(不是一个地址)
scanf("%s",&s[1]);
//用数组的形式定义一个字符串的话,输入scnaf中就不用&符号
//因为s本身就是一个地址——>指针和数组以及地址的关系
scanf("%s",s);//输入一个字符串
scanf("%s",s+1);//想从下标n开始读入,就输入"s+n"
puts(s);
//等价于scanf("%s\n",s)包括换行符
return 0;
}
2.2 下标改变的输入:
同理,对指定下标的输出:
2.3 字符串整行输入
输入字符串时,遇到空格或者回车就会停止(停止一次输入)
例如:
#include <stdio.h>
int main (void)
{
char s[100];
scanf("%s",s);//因为s为数组,就不用&
printf("%s",s);
//如果输入 abc bcd
//则输出abc
return 0;
}
- **输入字符串时,遇到空格或者回车就会停止(停止一次输入),那么怎么才能连续读入一行字符呢?【即遇到空格不停止】
附:scanf
的返回值
、在C语言中,scanf
函数用于从标准输入(通常是键盘)读取格式化输入。scanf
函数的返回值是成功读取的输入项的数量。如果输入不符合预期的格式,scanf
将停止读取并返回已经成功读取的项数。
例如,如果你使用 %d
来读取一个整数,而用户输入了一个字母,scanf
将不会读取该字母,并且返回0,因为它没有成功读取任何整数。
scanf
函数的原型如下:
int scanf(const char *format, ...);
这里的 format
是一个格式字符串,它指定了期望输入的格式。其余的参数是指向变量的指针,这些变量将存储输入的值。
返回值的具体含义如下:
- 如果成功读取至少一个项,返回读取的项数。
- 如果没有成功读取任何项(例如,因为输入不符合格式),返回
EOF
(通常定义为 -1)。 - 如果发生读取错误(例如,由于输入/输出问题),返回一个负值。
使用 scanf
时,通常需要检查其返回值,以确保输入是有效的。例如:
int number;
if (scanf("%d", &number) == 1) {
// 成功读取了一个整数
} else {
// 输入不符合预期格式或发生错误
}
在实际编程中,为了更安全地处理输入,推荐使用 fgets
函数配合 sscanf
函数,或者使用 fgets
函数配合字符串解析函数,这样可以更好地控制输入过程,并避免缓冲区溢出等安全问题。
在c++中的读入函数:
- gets函数【C中可用】
#include <cstdio>
#include <iostream>
using namespace std;
int main (void)
{
char s[100];
gets(s);
printf("%c\n",s+1);//C语言输出
cout << s + 2 << endl;//C++语言输出
return 0;
}
注意:
从C11标准开始,gets函数已经从C语言中移除,原因是它允许输入超过缓冲区大小的字符串,这会导致缓冲区溢出,可能引起程序崩溃或安全漏洞
但是部分编译器(如Devc++)没有严格遵守C11标准,因此允许gets函数的使用
- fgets函数【C中可用】
注意:
fgets 函数会读入输入流首位/末尾的回车符号
因此使用fgets函数之前需要特别注意是否会读入回车符
#include <cstdio>
#include <iostream>
using namespace std;
int main (void)
{
char s[100];
fgets(s,100,stdin);
printf("%c\n",s+1);//C语言输出
cout << s + 2 << endl;//C++语言输出
return 0;
}
注意:
在fgets(s,100,stdin);中s表示读入字符串写入的目标数组,100表示最多读取的字符串长度,stdin表示从哪个文件中读入,一般都是从文件stdin中读入(stdin是系统中的一个变量,已经被定义好了)
附:
使用fgets
函数读入字符串后计算长度的时候,记得判断并取消末尾读入的\n
fgets(input, 1001, stdin); // 获取输入
int len = strlen(input);
if (input[len - 1] == '\n')
{
input[len - 1] = '\0'; // 去掉换行符
}
- cin.getline函数【读入数组】
#include <cstdio>
#include <iostream>
using namespace std;
int main (void)
{
char s[100];
cin.getline(s,100);
printf("%c\n",s+1);//C语言输出
cout << s + 2 << endl;//C++语言输出
return 0;
}
- getline函数【读入string】
#include <cstdio>
#include <iostream>
using namespace std;
int main (void)
{
string s;
getline(cin,s);
printf("%c\n",s);//C语言输出
cout << s << endl;//C++语言输出
return 0;
}
//string的输入和输出就不能用
//cin >> s + 1;
注意:
getline(cin,s)中固定第一个为cin第二个传递的为string数组
2.4 遍历字符串数组中的字符
同理数组的遍历输出:
重点——循环结束的条件,引用strlen()函数
#include <cstdio>
#include <string.h>
#include <iostream>
using namespace std;
int main (void)
{
char s1[100];
cin >> s1;
for (int i = 0 ; i < strlen(s1) ; i ++) cout << s1[i] << endl;
//修改:
//int const len = strlen
//for (int i = 0 ; i < len ; i ++) cout << s1[i] << endl;
//或者更加简洁一点:
//for (int i = 0,int len = strlen(s1) ; i < len ; i ++) cout << s1[i] << endl;
return 0;
}
但是由于strlen()函数在条件判断里面,所以会循环n次,这样程序运行的时间就会增加,效率就会很低
修改:
~~~ C++
#include <cstdio>
#include <string.h>
#include <iostream>
using namespace std;
int main (void)
{
char s1[100];
cin >> s1;
//修改:
int const len = strlen
for (int i = 0 ; i < len ; i ++) cout << s1[i] << endl;
return 0;
}
或者更简洁一点:
~~~ C++
#include <cstdio>
#include <string.h>
#include <iostream>
using namespace std;
int main (void)
{
char s1[100];
cin >> s1;
//修改:
for (int i = 0,int len = strlen(s1) ; i < len ; i ++) cout << s1[i] << endl;
return 0;
}
3字符串数组
字符串数组分为两类:
- 指针 * 数组
指针数组很好理解,每一个数组空间里面都储存着一个字符串 - 数组 * 数组
相当于一个二维字符串数组,每一“维”都对应一个字符数组
二者的区别:
同字符串数组的区别
4 相对常见的几个字符串函数
4.1 strlen(s)函数
//作用,求字符串的长度(不包含“ \n
”)
#include <cstdio>
#include <string.h>
#include <iostream>
using namespace std;
int main (void)
{
char s1[100];
char s2[100];
scanf("%s",s1);
cin >> s2;
cout << strlen(s1) << endl;
printf("%d",strlen(s2));
return 0;
}
//输入:hello
//HELLO
//输出:5
//5
strlen(a,b)等价函数:
#include <cstdio>
#include <string.h>
#include <iostream>
using namespace std;
int main (void)
{
char s1[100],s2[100];
cin >> s1;
int len = 0;
//等价函数
for(int i = 0 ; s1[i] ; i ++) len ++;
//只要字符数组里面有符号,则进行循环,len++。由于“\0”不会被读到,所以最后输出的结果不包含“\0”的长度
cout << len << endl;
return 0;
}
链接:程序编译集锦P11
附加
这段代码使用 fgets
函数从标准输入(stdin
)读取最多11个字符(第12个位置留给空字符 \0
)到字符数组 ch
中,并计算字符串的长度。但是,当你输入 “a” 并按下回车键时,fgets
会将回车符(换行符)也读入到数组中。因此,如果你输入的是单个字符 “a” 后紧接着按下回车键,数组 ch
将包含以下字符:
'a', '\r', '\n', '\0'
这里:
'a'
是你输入的字符。'\r'
是回车符,如果你在Windows系统上运行程序,它可能会被包含。'\n'
是换行符,这是大多数Unix/Linux系统上的行结束符。'\0'
是字符串结束的空字符。
strlen
函数计算从字符串开始到第一个空字符之间的字符数量。在这个例子中,strlen
将计算 'a'
和 '\0'
之间的字符数量,但不包括回车符和换行符,因为它们位于空字符之前。
因此,如果你的系统在输入时包含回车符,那么 strlen
的返回值将是2,因为 'a'
和 '\0'
之间有一个回车符。
如果你希望 strlen
只计算实际输入的字符而不包括回车符和换行符,你可以在调用 strlen
之前手动移除这些字符。例如:
#include<stdio.h>
#include<string.h>
int main()
{
char ch[12];
fgets(ch, 12, stdin);
int len = strlen(ch);
// 移除字符串末尾的回车符和换行符
if (len > 0 && ch[len - 1] == '\n') {
ch[len - 1] = '\0';
len--;
}
if (len > 0 && ch[len - 1] == '\r') {
ch[len - 1] = '\0';
len--;
}
printf("%d", len);
return 0;
}
这段代码在计算长度之前检查并移除字符串末尾的回车符和换行符,这样 strlen
就会返回实际输入的字符数量。
4.2 strcmp(a,b)函数
比较两个字符串的大小(按字典序比较)。a > b 返回-1,a < b 返回1,a = b 返回0;
注意:是后面的与前面的进行(字符序)比较
附:字典序与贪心排序?
AI解释:
4.3 strcpy(a,b)函数
这个记错了吗?怎么回事???
把后面的字符串 b 复制给前面指定位置的 astrcpy(s + len - 3, temp);
比如这一句,就是将temp字符数组从s[s + len - 3]
位置开始复制到s数组中
复制的时候要注意空间是否重叠,如果是,则不能进行复制
如:(0表示没有输入)
a[6] = abc00;
b[6] = ab00a;
这样就不能进行copy
后面得修改一下语言,感觉描述语言有问题
#include <cstdio>
#include <string.h>
#include <iostream>
using namespace std;
int main (void)
{
char s1[100] = "abc000";
char s2[100] = "ab000a";
for(int k = 0 ; k < 6 ; k ++)
{
cout << s1[k] << endl;
}//检查数组
cout << endl;
for(int k = 0 ; k < 6 ; k ++)
{
cout << s2[k] << endl;
}//检查数组
cout << endl;
strcpy(s1,s2);//交换字符数组
for(int k = 0 ; k < 6 ; k ++)
{
cout << s1[k] << endl;
}
//检查交换
return 0;
}
4.31 strncpy();函数
- 函数定义和功能
strncpy
函数是 C 语言中的一个字符串处理函数,用于将一个字符串的一部分复制到另一个字符串中。它的函数原型定义在<string.h>
头文件中,形式如下:char* strncpy(char* destination, const char* source, size_t num);
。- 其主要功能是把
source
指向的字符串中最多num
个字符复制到destination
指向的数组中。
- 参数说明
destination
:这是目标字符串,是一个字符数组,用于存储复制后的内容。它应该有足够的空间来容纳复制的字符以及可能的字符串结束符'\0'
。source
:这是源字符串,是一个常量字符数组(由const
修饰,表示内容不被函数修改),是要被复制的字符串。num
:是一个无符号整数类型(size_t
)的参数,表示最多要从source
复制到destination
的字符个数。
- 返回值
strncpy
函数返回目标字符串destination
的指针,这使得它可以在一个表达式中被链式调用或者方便地进行后续操作。
- 示例代码
- 以下是一个简单的示例,展示
strncpy
函数的基本用法:
#include <stdio.h> #include <string.h> int main() { char source[] = "Hello, World!"; char destination[20]; strncpy(destination, source, 5); destination[5]='\0'; // 手动添加字符串结束符 printf("%s\n", destination); return 0; }
- 在这个示例中,
source
字符串包含"Hello, World!"
。strncpy
函数将source
的前 5 个字符复制到destination
中。由于strncpy
函数不会自动在destination
末尾添加'\0'
(除非source
中的字符数小于num
),所以需要手动添加字符串结束符'\0'
,这样才能正确地使用printf
函数输出destination
中的字符串,程序将输出Hello
。
- 以下是一个简单的示例,展示
- 注意事项
- 如果
source
中的字符数小于num
,strncpy
会在复制完source
的字符后,在destination
中剩余的位置填充'\0'
,直到复制了num
个字符。例如:
#include <stdio.h> #include <string.h> int main() { char source[] = "abc"; char destination[10]; strncpy(destination, source, 5); printf("%s\n", destination); return 0; }
- 这个程序会输出
abc\0\0
(\0
表示不可见的字符串结束符),因为source
只有 3 个字符,strncpy
在复制完这 3 个字符后,会在destination
中填充 2 个'\0'
,以达到num = 5
的要求。 - 如果
num
大于source
的长度但小于destination
的长度,destination
中多余的部分不会被初始化(除非手动进行处理),这可能会导致一些未定义的行为,例如在后续操作中误将这些未初始化的字符当作有效字符处理。 - 另外,
destination
必须有足够的空间来容纳复制的字符,否则可能会导致缓冲区溢出,这是一个常见的安全漏洞,可能会使程序崩溃或者被恶意攻击。
- 如果
4.4 strcat();函数
- 函数原型为
char *strcat(char *dest, const char *src);
。- 在下面这个例子中,
strcat
函数将字符串t
复制到字符串s
的末端。它首先找到s
的末尾(也就是当前s
中最后一个字符后面的位置,这个位置是由'\0'
来标识的,strcat
会从这个'\0'
位置开始复制),然后将t
中的字符逐个复制到s
中,直到遇到t
中的'\0'
结束符,最后在新的s
末尾也会保留这个'\0'
结束符。并且strcat
函数返回指向dest
(也就是s
)的首地址,这样就满足了题目中要求的功能。 - 不过要注意,使用
strcat
函数时,必须确保dest
所指向的数组有足够的空间来存储连接后的字符串,否则会导致缓冲区溢出。
- 在下面这个例子中,
- C++语言中的
std::strcat
函数(在<cstring>
头文件中)- C++兼容C标准库,所以在C++中也可以使用
strcat
函数来实现类似的功能。 - 另外,在C++中更推荐使用
std::string
类来操作字符串,std::string
类提供了append
方法来实现类似的字符串连接功能。 - 例如:
cpp #include <iostream> #include <string> int main() { std::string s = "Hello, "; std::string t = "World!"; s.append(t); std::cout << s << std::endl; return 0; }
- C++兼容C标准库,所以在C++中也可以使用
4. strstr()函数
函数原型:char *strstr(const char *haystack, const char *needle);
。其中haystack
是要在其中查找的主字符串,needle
是要查找的子字符串。
功能:用于在一个字符串中查找指定字符首次出现的位置。它返回一个指向该字符第一次出现位置的指针,如果找不到指定字符,则返回NULL
。
示例代码:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "This is a sample string";
char sub[] = "sample";
char *result = strstr(str, sub);
if (result!= NULL) {
printf("子字符串 '%s' 在字符串 '%s' 中的位置是 %ld\n", sub, str, result - str);
} else {
printf("未找到子字符串 '%s'\n", sub);
}
return 0;
}
4. puts()函数
puts函数属于系列的输入输出函数
(也就是说头文件#include中就包含这个函数
作用:
puts()函数只显示字符串,而且自动在显示的字符串末尾加上换行符
4. sprintf函数
sprintf
函数是 C 语言标准库中的一个函数,用于将格式化的数据写入字符串。它的原型定义在 stdio.h
头文件中,其功能类似于 printf
,但 printf
将格式化的数据输出到标准输出(通常是屏幕),而 sprintf
将数据写入一个字符串。
sprintf
函数的原型如下:
int sprintf(char *str, const char *format, ...);
参数说明:
str
:指向字符数组的指针,用于存储生成的格式化字符串。format
:格式控制字符串,它指定了后续参数如何被格式化。...
:可变参数列表,其个数和类型依赖于格式控制字符串。
返回值:
- 成功时返回写入的字符数,不包括结尾的空字符
\0
。 - 失败时返回一个负数。
格式控制字符串 format
可以包含普通的字符和特殊的格式化占位符。普通的字符会原样输出,而格式化占位符会被相应的参数替换。格式化占位符通常的形式是 %
后跟一个或多个格式说明符,例如:
%d
:替换为一个十进制整数。%f
:替换为一个浮点数。%s
:替换为一个字符串。%c
:替换为一个字符。%x
或%X
:替换为一个十六进制整数(小写或大写)。%o
:替换为一个八进制整数。%p
:替换为一个指针的值,通常显示为十六进制。
除了类型说明符,还可以指定宽度、精度、长度修饰符和标志位来控制输出的格式。例如:
%5d
:整数至少占用5个字符的宽度,如果不足则用空格填充。%.2f
:浮点数打印到小数点后两位。%05d
:整数至少占用5个字符的宽度,如果不足则用0填充。%-5s
:字符串至少占用5个字符的宽度,如果不足则用空格填充,但左对齐。
下面是一个使用 sprintf
的示例:
#include <stdio.h>
int main() {
char buffer[50];
int value = 10;
float fvalue = 3.14159;
char *text = "Moonshot AI";
sprintf(buffer, "Integer: %d, Float: %.2f, String: %s", value, fvalue, text);
printf("%s\n", buffer);
return 0;
}
在这个例子中,sprintf
将整数 value
、浮点数 fvalue
和字符串 text
格式化后存储到 buffer
中,然后通过 printf
函数输出这个格式化后的字符串。
安全注意事项:
- 使用
sprintf
时需要确保目标字符串str
有足够的空间来存储所有格式化后的字符,包括结尾的空字符\0
,否则可能会导致缓冲区溢出。 - 为了避免缓冲区溢出的风险,推荐使用
snprintf
函数,它允许指定目标缓冲区的最大长度,从而防止溢出。
snprintf
函数的原型如下:
int snprintf(char *str, size_t size, const char *format, ...);
这里的 size
参数指定了目标缓冲区的最大长度,snprintf
会确保不会写入超过这个长度的字符,包括结尾的空字符。如果格式化后的字符串长度超过了 size
,则只写入 size - 1
个字符,并在最后加上空字符 \0
。返回值是如果没有发生截断,它将返回写入的字符数(不包括结尾的空字符),如果发生截断,则返回所需的总长度(不包括结尾的空字符)。
5 标准库string
可变长的字符序列(是一个动态分配的数组),比字符数组更加好用。但是需要引入头文件:可以的
可以动态地把两个字符串拼在一块儿?
#inlcude <string>
5.1 定义和初始化
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main (void)
{
//默认初始化
string s1;
cin >> s1;
string s2 = s1; //s2是s1的副本
string s3 = "ziyang" ; //s3是该字符串的副本
string s4(10,'c'); //前面的参数指定字符串中字符的个数,后面的参数指定用来填充的字符
cout << s2 << endl;
return 0;
}
5.2 部分函数
5.2.1 .empty()函数
注意,是’.’+函数!!
用来判断字符串是否为空
如果是空字符串的话就返回“true/1”,如果不是的话,就返回“false/0“【返回一个布尔值】
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main (void)
{
string s1 = "ziyang",s2;
cout << s1.empty() <<endl;
cout << s2.empty() <<endl;
return 0;
}
输出:
0
1
5.2.2 .size()函数
和strlen()函数的效果相同,返回字符串的长度
但是.size()函数是储存了变量的,不需要循环整个数组,所以运行效率大于strlen()
strlen()函数本质上会循环整个字符数组,可以参考之前的等价代码
#include <iostream>
#include <string>
using namespace std;
int main (void)
{
string s1 = "ziyang",s2;
cout << s1.size() << endl;
cout << s2.size() << endl;
return 0;
}
![[Pasted image 20240913101236.png]]
5.2.3
5.2 输入和输出
string可以只能用cin读入,不能用scanf读入
string可以用cout输出,也可以用printf输出,但是输出的格式会有变化【同理,也可以用puts】
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
int main (void)
{
string s1,s2;
//scanf("%c",s1); //不行,会报错
cin >> s1 >> s2;
printf("%d",s1.c_str()); //printf特殊输出
puts(s1.c_str());
cout << s1 << s2 << endl;
return 0;
}
注意:
- 使用printf(“%d”,s1.c_str());进行输出,其中的s1.c_str()表示调用一个
<string>
中的xx成员函数,返回存储s1字符串的字符数组的首地址(也可以看作这个函数将(string)s1转化为(字符数组)s1,进而输出s1),进而进行输出 - 注意字符数组和string的读入函数和输出函数的区别
5.3 运算
5.3.1 关系运算
string类型的变量可以直接进行部分关系运算,例如
< > <= >= == !=
按照字典序进行比较
但是字符数组只能运用strxx()函数进行运算
#include <iostream>
#include <string>
using namespace std;
int main (void)
{
string s1,s2;
cin >> s1 >> s2;
if(s1 > s2) cout << "s1 > s2"
else cout << "s1 < s2"
return 0;
}
![[Pasted image 20240913110143.png]]
5.3.2 赋值运算
string的变量可以直接进行赋值运算,例如:
#include <iostream>
#include <string>
using namespace std;
int main (void)
{
string s1,s2;
s1 = "ziyang";
s2 = s1; //直接赋值运算
cout << "s1 = " << s1 << endl << "s2 = " <<s2 << endl;
return 0;
}
5.3.3 字符串”+“运算、“+=”运算
直接将字符串拼接在一起
#include <iostream>
#include <string>
using namespace std;
int main (void)
{
string s1,s2,s3,s4;
s1 = "ziyang";
s2 = " = ";
s3 = "子暘"; //直接赋值运算
s4 = "ziyang";
s4 += s2 + s3;
cout << s1 + s2 + s3 << endl;
cout << s4 << endl;
return 0;
}
//输出:
ziyang = 子暘
ziyang = 子暘
附加:字面值和string对象相加:
做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是把这些字面值串联起来string s1 = "hello",s2 = "world";
//在s1和s2中都没有标点符号string s3 = s1 + "," + s2 + '~!';
当把string 对象和字符字面值以及字符串面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string
–>因为只有string变脸才能够进行直接加法运算【简单运算符的一方是string变量,则整个运算就都会变成string变量的运算(同整型变量与浮点数运算时数据类型的转化一样)】string s4 = s1 + ","
//正确:把一个string 对象和一个字面值相加string s5 = "hello" + "."
//错误,两个运算对象都不是string变量string s6 = s1 + "," + "world"
//正确:每个加法运算都有一个运算对象是string参数string s7 = "hello" + "," + s2
//错误:不能把字面值直接相加,运算是从左到右进行的
5.3.4 处理string变量中的字符
可以直接把 string 当作字符数组处理
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int main (void)
{
string s = "hello world !";
for(int i = 0 ; i < s.size() ; i ++) cout << s[i] << endl;
return 0;
}
也可以利用 C++所特有的函数(范围遍历)
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int main (void)
{
string s = "hello world !";
for(char c : s) cout << c << endl;
//前面是字符数组里面每个数组元素的类型(上面的char)
//c是遍历数组中每个元素的变量
//s表示一个string字符串
//这个函数就表示c会顺次遍历s数组中元素s[0],s[1]...
return 0;
注意,这个函数中的“c”相当于是一个储存量,每次循环把数组元素储存到“c”里面,所以无法通过对“c”的运算来改变字符数组 string
那怎么完成上面的设想呢?for(char &c : s) cout << c << endl;
在 c 前加上一个取地址符(原因目前不知。感觉像是把 c 改成一个指针了)
附:汉字的字符串储存
前言:问题来源于我做哈夫曼编码和译码实训的时候,题目要求输入一段英文或中文。然后对其中的英文或中文进行检索,才能计算出相应的哈夫结点权值,构造哈夫曼树求哈夫曼编码。
对于中文的处理,头脑空白,只能想起一个char类型的变量无法存储中文汉字。然后我在查阅了许多对于c语言有关中文的文章之后,解决了把一个中文汉字当做变量来处理的问题。
我们可以先用strlen函数查看中文汉字占见个字节,(这里要注意不同字符集编码下的字节长度有所不同,包括英文字母,可以用strlen查看字节数)
![[Pasted image 20240922142501.png]]
我们可以发现,中文占两个字节,而char类型是一个字节长度的;因此,它的长度与char类型数据并不符合。这也就意味着我们不能像char类型一样简单操作中文字符。
问题总有解决的办法,像GB2312这些大多汉字编码,典型的用2个字节来表式大多数常用的中文汉字,最多可以表式65536个汉字,(同时:字符在数组中的存储是连续的)
所以我们可以用一个char数组实现对中文字符的操作,例如:
一、想要存入一个或多个中文:
#include<stdio.h>
int main()
{
char ch[12];
//输入一个中文
scanf("%c%c",&ch[0],&ch[1]);
printf("%c%c",ch[0],ch[1]);
//输入一个中文
scanf("%c%c",&ch[2],&ch[3]);
printf("%c%c",ch[2],ch[3]);
//输入任意个中文(不超过数组长度-1,数组有一个结束符'\0')
scanf("%s",ch);
printf("%s",ch);
return 0;
}
二、对其中的一些中文取出:
#include<stdio.h>
int main()
{
char ch[15]={"这里有七个中文"};
printf("char数组中第1个中文:\n");
printf("%c%c\n",ch[0],ch[1]);
printf("char数组中第2个中文:\n");
printf("%c%c\n",ch[2],ch[3]);
printf("全部中文:\n");
int j=1;
for(int i=0;i<=12;i+=2)
printf("第%d个中文是:%c%c\n",j++,ch[i],ch[i+1]);
return 0;
}
三、判断两个中文是否相等:
#include<stdio.h>
int main()
{
char ch1[3]="中";
char ch2[3]="汉";
char ch3[3]="汉";
if(ch1[0]==ch2[0]&&ch1[1]==ch2[1])
printf("ch1与ch2不相等\n");
else printf("ch1与ch2相等\n");
if(ch2[0]==ch3[0]&&ch2[1]==ch3[1])
printf("ch2与ch3不相等\n");
else printf("ch2与ch3相等\n");
return 0;
}
当然,如有需要也可以同时存入英文和中文;
总之,char类型一个字节用一个%c就可以表示,中文需要两个%c%c才能表示.
一些字符串函数的代码实现
strcat函数:
char *str_cat(char *s, char *t) {
char *p = s; // p指向s的首地址
while (*p) p++; // 移动p到s的末尾
while (*t) { // 将t的内容复制到s的末尾
*p = *t;
p++;
t++;
}
*p = '\0'; // 添加字符串结束符
return s; // 返回s的首地址
}
strstr函数:
char *search(char *s, char *t) {
int i,j;
int len = strlen(t);
for(i = 0 ; s[i] != '\0'; i ++) { // 为什么外层循环终止条件是s[i] != '\0'?
for(j = 0 ; j < len ; j ++) {
if(s[i + j] != t[j]) break;
}
if(j == len) return &s[i];
}
return NULL;
}
高级的strcpy函数
void strmcpy(char *t, int m, char *s) {
int len = strlen(t);
int start = m -1;
if(m > len) s[0] = '\0';
else {
for(int i = 0 ; i < len ; i ++) {
*s[i] = *t[start + i];
}
s[start + len] = '\0';
}
}
无string头文件实现版本:
[!note] 重点
关键在于len读取后重置t指针位置
void strmcpy(char *t, int m, char* s) {
int len = 0;
while(*t!='\0') {
t++;
len++;
}
// printf("len = %d",len);
int start = m - 1;
if(m > len) s[0] = '\0';
else {
t -= len;
while(t[start] != '\0') {
s[start - m + 1] = t[start];
start ++;
}
s[start - m + 1] = '\0';
}
}
删除指定字符函数
void delchar(char *str, char c) {
char *read = str;
char *write = str;
while(*read != '\0') {
if(*read != c) {
*write = *read;
write ++;
}
read ++;
}
*write = '\0';
}
由此可以拓展出删除指定字符串的函数:
char *delchar_plus(char *str, char *t) {
if (str == NULL || t == NULL) return NULL;
char *write = *str;
int len = strlen(t);
int j;
for(int i = 0 ; str[i] != '\0' ; i ++ ) {
j = 0;
while(str[i+j] == t[j] && str[i+j] != '\0' && t[j]!= '\0') {
// 这里要注意边界判断的问题
j++;
}
if(j == len) i += len - 1;
else {
*write = str[i];
write ++;
}
}
*write = '\0';
return str; // 注意返回值的问题
}