C 语言中字符串处理函数



一、字符串基础

  • 字符串可以当作以空字符结尾的字符数组。

空字符:一个所有位都为0的字节,因此用转义序列\0来表示。注意不要混淆空字符'\0'和零字符'0'。空字符的ASCII值为0,而零字符的码值为48

  • 字符串长度就是它所包含的字符个数但不包括空字符
  • 字符串常量是用一对引号括起来的字符序列,在程序执行过程中保持不变的数据
  • 字符串变量可以在程序运行过程中发生改变

二、字符串处理函数

计算字符串长度

库函数strlen的原型如下:

size_t strlen(const char *s);

注意strlen返回一个类型为size_t的值,这个类型是一个无符号整数类型。在表达式中使用无符号数可能导致不可预料的结果。例如,下面两个表达式看上去是相等的:

if (strlen(x) >= strlen(y)) ...
if (strlen(x) - strlen(y) >= 0) ...

但事实上它们是不相等的。第一条语句将按照预想的那样工作,但第2条语句的结果将永远是真值。strlen的结果是个无符号数,所以操作符>=左边的表达式也将是无符号数,而无符号数绝不可能是负的。

不受限制的字符串函数

  • 复制字符串

char *strcpy(char *dest, const char *src);

这个函数把参数src字符串复制到dest参数。如果参数src和dest在内在中出现重叠,其结果是未定义的。由于dst参数将进行修改,所以它必须是个字符数组或一个指向动态分配内存的数组的指针,不能使用字符串常量

例子:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char message[]= "Original message";
    strcpy(message, "Different");
    
    printf("message = %s\n", message);
    return 0;
}

运行结果

message = Different

数组message将包含下面的内容

char message[] = {'D', 'i', 'f', 'f', 'e', 'r', 'e', 'n', 't', '\0', 'e', 's', 's', 'a', 'g', 'e', '\0'};

第一个空字符后面的几个字符再也无法被字符串函数访问,从现实的角度看,它们已经是丢失的了。

  • 连接字符串

char *strcat(char *dest, const char *src);

要想把一个字符串添加(连接)到另一个字符串后面,可以使用strcat函数。

函数strcat要求dest参数原先已经包含一个空字符(可以是空字符串)。它找到这个字符串的未尾,并把src字符串的一份拷贝添加到这个位置。

例子:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char message[100];
    
    strcpy(message, "hello ");
    strcat(message, "string");

    printf("message = %s\n", message);

    return 0;
}

运行结果:

message = hello string

  • 比较字符串

int strcmp(const char *s1, const char *s2);

比较两个字符串时涉及对两个字符串对应的字符逐个比较,直到发现不匹配为止。采用的是字典比较,如果s1小于s2,返回一个小于零的值。如果s1 大于s2,返回一个大于零的值,如果两个字符串相等,返回零。

例子:

if (strcmp(a, b)) ...

这种写法是错误的,如果想要比较两个字符串相等,应该把返回值与零进行比较。

长度受限的字符串函数

char *strncpy(char *dest, const char *src, size_t n);
char *strncat(char *dest, const char *src, size_t n);
int strncmp(const char *s1, const char *s2, size_t n);

这些函接受一个显式的长度参数,用于限定进行复制或连接或比较的字符数。这些函数提供了一种方便的机制,可以防止难以预料的长字符串从它们的目标数组溢出。

字符串查找函数

  • 查找一个字符
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);

函数strchr在字符串s中查找字符c第1次出现的位置,找到后函数返回一个指向该位置的指针。如果该字符并不存在字符串中,函数就返回一个NULL指针。

例子:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char *p, str[] = "Form follows function.";
    p = strchr(str, 'f'); /* finds first 'f' */
    
    printf("p = %s\n", p); 
    return 0;
}

执行结果:

p = follows function.

函数strrchrstrchr类似,但它会反向搜索字符。

例子:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char *p, str[] = "Form follows function.";
    p = strrchr(str, 'f'); /* finds last 'f' */
    
    printf("p = %s\n", p); 
    return 0;
}

执行结果:

p = function.
  • 查找任何几个字符
char *strpbrk(const char *s, const char *accept);

函数strpbrk它并不查找某个特定的字符,而是查找任何一组字符第1次在字符串中出现的位置,它返回一个指向s中第一个匹配accept中任何一个字符的字符位置。

例子:

#include <string.h>

int main(void)
{
    char *p, str[] = "Form follows function.";
    p = strpbrk(str, "mn"); 
    
    printf("p = %s\n", p); 
    return 0;
}

执行结果:

p = m follows function.
  • 查找一个子串
char *strstr(const char *haystack, const char *needle);

函数strstr在第一个参数中查找第二个参数第1次出现的起始位置,并返回一个指向该位置的指针。如果needle并没有完整地出现在haystack的任何地方,函数将返回NULL,如果第二个参数是一个空字符串,函数就返回haystack

例子:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char *p, str[] = "Form follows function.";
    p = strstr(str, "fun");
    
    printf("p = %s\n", p); 
    return 0;
}

执行结果:

p = function.
  • 查找一个字符串前缀
size_t strspn(const char *s, const char *accept);
size_t strcspn(const char *s, const char *reject);

函数strspn返回字符串中第一个不属于该组字符的字符的下标strcspn函数返回第一个属于该组字符的字符的下标

例子:

size_t n;
char *p, str[] = "Form follows function.";

n = strspn(str, "Form");    /* n = 4 */ 
n = strspn(str, " \t\n");   /* n = 0 */
n = strcspn(str, "Form");   /* n = 0 */
n = strcspn(str, " \t\t");  /* n = 4 */
  • 查找标记
char *strtok(char *str, const char *delim);

函数strtok目的是在字符串中搜索一个记号(就是一系列不包含特定分隔字符的字符)。调用strtok(s1, s2)它会在s1中搜索不包含在s2中的非空字符序列。strtok函数会在记号末尾的字符后面存储一个空字符作为标记,然后返回一个指针指向记号的首字符。

strtok函数最有用的特点是以后可以调用strtok函数在同一字符串中搜索更多的记号。调用strtok(NULL, s2)就可以继续上一次的strtok函数调用。和上一次调用一样,strtok函数会用一个空字符来标记新的记号的末尾,然后返回一个指向新记号的首字符的指针。这个过程可以持续进行,直到strtock函数返回空指针,这表明找不到符合要求的记号。

一个提取年月日的例子:

#include <stdio.h>
#include <string.h>

int main(void)
{
    
    char *month, *day, *year;
    char str[] = " April  28,1998"; /* 月与日之间以空格或制表符分隔,逗号之前可能有空格或制表符 */
    
    month = strtok(str, " \t");
    day = strtok(NULL, " \t,");
    year = strtok(NULL, " \t");

    printf("month = %s, day = %s, year = %s\n", month, day, year);
    printf("str = %s\n");
    
    return 0;
}

执行结果:

month = April, day = 28, year = 1998
str = April

三、内存操作函数

  • void *memcpy(void *dest, const void *src, size_t n);

函数memcpy从src的起始位置复制n个字节到dest的内存起始位置。可以用该函数复制任何类型的值,如果src和dest内存出现了重叠则结果是未定义的。

例子:

#include <stdio.h>
#include <string.h>

#define SIZE 5

int main(void)
{
    int a[SIZE], b[] = {1, 2, 3, 4, 5}, *p; 
    
    memcpy(a, b, sizeof(b));    

    for (p = a; p < a + SIZE; p++) {
        printf(" %d", *p);
    }   
    fputc('\n', stdout);

    return 0;
}

执行结果:

1 2 3 4 5

  • void *memmove(void *dest, const void *src, size_t n);

函数memmove功能与memcpy相同,但它能够正确处理源参数和目标参数内存出现重叠的情况。

例子:

#include <stdio.h>
#include <string.h>

#define SIZE 5

int main(void)
{
    int a[SIZE] = {1, 2, 3, 4, 5}, *p; 
    
    memmove(a, a + 1, (SIZE - 1) * sizeof(a[0]));   

    for (p = a; p < a + SIZE; p++) {
        printf(" %d", *p);
    }   
    fputc('\n', stdout);

    return 0;
}

执行结果:

2 3 4 5 5

  • int memcmp(const void *s1, const void *s2, size_t n);

函数memcmp对两段内存的内容进行比较,这两段内存分别起始于s1和s2,共比较n个字节。这些值按照无符号字符逐个字节进行比较,函数返回类型与strcmp一样。由于这些值是根据一串无符号字节进行比较的,所以如果memcmp函数用于比较不是单字节的数据(如整数或浮点数)就可能会现出不可预料的结果。

  • void *memchr(const void *s, int c, size_t n);

函数memchr从s的起始位置开始查找字符c第1次出现的位置,并返回一个指向该位置的指针,它共查找n个字节。

  • void *memset(void *s, int c, size_t n);

函数memset函数把s开始的n个字节都设置为字符c。

例子:

#include <stdio.h>
#include <string.h>

#define SIZE 5

int main(void)
{
    int a[SIZE], *p; 
    
    memset(a, 0, sizeof(a));
    for (p = a; p < a + SIZE; p++) {
        printf(" %d", *p);
    }   
    fputc('\n', stdout);

    return 0;
}

运行结果:

0 0 0 0 0

文章作者: 张权
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 张权 !
评论
  目录