C-style字符串、库函数 与 std::string对象

一. C - style字符串(以空字符结尾的字符数组)

1. 字符串定义与初始化

可以使用字符数组来表示C - style字符串。

char str1[] = "Hello";
char str2[6];
str2[0] = 'W';
str2[1] = 'o';
str2[2] = 'r';
str2[3] = 'l';
str2[4] = 'd';
str2[5] = '\0';

在初始化字符数组时,如果使用字符串字面量(如"Hello"),编译器会自动在末尾添加空字符'\0'。而手动初始化字符数组时,必须确保最后一个元素是'\0',否则在使用一些字符串处理函数时可能会导致问题。

2. 字符串操作库函数

C标准库提供了一些用于处理C - style字符串的函数,如strcpy(字符串复制)、strcat(字符串连接)、strcmp(字符串比较)等。这些函数定义在<string.h>头文件中。

<string.h>是 C 语言中用于字符串操作的标准头文件。在 C++中也可以使用,但 C++更推荐使用<cstring>头文件(功能与<string.h>类似,但更符合 C++的标准)。以下是<string.h>中一些常见的函数及其功能:

(1). 字符串复制函数:

strcpy(char* dest, const char* src):将 src 指向的字符串复制到 dest 所指向的字符数组中,包括字符串结束符 '\0'。使用时需确保 dest 数组有足够的空间来容纳复制的字符串,否则会导致缓冲区溢出错误。

strncpy(char* dest, const char* src, size_t n):最多从 src 字符串中复制 n 个字符到 dest 字符数组中。当 src 的长度大于等于 n 时,只复制 n 个字符;当 src 的长度小于 n 时,先复制 src 的内容,然后将剩余空间用 '\0' 填充。

#include <stdio.h>
#include <string.h>
int main() {
    char dest[20];
    char src[] = "Hello, World!";
    strncpy(dest, src, 5);
    dest[5] = '\0';  // 手动添加结束符,因为如果src长度大于等于5,strncpy不会自动添加
    printf("Copied string: %s
", dest);
    return 0;
}

(2). 字符串连接函数:

strcat(char* dest, const char* src):将 src 字符串连接到 dest 字符串的末尾,覆盖 dest 字符串末尾的 '\0',并在连接后的字符串末尾添加一个新的 '\0'。同样,要确保 dest 数组有足够的空间来容纳连接后的字符串。

#include <stdio.h>
#include <string.h>
int main() {
    char dest[10] = "Hello";
    char src[] = " World";
    // dest数组长度为10,已经存放了"Hello"(占6个字节,包括'\0'),没有足够空间容纳" World"
    strcat(dest, src);
    printf("The concatenated string is: %s
", dest);
    return 0;
}

strncat(char* dest, const char* src, size_t n):最多将 src 字符串的前 n 个字符连接到 dest 字符串的末尾。如果 src 的长度小于 n,则将 src 的全部内容连接到 dest 末尾,然后在末尾添加 '\0'。

#include <stdio.h>
#include <string.h>
int main() {
    char dest[30] = "Hello";
    char src[] = " World";
    strncat(dest, src, 5);
    printf("Concatenated string: %s
", dest);
    return 0;
}

(3). 字符串比较函数:

strcmp(const char* s1, const char* s2):比较 s1 和 s2 两个字符串的大小。按照字典序逐个比较字符的 ASCII 值,直到找到不同的字符或者到达字符串末尾。如果 s1 小于 s2,返回一个负整数;如果 s1 等于 s2,返回 0;如果 s1 大于 s2,返回一个正整数。

#include <stdio.h>
#include <string.h>
int main() {
    char s1[] = "abc";
    char s2[] = "abd";
    int result = strcmp(s1, s2);
    if (result < 0) {
        printf("s1 is less than s2\n");
    } else if (result == 0) {
        printf("s1 is equal to s2\n");
    } else {
        printf("s1 is greater than s2\n");
    }
    return 0;
}

strncmp(const char* s1, const char* s2, size_t n):比较 s1 和 s2 两个字符串的前 n 个字符的大小。比较方式与 strcmp 类似,但只比较前 n 个字符。

#include <stdio.h>
#include <string.h>
int main() {
    char s1[] = "abcde";
    char s2[] = "abcdf";
    int result = strncmp(s1, s2, 3);
    if (result < 0) {
        printf("s1 is less than s2 (comparing first 3 characters)\n");
    } else if (result == 0) {
        printf("s1 is equal to s2 (comparing first 3 characters)\n");
    } else {
        printf("s1 is greater than s2 (comparing first 3 characters)\n");
    }
    return 0;
}

(4). 字符串查找函数:

strchr(const char* str, int c):在 str 字符串中查找字符 c 第一次出现的位置,并返回指向该位置的指针。如果未找到字符 c,则返回 NULL。

#include <stdio.h>
#include <string.h>
int main() {
    char str[] = "Hello, World!";
    char *result = strchr(str, 'o');
    if (result!= NULL) {
        printf("The part of the string starting from 'o': %s\n", result);
    }
    return 0;
}

strstr(const char* haystack, const char* needle):在 haystack 字符串中查找 needle 子字符串第一次出现的位置,并返回指向该位置的指针。如果未找到 needle 子字符串,则返回 NULL。

#include <stdio.h>
#include <string.h>
int main() {
    char haystack[] = "Hello, World!";
    char needle[] = "World";
    char *result = strstr(haystack, needle);
    if (result!= NULL) {
        printf("The substring is found at position: %ld\n", result - haystack);
    } else {
        printf("The substring is not found in the string.\n");
    }
    return 0;
}

(5). 字符串长度计算函数:

strlen(const char* str):计算 str 字符串的长度,即从字符串的首地址开始,直到遇到字符串结束符 '\0' 为止的字符个数(不包括 '\0')。

#include <stdio.h>
#include <string.h>
int main() {
    char str1[] = "Hello";
    char str2[] = " ";
    char str3[] = "";
    size_t len1 = strlen(str1);
    size_t len2 = strlen(str2);
    size_t len3 = strlen(str3);
    printf("The length of 'Hello' is %zu\n", len1);
    printf("The length of ' ' is %zu\n", len2);
    printf("The length of '' is %zu\n", len3);
    return 0;
}

(6). 内存操作函数:

memcpy(void* dest, const void* src, size_t n):从 src 所指向的内存空间中复制 n 个字节到 dest 所指向的内存空间。src 和 dest 所指向的内存区域不能重叠。

#include <stdio.h>
#include <string.h>
int main() {
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5];
    memcpy(arr2, arr1, sizeof(arr1));
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr2[i]);
    }
    printf("\n");
    return 0;
}
#include <stdio.h>
#include <string.h>
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    // 以下操作中源和目标内存区域重叠,使用memcpy会导致未定义行为
    memcpy(&arr[1], &arr[0], sizeof(int) * 4);
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

memmove(void* dest, const void* src, size_t n):与 memcpy 类似,也是从 src 复制 n 个字节到 dest,但允许 src 和 dest 所指向的内存区域重叠。其实现方式是先将 src 中的数据复制到一个临时缓冲区,然后再从临时缓冲区复制到 dest。

#include <stdio.h>
#include <string.h>
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    // 源和目标内存区域重叠,使用memmove是安全的
    memmove(&arr[1], &arr[0], sizeof(int) * 4);
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

memset(void* s, int c, size_t n):将 s 所指向的内存空间的前 n 个字节设置为指定的字符 c。常用于初始化数组或内存块。

#include <stdio.h>
#include <string.h>
int main() {
    int arr[5];
    char str[10];
    memset(str, 'a', 5); // 字符数组初始化
    str[5] = '\0';
    printf("%s\n", str);
    memset(arr, 0, sizeof(arr)); // 整数数组初始化(清零)
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

memcmp(const void* s1, const void* s2, size_t n):比较 s1 和 s2 所指向的内存空间的前 n 个字节的大小。比较结果与 strcmp 类似,返回值小于 0 表示 s1 小于 s2,返回值等于 0 表示 s1 等于 s2,返回值大于 0 表示 s1 大于 s2。

#include <stdio.h>
#include <string.h>
int main() {
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5] = {1, 2, 3, 4, 6};
    int result = memcmp(arr1, arr2, sizeof(arr1));
    if (result < 0) {
        printf("arr1 is less than arr2\n");
    } else if (result == 0) {
        printf("arr1 is equal to arr2\n");
    } else {
        printf("arr1 is greater than arr2\n");
    }
    return 0;
}

二. std::string类(C++标准库中的字符串类)

1. 定义与初始化

使用std::string类可以更方便地处理字符串。可以通过多种方式初始化std::string对象。

(1) 直接初始化

可以使用字符串字面量直接初始化std::string对象。

std::string str1 = "Hello";
std::string str2("World");

(2) 复制初始化

通过已有的std::string对象进行复制初始化。

std::string str3 = str1;

(3) 使用n个相同字符初始化

使用std::string的构造函数,可以创建一个由n个相同字符组成的字符串。

std::string str4(5, 'a'); // 创建一个包含5个 'a' 的字符串,即 "aaaaa"

2. 成员函数

(1) 获取字符串长度:length和size

这两个函数的功能相同,都用于返回字符串中字符的个数。

std::string str = "Hello";
std::cout << "Length of the string: " << str.length() << std::endl;
std::cout << "Size of the string: " << str.size() << std::endl;

(2) 字符串连接:+运算符和append函数

+运算符:可以将两个字符串连接起来,也可以将字符串和字符串字面量连接。

std::string str5 = "Hello";
std::string str6 = " World";
std::string str7 = str5 + str6;
std::string str8 = str5 + " there";

append函数:用于在原字符串末尾添加另一个字符串或字符序列。

std::string str9 = "Hello";
std::string str10 = " World";
str9.append(str10);

(3) 字符串比较:compare函数

用于比较两个字符串的大小关系。如果返回值小于0,表示当前字符串小于被比较的字符串;等于0,表示相等;大于0,表示大于。

std::string str11 = "abc";
std::string str12 = "abd";
int result = str11.compare(str12);
if (result < 0) {
    std::cout << "str11 is less than str12" << std::endl;
} else if (result == 0) {
    std::cout << "str11 is equal to str12" << std::endl;
} else {
    std::cout << "str11 is greater than str12" << std::endl;
}

(4) 访问字符串中的字符:[]运算符和at函数

[]运算符:可以像访问数组元素一样访问字符串中的字符,但不进行边界检查。

std::string str13 = "Hello";
std::cout << "The first character of str13: " << str13[0] << std::endl;
// 如果使用越界的索引,可能会导致未定义行为,如 str13[10]

at函数:与[]运算符类似,但会进行边界检查,如果越界则抛出std::out_of_range异常。

try {
    std::string str14 = "Hello";
    std::cout << "The first character of str14: " << str14.at(0) << std::endl;
    std::cout << "The sixth character of str14: " << str14.at(5) << std::endl;
} catch (std::out_of_range& e) {
    std::cout << "Index out of range: " << e.what() << std::endl;
}

(5) 查找子字符串:find函数

用于在字符串中查找子字符串。如果找到,则返回子字符串在原字符串中的起始位置(索引);如果找不到,则返回std::string::npos。

std::string str15 = "Hello World";
int pos = str15.find("World");
if (pos!= std::string::npos) {
    std::cout << "Substring 'World' found at position: " << pos << std::endl;
} else {
    std::cout << "Substring not found" << std::endl;
}

(6) 替换子字符串:replace函数

可以用指定的字符串替换原字符串中的部分内容。

std::string str16 = "Hello World";
str16.replace(0, 5, "Hi");
std::cout << "After replacement: " << str16 << std::endl;

三. 字符串输入输出操作

可以使用std::cin和std::cout对std::string对象进行输入输出操作。

std::string input;
std::cout << "Enter a string: ";
std::cin >> input;
std::cout << "You entered: " << input << std::endl;

当使用std::cin读取字符串时,如果输入包含空格,std::cin会在遇到第一个空格时停止读取如果要读取包含空格的整行字符串,可以使用std::getline函数

std::string line;
std::cout << "Enter a line: ";
std::getline(std::cin, line);
std::cout << "You entered: " << line << std::endl;

C++编程语言基础

C-style字符串、库函数 与 std::string对象