第八章 字符串和字符串函数


C程序中,字符串是用双引号括起来的字符序列。

表示字符串

字符串字面量

程序中用双引号括起来的内容称为字符串字面量(string literal),也叫做字符串常量

字符串常量属于静态存储类别(static storage class)。字符串作为可执行文件的一部分储存在数据段中,当把程序载入内存时,字符串被载入静态存储区。在程序中使用一个字符串常量,该字符串常量就会被存储到静态存储区中:

  • 可以使用多次
  • 只会被储存一次
  • 被视为const数据不可修改
  • 在程序的生命期内存在。

指针表示法创建字符串

用双引号括起来的内容被视为指向该字符串储存首字符位置的指针。类似于把数组名作为指向数组首元素的指针。

int main(void) { 
	printf("%s, %p, %c", "We", "are", *"space farers");
	return 0; 
} 
输出
We, 00AE7B44, s
const char * pt1 = "Something is pointing at me.";
/* 该字符串被存储到静态存储区, 且在末尾存储了一个 \0 来标记字符串结束
   然后把字符串首字符位置赋给指针 */

数组表示法创建字符串

// 不指定数组长度
const char m1[] = "If you can't think of anything, fake it.";
/* 编译器自动确定字符串数组大小 */
/* 当把程序载入内存时,也将字符串载入静态存储区。程序在开始运行时才会为数组分配内存,
   此时才将储存在静态存储区(static memory)中的字符串拷贝到数组中 。
   程序应确保字符数组长度比字符串长度多1(为了存储\0)
   此时字符串有两个副本。一个是在静态内存中的字符串常量,另一个是储存在 m1 数组中的字符串 */

// 指定数组长度
const char m2[40] = "Limit yourself to one line's worth.";
/* 要确保数组长度比字符串长度多1,用于存储 \0
   所有未被使用的元素都被自动初始化为 \0  */

//创建一个稍后填充的字符数组
char m3[40]; 

/* 字符数组名和其他数组名一样,是该数组首元素的地址 */
char car[10] = "Tata";
car == &car[0]
*car == 'T'
*(car+1) == car[1] == 'a'

从ANSI C标准起,如果字符串字面量之间没有间隔,或者用空白字符分隔,C会将其视为串联起来的字符串字面量。

char greeting[50] = "Hello, and"" how are" " you"
		" today!";
		//等价
char greeting[50] = "Hello, and how are you today!"; 

//如果要在括号内使用引号,需要使用转义序列 \"
printf("\"Run,Spot, run!\" exclaimed Dick.\n");

数组和指针创建字符串的区别

首先,用双引号括起来的字符串字面量都会存储在静态存储区中。
初始化数组把静态存储区的字符串拷贝到数组中,
初始化指针只把静态存储区中·字符串的地址拷贝给指针。

/* 数组表示法创建字符串 */
const char ar1[] = "A copy of the static store.";
/* 数组存储的是静态存储区中字符串常量的副本, 可随意修改  */

/* 指针表示法创建字符串 */
const char * pt1 = "Something is pointing at me.";
/* 指针存储的是静态存储区中字符串常量的首字符地址, 不可通过指针修改字该符串常量 */

在这里插入图片描述

字符串数组

#include<stdio.h>
#define SLEN 40
#define LIM 5

int main(void) { 
	const char *mytalents[LIM] = {
		"Adding numbers swiftly",
		"Multiplying accurately", "Stashing data",
		"Following instructions to the letter",
		"Understanding the C language"
	};
	char yourtalents[LIM][SLEN] = {
		"Walking in a straight line",
		"Sleeping", "Watching television",
		"Mailing letters", "Reading email"
	};
	int i;
	puts("Let's compare talents.");
	printf("%-36s  %-25s\n", "My Talents", "Your Talents");
	for(i = 0; i < LIM; i++)
		printf("%-36s  %-25s\n", mytalents[i], yourtalents[i]);
	printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",
		sizeof(mytalents), sizeof(yourtalents));
	return 0; 
} 
/* 
   使用一个下标时都分别表示一个字符串, 如 mytalents[0]和yourtalents[0]
   使用两个下标时都分别表示一个字符, 如 mytalents[1][2]和yourtalents[1][2]

   mytalents 是一个内含5个指针的数组, 在系统中共占用40字节。
   yourtalents 是一个内含5个数组的数组, 每个数组内含40个char类型的值,共占用200字节。
   
   mytalents 中的指针指向静态存储区中的字符串字常量的位置
   yourtalents 中的数组则储存着静态存储区中字符串字常量的副本 */

字符串输入输出

如果想把一个字符串读入程序,首先必须预留储存该字符串的空间, 然后用输入函数获取该字符串。 可以定义一个字符数组或使用C库函数分配内存空间。

gets() 和 puts()

gets() 和 puts() 常常搭配使用。由于gets()函数无法检查数组是否装得下输入行,因此使用它会造成安全隐患,C11标准委员会已经将其废除,尽量不要使用。

char *gets(char *str);
/* 从标准输入流(stdin)读取一行输入, 直到遇到换行符或文件结尾停止, 读取并丢弃遇到的换行符。
   然后将读取到的字符存入str指向的内存中, 并在末尾添加一个 \0 使其成为一个 C 字符串。
   成功读取则返回str, 读取出错或读取时文件为空, 则返回一个空指针(null pointer) */

int puts(const char *str);
/* 将str指向的字符串输出到标准输出流(stdout), 并额外输出一个换行符
   执行成功返回非负数。发生错误返回EOF */

char str[100];
printf("%d\n",puts(gets(str)));
#include <stdio.h>
#define STLEN 81
int main(void) {
    char words[STLEN];
    puts("Enter a string, please.");
    gets(words); //读取一行字符,丢弃换行符
    printf("Your string twice:\n");
    printf("%s\n", words);
    puts(words); // 输出字符串并额外输出一个换行符
    puts("Done.");
    return 0;
}

fgets() 和 fputs()

char *fgets(char *str, int n, FILE *stream) 
/* 从指定的流 stream 读取一行
   当读取(n-1)个字符时, 或者读取到换行符时(读取并保留), 或者到达文件末尾时, 停止读取。
   然后将读取到的字符存入str指向的内存中, 并在末尾添加一个 \0 使其成为一个 C 字符串
   成功读取返回 str, 读取出错或读取时文件为空,则返回一个空指针 null pointer
   
   fgets()会读取并保留换行符, 而gets()会读取并丢掉换行符 */
   
int fputs(const char * str, FILE * stream);
/* 将 str 指向的字符串输出到 stream 流
   成功执行返回一个非负值, 发生错误则返回 EOF(-1)
   
   fputs()不会额外输出一个换行符, 
   puts()会额外输出一个换行符 */

fgets()函数的第3个参数指明要读入的文件。 如果要读入从键盘输入的数据, 应以stdin(标准输入)作为参数。

fputs()函数的第2个参数指明要写入的文件。 如果要显示在显示器上, 应使用 stdout(标准输出) 作为该参数。

空字符和空指针

  • 都可以用数值0来表示
  • 空字符是整数类型, 空指针是指针类型
  • 空字符是一个字符, 占1字节
    空指针是一个地址, 占4字节
  • 空字符用于标记字符串结尾
  • 函数返回空指针表示某些特殊情况发生,如遇到文件结尾或未能按预期执行。

在这里插入图片描述

//读取多行输入行, 删除储存在字符串中的换行符, 如果没有换行符, 则丢弃一行内多余字符
#include <stdio.h>
#define STLEN 10
int main(void) {
    char words[STLEN];
    int i;
    puts("Enter strings (empty line to quit):");
    while(fgets(words, STLEN, stdin) != NULL && words[0] != '\n') {
        i = 0;
        while(words[i] != '\n' && words[i] != '\0') 
            i++;
        if (words[i] == '\n')
            words[i] = '\0';
        else // 如果word[i] == '\0'则执行这部分代码
            while (getchar() != '\n') 
            	continue;
        puts(words);
    }
    puts("done");
    return 0;
}
/* s_gets.c -- 
   读取一行输入行, 删除储存在字符串中的换行符, 如果没有换行符, 则丢弃一行内多余字符 */
char * s_gets(char * st, int n) {
	char * ret_val;
	int i = 0;
	ret_val = fgets(st, n, stdin);
	if (ret_val) // 即,ret_val != NULL
	{
		while(st[i] != '\n' && st[i] != '\0')
			i++;
		if(st[i] == '\n')
			st[i] = '\0';
		else
			while(getchar() != '\n')
				continue;
	}
	return ret_val;
}

gets_s()

char *gets_s( char *str, rsize_t n );

https://cloud.tencent.com/developer/section/1009156

自定义输入输出函数

void put1(const char * string) {
	while (*string)
		putchar(*string++);
}

char *get1(char *str, int length){ }


string.h 函数

C库提供了多个处理字符串的函数, ANSI C把这些函数的原型放在 string.h头文件中。

strlen() 求字符串长度

size_t strlen(const char *str);
/* 返回字符串的长度, 不包括空字符  */

strcat() 拼接字符串

char *strcat(char *dest, const char *src);
/* 把 src 指向的字符串追加到 dest 指向的字符串的末尾, 直到遇到空字符, 并在末尾添加 \0
   从dest 的 \0 的位置开始追加
   返回dest
   strcat()无法检查 dest 剩余内存是否能容纳src,有安全隐患 */

char *strncat(char *dest, const char *src, size_t n);
/* 把 src 指向的字符串追加到 dest 指向的字符串的末尾, 直到 n 个字符或遇到空字符为止, 并在末尾添加 \0 
   从dest 的 \0 的位置开始追加
   返回dest  */

strcmp() 比较字符串

int strcmp(const char *str1, const char *str2);
/* 把 str1 所指向的字符串和 str2 所指向的字符串进行比较
	  1. 如果返回值小于 0, 则表示 str1 小于 str2
	  2. 如果返回值大于 0, 则表示 str1 大于 str2
      3. 如果返回值等于 0, 则表示 str1 等于 str2 */
      
int strncmp(const char *str1, const char *str2, size_t n);
/* 把 str1 和 str2 进行比较,最多比较前 n 个字符 */

strcpy() 拷贝字符串

char *strcpy(char *dest, const char *src);
/* 把 src 指向的字符串复制到 dest, 并在末尾添加一个 \0, src 的内容将覆盖 dest 的内容
   返回 dest 
   不能检查 dest 指向的内存能否容纳 src , 有安全隐患 */

char *strncpy(char *dest, const char *src, size_t n)
/* 把 src 指向的字符串复制到 dest, 复制 n 个字符或遇到 \0 停止, 并在末尾添加 \0 
   返回dest */

strchr() 查找字符首次出现的位置

char *strchr(const char * str, int c); 
/* 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
   返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL */

strrchr() 查找字符最后一次出现的位置

char *strrchr(const char *str, int c);
/* 在 str 所指向的字符串中搜索最后一次出现字符 c 的位置
   返回 str 中最后一次出现字符 c 的位置, 如果未找到该值,则函数返回 NULL */

strpbrk() 公共字符第一次出现的位置

char *strpbrk(const char *str1, const char *str2);
/* 检索字符串 str1 中第一个匹配字符串 str2 中任意字符的字符, 不包含 \0
   返回 str1 中第一个匹配字符串 str2 中任意字符的字符位置, 如果未找到字符则返回 NULL */

strstr() 子字符串匹配

char *strstr(const char *haystack, const char *needle); 
/* 在字符串 haystack 中查找第一次出现字符串 needle 的位置, 不包含 \0
   返回在 haystack 中第一次出现 needle 字符串的位置, 如果未找到则返回 NULL */

字符串比较

1 两个字符串自左向右逐个按 ASCII 值大小相比
2 直到出现差异,ASCII 码大的字符串大
3 或者其中一个字符串先结束,先结束的字符串小

 "A"<"B" 
 "A"<"AB" 
 "Apple"<"Banana" 
 "A"<"a" 
 "compare"<"computer"

字符串排序

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

#define SIZE 81		/* 限制字符串长度,包括 \0 */
#define LIM 20		/* 可读入的最多行数 */
#define HALT "" 	/* 空字符串停止输入 */
void stsrt(char *strings[], int num);  /* 字符串排序函数 */
char * s_gets(char * st, int n);
int main(void)
{
	char input[LIM][SIZE]; /* 储存输入的数组   */
	char *ptstr[LIM];      /* 内含指针变量的数组 */
	int ct = 0;            /* 输入计数      */

	int k;  /* 输出计数 */
	printf("Input up to %d lines, and I will sort them.\n", LIM);
	printf("To stop, press the Enter key at a line's start.\n");
	while (ct < LIM && s_gets(input[ct], SIZE) != NULL && input[ct][0] != '\0')
	{
		ptstr[ct] = input[ct];  /* 设置指针指向字符串  */
		ct++;
	}
	stsrt(ptstr, ct);           /* 字符串排序函数  */
	puts("\nHere's the sorted list:\n");
	for (k = 0; k < ct; k++)
		puts(ptstr[k]);   /* 排序后的指针  */
	return 0;
}

/* 字符串-指针-排序函数 */
void stsrt(char *strings[], int num)
{
	char *temp;
	int top, seek;
	for (top = 0; top < num - 1; top++)
		for (seek = top + 1; seek < num; seek++)
			if (strcmp(strings[top], strings[seek]) > 0)
			{
				temp = strings[top];
				strings[top] = strings[seek];
				strings[seek] = temp;
			}
}
/* 读取字符串 */
char * s_gets(char * st, int n)
{
	char * ret_val;
	int i = 0;
	ret_val = fgets(st, n, stdin);
	if (ret_val)
	{
		while (st[i] != '\n' && st[i] != '\0')
			i++;
		if (st[i] == '\n')
			st[i] = '\0';
		else
			while (getchar() != '\n')
				continue;
	}
	return ret_val;
}
/* 输入:
O that I was where I would be,
Then would I be where I am not;
But where I am I must be,
And where I would be I can not.
*/

ctype.h 函数

在这里插入图片描述


命令行参数

/* repeat.c -- 把命令行参数转换为数字 */
#include <stdio.h>
#define STLEN 81
int main(int argc, char *argv []) {
    int count;
    printf("The command line has %d arguments:\n", argc - 1);
    for (count = 1; count < argc; count++) 
        printf("%d: %s\n", count, argv[count]);
    printf("\n");
    return 0;
}
//将程序编译成可执行文件 repeat.exe

命令行(command line) 是在命令行环境中, 用户为运行程序输入命令的行。Win10运行 repeat. exe 的命令行:
C:\Users\liwei\Desktop>repeat

命令行参数(command-line argument) 是同一行的附加项。 一个C程序可以读取并使用这些附加项:
C:\Users\liwei\Desktop>repeat Resistance is futile
The command line has 3 arguments:
1:  Resistance
2:  is
3:  futile
在这里插入图片描述

C编译器允许main()没有参数或者有两个参数(一些实现允许main()有更多参数, 属于对标准的扩展) 。

main()有两个参数时, 第1个参数是命令行中的字符串数量。 这个int类型的参数被称为argc(表示参数计数 argument count)。

系统用空格分隔字符串。 该程序把命令行字符串储存在内存中, 并把每个字符串的地址储存在指针数组中。 而该数组的地址则被储存在 main()的第 2 个参数中。 这个指向指针的指针称为argv(表示参数值 argument value)。 如果系统允许(一些操作系统不允许这样), 就把程序本身的名称赋给 argv[0], 然后把随后的第1个字符串赋给argv[1]。

许多环境(包括UNIX和DOS) 都允许用双引号把多个单词 括起来形成一个参数。 C:\Users\liwei\Desktop>repeat " I am hungry " now
这行命令把字符串 “I am hungry” 赋给argv[1],把 “now” 赋给argv[2]。


将字符串转换为数字

printf()通过转换说明, 把数字从数值形式转换为字符串形式
scanf()通过转换说明,把字符串转换为数值形式

sprintf() 和 sscanf()

int sprintf(char *str, const char *format, ...);
/* 格式化输出到字符串 str, 格式说明与printf() 一样 */

int sscanf(const char *str, const char *format, ...);
/* 从字符串 str 读取格式化输入, 格式说明与scanf()一样 */

// 位于<stdio.h>
#include<stdio.h>

int main(void) {
    char str1[50]="";
    sprintf(str1, "PI 的值 = %f", 3.1415926);
    puts(str1);

    int n;
    char str2[50]="9527";
    sscanf(str2,"%d",&n);
    printf("n = %d",n);
    return 0;
}

atoi() aotl() aotf()

int atoi(const char *str);
/* 把参数 str 所指向的字符串转换为一个整数(类型为 int 型) 
   返回转换后的双精度浮点数, 如果没有执行有效的转换, 则返回零 */

long int atol(const char *str);
/* 把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)
   返回转换后的双精度浮点数, 如果没有执行有效的转换,则返回零 */

double atof(const char *str);
/* 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)
   返回转换后的双精度浮点数, 如果没有执行有效的转换,则返回零(0.0) */

// 这三个函数处理以数字或小数点(strtod().)的字符串, 如果字符串以其它字符开头, 返回0/0.0

// 位于<stdlib.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    puts("atoi() -----------------------------------");
    int val1;
    char str1[20]="93489";
    val1 = atoi(str1);
    printf("字符串值 = %s, 整型值 = %d\n",str1, val1);

    puts("atol() -----------------------------------");
    long val2;
    char str2[20]="93489ABCD";
    val2 = atol(str2);
    printf("字符串值 = %s, 长整型值 = %ld\n", str2, val2);

    puts("atof() -----------------------------------");
    float val3;
    char str3[20]="93.489=&-";
    val3 = atof(str3);
    printf("字符串值 = %s, 浮点值 = %f\n", str3, val3);
    return 0;
}

在这里插入图片描述

strtol() strtoul() strtod()

long int strtol(const char *str, char **endptr, int base); 
/* 把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型)
   endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中读取完数值后的下一个字符
   base -- 基数, 必须介于 2 和 36(包含)之间,或者是特殊值 0。
   返回转换后的长整数, 如果没有执行有效的转换,则返回零 */

unsigned long int strtoul(const char *str, char **endptr, int base); 
/* 把参数 str 所指向的字符串根据给定的 base 转换为一个无符号长整数(类型为 unsigned long int 型)
   endptr -- 对类型为 char* 的对象的引用, 其值由函数设置为 str 中读取完数值后的下一个字符
   base 必须介于 2 和 36(包含)之间, 或者是特殊值 0
   返回转换后的无符号长整数, 如果没有执行有效的转换,则返回一个零 */

double strtod(const char *str, char **endptr); 
/* 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)
   endptr -- 对类型为 char* 的对象的引用, 其值由函数设置为 str 中读取完数值后的下一个字符
   返回转换后的双精度浮点数,如果没有执行有效的转换,则返回零(0.0) */

// 位于<stdlib.h>
#include<stdio.h>
#include <stdlib.h>

int main(void) {
	puts("strtol() -----------------------");
	char str1[30] = "20221130 This is test";
	char *ptr1;
	long ret1;
	ret1 = strtol(str1, &ptr1, 10);
	printf("数字(无符号长整数)是 %ld\n", ret1);
	printf("字符串部分是 |%s|\n", ptr1);

	puts("strtoul() ----------------------");
	char str2[30] = "20221130 This is test";
	char *ptr2;
	long ret2;
	ret2 = strtoul(str2, &ptr2, 10);
	printf("数字(无符号长整数)是 %lu\n", ret2);
	printf("字符串部分是 |%s|\n", ptr2);

	puts("strtod() -----------------------");
	char str3[30] = "20.221130 This is test";
	char *ptr3;
	double ret3;
	ret3 = strtod(str3, &ptr3);
	printf("数字(double)是 %f\n", ret3);
	printf("字符串部分是 |%s|\n", ptr3);
	return(0);
}

在这里插入图片描述

把命令行参数转换为数字

/* repeat.c -- 把命令行参数转换为数字 */ 
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv []) {
    int i, times;
    if (argc < 2 || (times = atoi(argv[1])) < 1)
        printf("Usage: %s positive-number\n", argv[0]);
    else for (i = 0; i < times; i++)
            puts("Hello, good looking!");
    return 0;
}

C:\Users\liwei\Desktop>repeat 3
在这里插入图片描述

itoa() ftoa()

许多实现使用 itoa()和 ftoa()函数分别把整数和浮点数转换成字符串。 但是这两个函数并不是 C标准库的成员, 可以用sprintf()函数代替它们, 因为sprintf()的兼容性更好。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值