字符串输入输出函数

字符串输入

字符串输入首先要考虑的是存储位置和存储空间大小。
例子:char *name; scanf("%s", name); 指针未初始化,可能指向内存中的任意地方,字符串读入的时候有可能覆盖内存中的关键数据,造成程序或机器崩溃。
char name[81]; 这种方式显式声明空间并进行初始化。

gets()函数

scanf("%s", str)函数读入单个word。而gets()可以读取一整行的字符串。它通过换行符读取整行,丢弃换行符,存储剩余的字符,添加空字符('\0')以创建C字符串。一般和puts()搭配使用。
示例:

#include<stdio.h>
#define STLEM 81
int main(void)
{
    char words[STLEM];

    puts("Enter a string, please.");
    gets(words);
    printf("Your string twice:\n");
    printf("%s\n", words);
    puts(words);
    puts("Done.");

    return 0;
}

⚠️:gets函数的问题(int puts(const char *);):虽然在定义字符串的时候声明了大小为81,但gets函数无法阻止输入超过大小的字符串长度,编译运行时将报错"warning: this program uses gets(), which is unsafe."
gets函数只接收一个参数,char类型的指针。没有其他参数限定用户输入的字符串长度。gets()只知道数组的开始位置,却不知道数组的长度。
如果输入字符串太长,则会出现缓冲区溢出,这意味着多余的字符溢出了指定的目标。 额外的字符可能只是进入未使用的内存并且没有立即出现问题,或者它们可能会覆盖程序中的其他数据,但这些肯定不是唯一的可能性。
C99已经不建议使用该函数,并建议将gets()从标准库中移除,C11已经将gets()从标准中移除。但C的标准只是要求编译器厂商必须符合所要求的标准,而不是限制编译器厂商不能做什么。所以,主流的编译器依然支持gets()函数已确保向后兼容。

gets()的替代品fgets()

为了解决gets()函数容易引起的缓冲区溢出问题,出现了fgets()函数。char fgets(char __restrict, int, FILE *);
fgets()与gets的主要不同点为:

  • fgets()有第二个参数,是一个int类型,限定输入的字符串最大长度。
  • fgets()读取换行符('\n'),并保存在字符串中,而不是像gets(),丢弃换行符。
  • fgets()有第三个参数,默认为文件输入。也可以读取键盘输入,参数为stdin。

示例:

#include<stdio.h>
#define STLEN 14
int main(void)
{
    char words[STLEN];

    puts("Enter a string, please.");
    fgets(words, STLEN, stdin);
    printf("Your string twice (puts(), then fputs()):\n");
    puts(words);
    fputs(words, stdout);
    puts("Enter another string, please.");
    fgets(words, STLEN, stdin);
    printf("Your string twice (puts(), then fputs()):\n");
    puts(words);
    fputs(words, stdout);
    puts("Done.");

    return 0;
}

输出:
Enter a string, please.
apple pie
Your string twice (puts(), then fputs()):
apple pie

apple pie
Enter another string, please.
strawberry shortcake
Your string twice (puts(), then fputs()):
strawberry sh
strawberry shDone.

解读:

  • 首先输入"apple pie",字符串长度小于14,使用fgets()时会保留换行符。使用puts()输出时,会额外添加一个换行符(因为gets()丢弃了换行符),则puts()会输入"\n\0\n"。fputs()会按words实际保存的进行输出。因此,两个"apple pie"之间的空行就是puts()函数额外添加的。
  • 其次输入"strawberry shortcake",字符串长度超过14,则仅保留13个有效当前输入(额外一个留给'\0')。故使用puts()时输出"strawberry sh\0\n"('\n'为额外添加)。而使用fputs()输出时,不会添加'\n',故直接输出"strawberry sh",紧接着是"Done."。

fgets()函数的返回值为指向char类型的指针。指针指向输入的字符串地址。但是,如果函数遇到文件结尾,则返回一个称为空指针的特殊指针。这是一个确保不指向有效数据的指针,因此它可用于指示特殊情况。在代码中,该指针可以表示为数字0,或者在C中更通常的表示方法为宏NULL。

fgets()的输入问题

由于fgets()会保存换行符,这就带来了一些问题。有时,你不想保存该换行符,而仅仅将用户输入的换行符作为buffered I/O的一个指令,通知临时存储区的数据可以被fgets()函数使用。
如何丢弃换行符?

while (words[i] != '\n')
    i++;
words[i] = '\0';

words为通过fgets()得到的输入字符串,将换行符替换为'\0'。

如果想将超过数组定义大小的剩余字符串也丢弃掉,以免作为下次的输入,如何做到呢?

while (getchar() != '\n')
    continue;

Null字符和NULL指针

NULL字符或'\0',用来标记字符串的结束。代码为0的字符,ASCII 为0的字符。
空指针或NULL具有与有效数据地址不对应的值。 它通常由函数使用,否则返回有效地址以指示某些特殊情况,例如遇到文件结束或未能按预期执行。
另外,空字符是int类型,而NULL指针是指针类型。

gets_s()函数

C11引入了一个新函数gets_s(),类似fgets(),但和fgets也有不同之处。

  • gets_s()只读取标准输入,因此不需要第三个参数如stdin。
  • 如果get_s()读取到换行符,则丢弃换行符,而不是和fgets()一样,保留它。
  • 如果gets_s()读取最大字符数并且无法读取换行符,则需要几个步骤。 它将目标数组的第一个字符设置为空字符。 它会读取并丢弃后续输入,直到遇到换行符或文件结尾。 它返回空指针。 它调用依赖于实现的“处理程序”函数(或者选择的函数),这可能导致程序退出或中止。

轮子

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;
}

s_gets()函数为自定义函数,可以丢弃fgets()函数保留的换行符,同时丢弃超过最大字符数的剩余输入。

scanf()函数

一般我们使用scanf()函数和%s格式读取字符串。scanf()更像是读取一个单词,而不是一个字符串。
示例:

Input Statement Original Input Queue* Name Contents Remaining Quenue
scanf("%s', name); Fleebert#Hup Fleebert #Hup
scanf("%5s", name); Fleebert#Hup Fleeb ert#Hup
scanf("%5s", name); Ann#Ular Ann #Ular

‘#’代表space character(/t, /b等)。
可以到,scanf会在遇到空白字符或最大字符时读取结束,其他字符将再下次输入时读取。scanf的返回值为成功读取到的字符串个数。

字符串输出

猜你喜欢

转载自www.cnblogs.com/jeffrey-yang/p/10224803.html