字符串
11.1 表示字符串和字符串I/O
11.1.1 在程序中定义字符串
1.字符串字面量(字符串常量)
用双引号括起来的内容称字符串字面量(string literal),叫作字符串常量(string constant)。
:如果要在字符串内部使用双引号,必须在双引号前面加上一个反斜杠(\)
字符串常量属于静态存储类别
#include <stdio.h>
int main(){
printf("%s,%p,%c\n","We","are",*"space farers");
return 0;
}
输出结果如下:
*"space farers"表示该字符串所指向地址上存储的值,应该是字符串的首字符
2.字符串数组和初始化
定义字符串,必须让编译器知道需要多少空间 在指定数组大小时,要确数组的元素个数至少比字符串长度多1(容纳空字符)
0是指char形式的空字符,不是数字字符0
声明数组时必须是可求值的整数
字符数组名是该数组首元素的地址
3.数组和指针
字符串储存在静态存储区
const char ar1[ ] = “Something is pointing at me”
ar1是地址常量,不能更改ar1 可以进行ar1 + 1,但不允许进行++ar1的操作
:递增运算符只能在变量名前
#define MSG "I'm special"
#include <stdio.h>
int main(){
char ar[] = MSG;
const char *pt = MSG;
printf("address of \"I'm special\":%p \n","I'm special");
printf(" address ar:%p\n",ar);
printf(" address pt:%p\n",pt);
printf(" address of MSG:%p\n",MSG);
printf("address of \"I'm special\":%p \n","I'm special");
return 0;
}
输出结果如下:
4.数组和指针的区别
char heart[ ] = “I LOVE YOU”
const cahr *head = “I HATE YOU”
两者主要区别是:数组名heart是常量 指针名head是变量
数组的元素是变量,但是数组名不是变量
建议把指针初始化为字符串字面量时使用const限定符
如果不修改字符串,不要用指针指向字符串字面量
5.字符串数组
上方是数组 下方是指针
如果要改变字符串或为字符串输入预留空间,不要使用指向字符串字面量的指针
11.2 字符串输入
11.2.1 分配空间
最简单的方法是:在声明时显式指明数组的大小
11.2.2 gets( )
gets()简单易用,它读取整行输入,直至遇到换行符,然后丢弃换行符,储存其余字符,并在这些字符的末尾添加一个空字符使其成为一个C字符串。它经常和 puts()函数配对使用,该函数用于显示字符串,并在末尾添加换行符。
11.2.3 gets( )的替代品
1.fgets( ) fputs( )
- fgets( )的第2个参数指明了读入字符的最大数量。如果该参数的值是n,那么fgets( ) 将读入n-1 个字符,或者读到遇到的第一个换行符为止。
- 如果fgets( )读到一个换行符,会把它储存在字符串中。这点与gets( )不同,gets()会丢弃换行符。
- fgets( )两数的第3个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin(标准输入)作为参数,该标识符定义在stdio.h中。
当puts( )显示该字符串时在末尾添加了换行符 而fputs( )不会这样做
- 如何用fgets( )处理掉换行符?
一个方法是在已储存的字符串中查找换行符 并将其替换成空字符
while(words[i] != '\n')
i++;
words[i] = '\0';
- 如果仍有字符串留在输入行怎么办?
如果目标数组装不下一整行输入,丢弃那些多余字符
while(getchar() != '\n'
continue;
2.gets_s()
- gets_s( )只从标准输入中读取数据,所以不需要第3个参数。
- 如果gets_s()读到换行符,会丢弃它而不是储存它。
- 如果gets_s( )读到最大字符数都没有读到换行符,会执行以下几步.首先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直至读到换行符或文件结尾,然后返回空指针。接着,调用依赖实现的 “处理函数”(或你选择的其他函数),可能会中止或退出程序。
11.2.4 scanf( )函数
scanf( )更像是“获取单词”含糊 而不是获取字符串
scanf( ) 函数返回一个整数值 该值等于scanf( ) 成功读取的项数或EOF(读到文件结尾时返回EOF)
11.3 字符串输出
put( )、fputs( ) 、printf( )
11.3.1 puts函数
- puts在显示字符串时会自动在末尾添加换行符
- puts在遇到空字符时输出停止 所以必须确保有空字符
11.3.2 fputs( )函数
- fputs( )末尾部位添加换行符
- 第2个参数指明要写入数据的文件
11.3.3 printf( )函数
-
printf( ) 不会自动在每个字符串末尾加上一个换行符 因此必须指明在哪里使用换行符
-
printf(“%s\n”,string)与puts(string)效果相同
11.4自定义输入/输出函数
- 需求:类似puts( )但不会自动添加换行符的函数
#include <stdio.h>
void put1(const char *string){
while(*string != '\0')
putchar(*string++);
}
++优先级高于*
注意:while(string)
当string指向空字符时,*string的值是0,即测试条件为假,while循环结束
- 用户自定义输出函数:
#include <stdio.h>
void put1(const char*);
int put2(const char *);
int main(){
put1("If I'd as much money");
put1(" AS I could spend\n");
printf("I count %d characters.\n",
put2("I never would cry old chairs to mend"));
return 0;
}
void put1(const char *string){
while (*string)
putchar(*string++);
}
int put2(const char *string){
int count = 0;
while(*string){
putchar(*string ++);
count++;
}
putchar('\n');
return(count);
}
11.5 字符串函数
11.5.1 strlen()函数
用于统计字符串的长度
下面的函数可以缩短字符串的长度
void fit(char *string,unsigned int size){
if(strlen(string) > size)
string[size] = '\0';
string.h头文件包含了C字符串系列原型
11.5.2 strcat( )函数
strcat ( )(用于拼接字符串)接受两个字符串作为参数
该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。
strcat()函数的类型是 char * (即指向 char 的指針)
strcat ( ) 函数返回第1个参数,即拼接第2个字符串后的第1个字符串的地址。
#include <stdio.h>
#include <string.h>
#define SIZE 80
char * s_gets(char * st,int n);
int main(void){
char flower[SIZE];
char addon [] = "s smell like old shoes";
puts("What is your favorite flower?");
if(s_gets(flower,SIZE)){
strcat(flower,addon);
puts(flower);
puts(addon);
}else
puts("End of file encountered!");
puts("bye");
return 0;
}
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;
}
11.5.3 strncat( )函数
strncat( )函数的第三个参数指定了最大添加字符数
11.5.4 strcmp( )函数
通过比较运算符来比较字符串 就像比较数字一样 如果字符串参数相同就返回0 否则返回非0值
#include <string.h>
#include <stdio.h>
#define LISTSIZE 6
int main(){
const char * list[LISTSIZE] =
{"astronomy","astounding","astrophysics","ostracize","asterism","astrophobia"};
int count = 0;
for (int i = 0; i < LISTSIZE; i++) {
if(strncmp(list[i],"astro",5) == 0){
printf("Found :%s\n",list[i]);
count ++;
}
}
printf("The list contained %d words beginning"
"with astro.\n",count);
return 0;
}
输出结果如下:
11.5.5 strcpy( ) 、strncoy( )函数
如果pts1和pts2都是指向字符串的指针 那么拷贝的是字符串的地址而不是字符串本身
pts1 = pts2;
如果希望拷贝整个字符串,要是用strcpy( )函数
strcpy( )的属性
- 返回类型是char *,该函数返回的是第一个参数的值,即一个字符的地址
- 第1个参数不必指向数组的开始。这个属性可以用来拷贝数组的一部分
二、更谨慎的选择:strncpy( )
- strncpy( )的第三个参数指明可拷贝的最大字符数
11.5.6 sprintf() 函数
- sprintf( )两数声明在stdio.h中,而不是在string.h中。
- sprintf( )的第1个参数是目标字符串的地址。其余参数和 printf( )相同,即格式字符串和待写入项的列表。
11.6字符串示例:字符串排序
#include <stdio.h>
#include <string.h>
#define SIZE 81 //限制字符串长度
#define LIM 20 /* 可读入的最多行数 */
#define HALT "" /* 空字符停止输入*/
char * s_gets(char *st,int n); /*字符串排序函数*/
void stsrt(char *strings [],int num);
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 is 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;
}
11.6.1排序指针而非字符串
11.7 ctype.h字符函数和字符串
include <string.h>
#include <stdio.h>
#include <ctype.h>
#define LIMIT 81
void ToUpper(char *);
int PunctCount(const char *);
int main(){
char line[LIMIT];
char *find;
puts("Please enter a line: ");
fgets(line,LIMIT,stdin);
find = strchr(line,'\n');
if(find)
*find = '\0';
ToUpper(line);
puts(line);
printf("That line has %d punctuation characters\n", PunctCount(line));
return 0;
}
void ToUpper(char *str){
while (*str){
*str = toupper(*str);
str++;
}
}
int PunctCount(const char *str){
int ct =0;
while (*str){
if(ispunct(*str))
ct++;
str++;
}
return ct;
}
输出结果如下:
11.9 字符换转换成数字
- 转换十进制与十六进制
#include <stdio.h>
#include <stdlib.h>
#define LIM 30
char * s_gets(char *st,int n);
int main(){
char number[LIM];
char * end;
long value;
puts("Enter a number (empty to quit):");
while (s_gets(number,LIM) && number[0] !='\0'){
value = strtol(number,&end,10);//十进制
printf("base 10 input,base 10 output:%ld,stopped at %s(%d)\n",value,end,*end);
value = strtol(number,&end,16);//十六进制
printf("base 16 input,base 10 output:%ld,stopped at %s(%d)\n",value,end,*end);
puts("Next number:");
} puts("Bye!\n");
return 0;
}
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;
}
strtol( )函数最多可以转换36进制 a~z都可以作数字
11.10 关键概念
字符串,无论是由字符数组、指针还是字符串常量标识,都储存为包含字符编码的一系列字节,并以空字符串结尾。C 提供库两数处理字符串,查找字符串并分析它们。尤其要牢记,应该使用 strcmp( ) 来代替符,当比较字符串时,应该使用strcpy( )或strncpy( )代替赋值运算符把字符串赋给字符数组。