文章目录
- 4.3 字符串
- 4.3.1 字符串的定义与存储
- 4.3.2 字符串的基本操作(顺序存储)
- 1. 串长统计
- 2. 串定位
- 3. 串复制
- 4. 串插入
- 5. 串删除
- 6. 串拼接
- 7.主函数
- 8. 代码整合及优化
4.3 字符串
字符串(String)是由零个或多个字符(char)顺序排列组成的有限序列,简称为串。例如 “good morning”就是由12个字符构成的一个字符串。一般把字符串记作:
S = ′ ′ a 0 a 1 … a n − 1 ′ ′ S=''a_{0} a_{1}…a_{n-1}'' S=′′a0a1…an−1′′
其中S是串名,引号中的字符序列是串值。字符个数是串的长度,长度为0的串被称为空串,因为它不包含任何字符。需要注意的是,空格字符(" ")并不是空串,因为它包含一个字符——空格。
若把某个串称为主串,则主串中任意个连续的字符组成的子序列被称为子串。子串在主串中第一次出现时,其首字符在主串中的序号被称为该子串在主串中的位置。
关于字符串的基础知识亦可参考前文:
【重拾C语言】六、批量数据组织(三)数组初值;字符串、字符数组、字符串数组;类型定义 typedef
【重拾C语言】七、指针(三)指针与字符串(字符串与字符串数组;指针与字符串的遍历、拷贝、比较;反转字符串)
4.3.1 字符串的定义与存储
字符串在许多非数值计算问题中扮演着重要的角色,并在模式匹配、程序编译和数据处理等领域得到广泛应用。在高级程序设计语言中,字符串通常被定义为以特殊字符’\0’(称为空字符或字符串结束符)结尾的字符序列。这个约定使得在处理字符串时可以方便地确定字符串的结束位置。关于字符串的存储方式,主要有两种常见的方式:
-
顺序存储:字符串的字符按照顺序依次存储在连续的内存空间中。这种方式使得字符串的访问和操作效率较高,可以通过索引直接访问任意位置的字符。在顺序存储方式中,字符串的长度可以通过计算字符个数或者遇到’\0’结束符来确定。
-
链式存储:字符串的字符通过链表的方式进行存储。每个节点包含一个字符和指向下一个节点的指针。链式存储方式可以动态地分配内存,适用于长度可变的字符串。但是相比于顺序存储,链式存储方式需要更多的内存空间,并且访问字符需要遍历链表。
选择何种存储方式取决于具体的应用场景和需求。顺序存储适合于需要频繁访问和操作字符串的情况,而链式存储适合于长度可变的字符串或者对内存空间要求较高的情况。具体C语言实现可参照前文:
【数据结构】数组和字符串(十一):字符串的定义与存储(顺序存储、链式存储及其C语言实现)
4.3.2 字符串的基本操作(顺序存储)
- 串长统计返回串s的长度;
- 串定位返回字符或子串在母串s中首次出现的位置的指针;
- 串复制将一个串s2复制到另一个串s1中;
- 串插入在指定位置后面插入字符串;
- 串删除是删除一个子串;
- 串拼接将串s2拼接到串s1的尾部;
- ……
1. 串长统计
返回字符串的长度,即字符串中字符的个数。
int stringLength(const char* str) {
int length = 0;
while (str[length] != '\0') {
length++;
}
return length;
}
- 接受一个指向字符数组的指针作为参数
- 通过遍历数组中的字符,直到遇到字符串结束符
\0
,来确定字符串的长度。 - 返回值为字符串的长度。
2. 串定位
查找字符或子串在母串中首次出现的位置,返回指向该位置的指针或索引。
int stringSearch(const char* str, const char* target) {
int strLen = stringLength(str);
int targetLen = stringLength(target);
for (int i = 0; i <= strLen - targetLen; i++) {
int j;
for (j = 0; j < targetLen; j++) {
if (str[i + j] != target[j]) {
break;
}
}
if (j == targetLen) {
return i;
}
}
return -1;
}
- 接受两个指向字符数组的指针作为参数:
str
是要搜索的字符串,target
是要查找的目标字符串。 - 使用双重循环来遍历字符串,并在每个可能的位置比较目标字符串和源字符串的字符。
- 如果找到了目标字符串,函数返回目标字符串在源字符串中的起始位置;
- 否则,返回-1表示未找到。
3. 串复制
将一个串复制到另一个串中:将源串的内容复制到目标串中,使得目标串与源串内容相同。
void stringCopy(char* dest, const char* src) {
int i = 0;
while (src[i] != '\0') {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
- 接受两个指向字符数组的指针作为参数:
dest
是目标字符串,src
是源字符串。 - 通过遍历源字符串中的字符,并将每个字符复制到目标字符串中的相应位置,直到遇到源字符串的结束符
\0
。 - 注意,在目标字符串的末尾添加结束符
\0
。
显然,复制函数通过将字符串s2中的字符逐个复制到s1中来实现,这就要求s1足够大,否则一旦字符串s2比s1长,该程序无检查复制出界和报告错误的机制,可能导致字符的丢失。可以检查出界和报错机制的算法请读者自己尝试给出。(具体错误检查和报错机制详见8. 代码整合及优化)
4. 串插入
在指定位置后面插入一个字符串:在母串中的指定位置插入一个子串,改变母串的内容。
void stringInsert(char* str, const char* insertStr, int pos) {
int strLen = stringLength(str);
int insertStrLen = stringLength(insertStr);
// 移动字符,为插入字符串腾出空间
for (int i = strLen; i >= pos; i--) {
str[i + insertStrLen] = str[i];
}
// 插入字符串
for (int i = 0; i < insertStrLen; i++) {
str[pos + i] = insertStr[i];
}
}
- 接受三个参数:
str
是要插入字符串的目标字符串,insertStr
是要插入的字符串,pos
是插入位置的索引。 - 函数首先移动目标字符串中的字符,为插入字符串腾出空间。然后,将插入字符串的字符逐个复制到目标字符串的指定位置。
5. 串删除
删除一个子串:母串中删除指定位置的子串,改变母串的内容。
void stringDelete(char* str, int pos, int length) {
int strLen = stringLength(str);
// 移动字符,删除指定位置的子串
for (int i = pos + length; i <= strLen; i++) {
str[i - length] = str[i];
}
}
- 接受三个参数:
str
是要删除子串的目标字符串,pos
是要删除的子串的起始位置,length
是要删除的子串的长度。 - 通过移动目标字符串中的字符,将指定位置后的字符覆盖掉待删除的子串,从而实现删除操作。
6. 串拼接
将一个串连接到另一个串的尾部:将两个串的内容连接起来,形成一个新的串。
void stringConcat(char* str1, const char* str2) {
int str1Len = stringLength(str1);
int str2Len = stringLength(str2);
// 将str2拼接到str1的尾部
for (int i = 0; i < str2Len; i++) {
str1[str1Len + i] = str2[i];
}
str1[str1Len + str2Len] = '\0';
}
- 接受两个参数:
str1
是目标字符串,str2
是要拼接的字符串。 - 通过遍历第二个字符串中的字符,并将每个字符依次追加到第一个字符串的末尾。
- 最后,在目标字符串的末尾添加结束符
\0
。
7.主函数
int main() {
char S[100] = "Qomolangma";
char target[] = "lang";
char insertStr[] = " H ";
int pos = 4;
int length = 3;
printf("String S: %s\n", S);
printf("String Length: %d\n", stringLength(S));
int searchResult = stringSearch(S, target);
if (searchResult != -1) {
printf("Target found at position: %d\n", searchResult);
} else {
printf("Target not found\n");
}
char copyStr[100];
stringCopy(copyStr, S);
printf("Copied String: %s\n", copyStr);
stringInsert(S, insertStr, pos);
printf("String after insertion: %s\n", S);
stringDelete(S, pos, length);
printf("String after deletion: %s\n", S);
stringConcat(S, "H");
printf("String after concatenation: %s\n", S);
return 0;
}
8. 代码整合及优化
#include <stdio.h>
#include <stdbool.h>
int stringLength(const char* str) {
int length = 0;
while (str[length] != '\0') {
length++;
}
return length;
}
int stringSearch(const char* str, const char* target) {
int strLength = stringLength(str);
int targetLength = stringLength(target);
if (targetLength > strLength) {
// 目标字符串比源字符串长,无法找到
printf("Error: Target string is longer than the source string.\n");
return -1;
}
for (int i = 0; i <= strLength - targetLength; i++) {
bool match = true;
for (int j = 0; j < targetLength; j++) {
if (str[i + j] != target[j]) {
match = false;
break;
}
}
if (match) {
return i;
}
}
// 未找到目标字符串
printf("Error: Target string not found in the source string.\n");
return -1;
}
bool stringCopy(char* dest, const char* src, int destSize) {
int i = 0;
while (src[i] != '\0') {
if (i >= destSize - 1) {
// 目标字符串空间不足,报错并返回
printf("Error: Destination string is too small to hold the source string.\n");
return false;
}
dest[i] = src[i];
i++;
}
dest[i] = '\0';
return true;
}
bool stringInsert(char* str, const char* insertStr, int pos, int maxSize) {
int strLength = stringLength(str);
int insertStrLength = stringLength(insertStr);
if (pos < 0 || pos > strLength) {
// 插入位置越界
printf("Error: Invalid insertion position.\n");
return false;
}
if (strLength + insertStrLength >= maxSize) {
// 目标字符串空间不足,报错并返回
printf("Error: Destination string is too small to hold the inserted string.\n");
return false;
}
// 向后移动字符,为插入字符串腾出空间
for (int i = strLength; i >= pos; i--) {
str[i + insertStrLength] = str[i];
}
// 插入字符串
for (int i = 0; i < insertStrLength; i++) {
str[pos + i] = insertStr[i];
}
return true;
}
bool stringDelete(char* str, int pos, int length) {
int strLength = stringLength(str);
if (pos < 0 || pos >= strLength) {
// 删除位置越界
printf("Error: Invalid deletion position.\n");
return false;
}
if (pos + length > strLength) {
// 删除长度超出字符串范围
printf("Error: Deletion length exceeds the length of the string.\n");
return false;
}
// 向前移动字符,覆盖待删除的子串
for (int i = pos; i < strLength - length; i++) {
str[i] = str[i + length];
}
str[strLength - length] = '\0';
return true;
}
bool stringConcat(char* str1, const char* str2, int maxSize) {
int str1Length = stringLength(str1);
int str2Length = stringLength(str2);
if (str1Length + str2Length >= maxSize) {
// 目标字符串空间不足,报错并返回
printf("Error: Destination string is too small to hold the concatenated string.\n");
return false;
}
// 追加字符串
for (int i = 0; i < str2Length; i++) {
str1[str1Length + i] = str2[i];
}
str1[str1Length + str2Length] = '\0';
return true;
}
int main() {
char S[20] = "Qomolangma";
const char target[5] = "lang";
char copyStr[10];
const char insertStr[5] = " H ";
int pos = 4;
int maxSize = 20;
int length = 3;
int searchIndex = stringSearch(S, target);
if (searchIndex != -1) {
printf("Target string found at index: %d\n", searchIndex);
}
bool copied = stringCopy(copyStr, S, sizeof(copyStr));
if (copied) {
printf("Copied string: %s\n", copyStr);
}
bool inserted = stringInsert(S, insertStr, pos, maxSize);
if (inserted) {
printf("After insertion: %s\n", S);
}
bool deleted = stringDelete(S, pos, stringLength(insertStr));
if (deleted) {
printf("After deletion: %s\n", S);
}
bool concatenated = stringConcat(S, "H", maxSize);
if (concatenated) {
printf("After concatenation: %s\n", S);
}
return 0;
}