13.3 字符串的读/写
使用printf函数或puts函数来编写字符串是很容易的。读入字符串却有点麻烦,主要是因为输入的字符串可能比用来存储的字符串变量更长。为了一次性读入字符串,可以使用scanf函数或gets函数,也可以每次一个字符的方式读入字符串。
13.3.1 用printf函数和puts函数写字符串
转换说明%s允许printf函数写字符串。参考下面的例子:
char str[] = "Are we having fun yet?"
printf("Value of str: %s\n", str);
输出会是
Value of str: Are we having fun yet?
printf函数会逐个写字符串中的字符直到遇到空字符才停止。(如果空字符丢失,printf函数会越过字符串的末尾继续写,直到最终在内存的某个地方找到空字符为止。)
如果只显示字符串的一部分,可以使用转换说明%.ps。这里的p是要显示的字符数量。语句
printf("%.6s\n", str);
会显示出
Are we
就像数一样,字符串可以在指定域内显示。转换说明%ms会在大小为m的域内显示字符串。(对于超过m个字符的字符串,printf函数会显示出整个字符串,而不会截断。)如果字符串少于m个字符,则会在域内右对齐输出。为了强制左对齐,可以在m前加一个减号。m值和p值可以组合使用:转换说明%m.ps会使字符串的前p个字符在大小为m的域内显示。
printf函数不是唯一一个字符串输出函数。C函数库还提供puts函数。此函数可以按如下方式使用:
puts(str);
puts函数只有一个参数,此参数就是需要显示的字符串,参数中没有格式串。在写完字符串后,puts函数总会添加一个额外的换行符,因此显示会移至下一输出行的开始处。
13.3.2 用scanf函数 和gets函数读字符串
转换说明%s允许scanf函数读入字符串:
scanf("%s", str);
在scanf函数调用中,不需要在str前添加运算符&。因为str是数组名,编译器会自动把它当作指针来处理。
调用时,scanf函数会跳过空白字符,然后读入字符,并且把读入的字符存储到str中,直到遇到空白字符为止。scanf函数始终会在字符串末尾存储一个空字符。
用scanf函数读入字符串永远不会包含空白字符。因此,scanf函数通常不会读入一整行输入。换行符会使scanf函数停止读入,空格符或制表符也会产生同样的结果。为了每次读入一整行输入,可以使用gets函数。类似于scanf函数,gets函数把读入的字符放到数组中,然后存储一个空字符。然而,在其他方面gets函数有些不同于scanf函数:
l gets函数不会在开始读字符串之前跳过空白字符(scanf函数会跳过)。
l gets函数会持续读入直到找到换行符才停止(scanf函数会在任意空白字符处停止)。此外,gets函数会忽略掉换行符,而不会把它存储到数组中,用空字符代替换行符。
为了领会scanf函数与puts函数之间的差异,请考虑下面的程序段:
char sentence[SENT_LEN+1];
printf("Enter a sentence: \n");
scanf("%s", sentence);
假定在提示信息后
Enter a sentence:
用户输入信息
To C, or not to C: that is the question.
scanf函数会把字符串"To"存储到sentence中。下一次scanf函数调用将从单词To后面的空格处继续读入这行。
现在假设用gets函数替换掉scanf函数:
gets(sentence);
当用户输入和先前相同的信息时,gets函数会把字符串
"To C, or not to C: that is the question."
存储到sentence中。
|
|
在把字符读入数组时,scanf函数和gets函数都无法检测何时填满数组。因此,它们可能越过数组的边界存储字符,这会导致程序行为异常。通过用转换说明%ns代替%s可以使scanf函数更安全。这里的数字n指出可以存储的最大字符的数量。可惜的是,gets函数天生就是不安全的。fgets函数( |
关于gets函数和puts函数的最后一点提示:既然这些函数比scanf函数和printf函数简单,因此通常运行也就更快。
13.3.3 逐个字符读字符串
对许多程序而言,因为scanf函数和gets函数都有风险且不够灵活,C程序员经常会编写自己的输入函数。通过每次一个字符的方式来读入字符串,这类函数可以提供比标准输入函数更大程度的控制。
如果决定设计自己的输入函数,那么就需要考虑下面这些问题:
l 在开始存储字符串之前,函数应该跳过空白字符吗?
l 什么字符会导致函数停止读取:换行符、任意空白字符、还是其他一些字符?需要存储这类字符还是忽略掉?
l 如果输入的字符串太长以致无法存储,那么程序应该做些什么:忽略额外的字符,还是把它们留给下一次的输入操作?
假定需要的函数不会跳过空白字符,在第一个换行符处(不把换行符存储到字符串中)停止读取,并且忽略额外的字符。函数将有如下原型:
int read_line(char str[], int n);
str表示用来存储输入的数组,而且n是最大读入字符的数量。如果输入行包含多于n个的字符,read_line函数将忽略多余的字符。read_line函数会返回实际存储在str中的字符数量(在0到n之间的任意数)。不可能总是需要read_line函数的返回值,但是有这个返回值也没问题。
read_line函数主要由一个循环构成,只要str留有空间,那么此循环就逐个读入字符并且把它们存储起来。在读入换行符时循环终业。(严格地说,如果getchar函数读入字符失败,也应该终止循环,但是这里暂时忽略这种复杂情况。)
下面是read_line函数的完整定义:
int read_line(char str[], int n)
{
char ch;
int i = 0;
while ((ch = getchar()) ! = '\n')
if (i < n)
str[i++] = ch;
str[i] = '\0' /* terminates string */
return i; /* number of characters stored */
}
返回之前,read_line函数在字符串的末尾放置了一个空字符。就像scanf函数和gets函数一样,标准函数会自动在输入字符串的末尾放置一个空字符。然而,如果你正在写自己的输入函数,那么必须要考虑这一点。






