字符串
本文最后更新于 113 天前,其中的信息可能已经过时,如有错误请发送邮件到 zhangweihao22@outlook.com

BY ziyang

0 字符与整数的联系 ——ASCLL 码

0.1 ASCLL 需要记住的几个点

  • 数字 0~9 对应 48~57
  • 26 个英文字母
  • 65 为 A
  • 97 为 a
  • 大小写之前差 32(不知道与 32 进制是否有关系,哈哈)

0.2 字符与数字之间怎么进行转化?

注意:字符要用” 进行分隔!!

  1. 强制类型转化
#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;
}
  1. 字符间的运算:
    会把字符自动转化为数字,再进行数字间的运算,例如:
#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';

注意事项

  1. 在静态初始化时,如果某个子数组的元素少于声明的列数,编译器会自动用空字符(\0)填充剩余的位置。
  2. 如果你初始化的字符串长度超过了数组的列数,编译器会截断多余的部分。
  3. 在 C 语言中,字符串以空字符(\0)结尾,所以在初始化包含字符串的二维字符数组时,不需要显式添加空字符,编译器会自动处理。
  4. 在动态初始化时,如果数组的某一行没有完全初始化,剩余的位置会被初始化为垃圾值(未定义的值),所以通常需要显式地初始化每一行的每一个元素。

二维字符数组在 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++ 中的读入函数:

  1. 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 函数的使用

  1. 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'; // 去掉换行符
}
  1. 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;
}
  1. 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 = 0int len = strlen(s1) ; i < len ; i ++) cout << s1[i] << endl;
return 0;
}

3 字符串数组

字符串数组分为两类:

  1. 指针 * 数组
    指针数组很好理解,每一个数组空间里面都储存着一个字符串
  2. 数组 * 数组
    相当于一个二维字符串数组,每一 “维” 都对应一个字符数组
    二者的区别:
    同字符串数组的区别

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 复制给前面指定位置的 a
strcpy(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 (); 函数

  1. 函数定义和功能
    • strncpy 函数是 C 语言中的一个字符串处理函数,用于将一个字符串的一部分复制到另一个字符串中。它的函数原型定义在 <string.h> 头文件中,形式如下:char* strncpy(char* destination, const char* source, size_t num);
    • 其主要功能是把 source 指向的字符串中最多 num 个字符复制到 destination 指向的数组中。
  2. 参数说明
    • destination:这是目标字符串,是一个字符数组,用于存储复制后的内容。它应该有足够的空间来容纳复制的字符以及可能的字符串结束符'\0'
    • source:这是源字符串,是一个常量字符数组(由 const 修饰,表示内容不被函数修改),是要被复制的字符串。
    • num:是一个无符号整数类型(size_t)的参数,表示最多要从 source 复制到 destination 的字符个数。
  3. 返回值
    • strncpy 函数返回目标字符串 destination 的指针,这使得它可以在一个表达式中被链式调用或者方便地进行后续操作。
  4. 示例代码
    • 以下是一个简单的示例,展示 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
  5. 注意事项
    • 如果 source 中的字符数小于 numstrncpy 会在复制完 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 (); 函数

  1. 函数原型为 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 所指向的数组有足够的空间来存储连接后的字符串,否则会导致缓冲区溢出。
  2. 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; }

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;
}

注意:

  1. 使用 printf (“% d”,s1.c_str ()); 进行输出,其中的 s1.c_str () 表示调用一个 <string> 中的 xx 成员函数,返回存储 s1 字符串的字符数组的首地址(也可以看作这个函数将 (string) s1 转化为 (字符数组) s1,进而输出 s1),进而进行输出
  2. 注意字符数组和 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 才能表示.

原文 CSDN 链接

一些字符串函数的代码实现

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; // 注意返回值的问题
}
2024/12/17 创建
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
Copyright 2025-2025 @ Ziyang
Running Time146 days 11 H 1 M 57S