C语言:字符(串)的输入和输出

目录

  • 1. 文件类型FILE及其指针FILE *
  • 2. 字符的输入与输出
  • 3. 字符串的输入和输出
  • 4. 格式化输入和输出
  • 5. 命令行重定向输入输出流

1. 文件类型FILE及其指针FILE *

C语言程序把输入和输出看作是字节流,对于面向文本的程序,每个字节代表一个字符。文件类型type FILE对象包含控制流的信息1:指向缓存的指针、位置指示器(缓存中当前字符位置)和状态指示器(是否正在读或写文件、是否出现错误或EOF等等)。包括头文件后,自动创建三个文件类型FILE的对象,相应指针FILE *分别是stdin(标准输入流standard input stream)、stdout(标准输出流standard output stream)和stderr(标准错误流standard error stream),都是常量constant,无法直接赋值。通常,stdin与键盘相连,stdoutstderr与屏幕相连,但是也可以把它们重定向到文件或者管道(见下文重定向标准输入和输出流)。

FILE * fopen (const char * filename, const char * mode);
FILE * freopen (const char * filename, const char * mode, FILE * stream);
int fclose (FILE * stream);

除了程序开始时自动创建的三个文件类型对象,也可以使用头文件中的fopen函数创建需要的文件类型对象。根据fopen函数原型,其第一个参数是文件名,第二个参数是文件访问模式,返回值是一个指向文件类型对象的指针(空指针NULL代表打开失败)。

C string 模式
“r”
“w”
“a” 附加
“r+” 读和更新
“w+” 写和更新
“a+” 附加和更新

fopen函数按照文件访问模式打开文件,并将该文件与一个流联系起来。在未来的操作中,该流可以通过返回的文件类型指针来辨认。流充当着程序端与文件端之间的媒介,连接二者。文件端可以是文件,也可以是装置,比如键盘和屏幕。相应地,可以通过fclose函数关闭与传入文件类型指针相关联的文件,并将流与文件和所有相关联的缓存分离,清空所有缓存。分离后,该流FILE *还可以通过freopen函数重新被使用去打开(其他)文件或者改变文件访问模式。代码示例如下:

#include 

int main(int argc, char const *argv[]) {
    FILE * pfile = fopen("1.txt", "r");     // open file 1.txt
    if (pfile != NULL) {
        printf("fopen succeeded.\n");
    } else {
        printf("fopen failed.\n");
    }

    int n = 13;
    char a[n];
    fgets(a, n, pfile); // read n-1 characters from pfile stream
    // 1.txt only contains one line: hello, world\n
    printf("%s\n", a);
    fclose(pfile);      // close file 1.txt
    
    freopen("1.txt", "a", stdout); 	// redirect stdout to file 1.txt
                                    // and in append mode
    printf("This sentence is redirected to a file.\n");
    // append to 1.txt, instead of display
    fclose(stdout);
    return 0;
}

Shell命令和输出

user@Laptop C I/O % clang freopen.c
user@Laptop C I/O % cat 1.txt
hello, world
user@Laptop C I/O % ./a.out
fopen succeeded.
hello, world
user@Laptop C I/O % cat 1.txt
hello, world
This sentence is redirected to a file.

2. 字符的输入与输出

字符的输入和输出有如下(代码片段示例)三种2,其中getcharputchar默认使用stdinstdout,具体实现方式可能是函数也可能是宏指令(macro);其余两种增加了一个参数FILE * stream,可以传入具体想利用的输入输出流,二者基本等价,除了getcputc也可能像getcharputchar一样是宏指令。如果出现错误,返回EOFend of file)。EOF是一个宏定义的int类型的负整常数(符号常量),一般为-1,除了真实的文件末尾,还可以通过键盘模拟,比如DOS和Windows使用Ctrl+Z和UNIX使用Ctrl+D

#include 	// in C
#include 	// in C++

// use stdin and stdout
int getchar(void);
int putchar(int character);

// pass in stream to read from and write to
int getc(FILE * stream);
int putc(int character, FILE * stream);
int fgetc(FILE * stream);
int fputc(int character, FILE * stream);

3. 字符串的输入和输出

// use stdin and stdout
char * gets(char * str);
int puts(const char * str);

// specify stream to read from and write to
char * fgets(char * str, int num, FILE * stream);
int fputs(const char * str, FILE * stream);

字符串的输入和输出有以上两种:

  1. 输入函数返回char *指针,返回NULL表示出现错误;
  2. 输出函数返回int型整数,非负代表成功,EOF表示出错;
  3. 第二种比第一种多一个参数流FILE *
  4. fgets还比gets多一个整型int参数,表示最多读取num-1个字符,末尾再添加无效字符'\0'
  5. gets没有指明字符串大小,可能导致缓存溢出,是不安全的;
  6. gets遇到换行符(newline character)或者EOF停止,无论哪个先发生;
  7. fgets遇到换行符'\n'(newline character)、EOF或者已经读了num-1个字符停止,无论哪个先发生;
  8. gets不包括末尾换行符,puts添加一个换行符,而fgetsfputs相反。
#include 
#include 

int main(int argc, char const *argv[]) {
    int n = 16;
    char a[n];
    gets(a);    // keyboard input: hello, world\n
    printf("strlen(a): %lu\n", strlen(a)); // without ending '\n'
    puts(a);    // add '\n' at end
    printf("%s", a);    // without '\n' at end
    printf("newline\n");
    return 0;
}

Shell命令和输出:C警告gets不安全

user@Laptop C I/O % clang -std=c11 -Wall ctest.c
user@Laptop C I/O % ./a.out               
warning: this program uses gets(), which is unsafe.
hello, world				// keyboard input
strlen(a): 12
hello, world
hello, worldnewline
#include 
#include 

int main(int argc, char const *argv[]) {
    FILE * pfileI = fopen("1.txt", "r");
    // 1.txt contains only one line: hello, world\n
    FILE * pfileO = fopen("2.txt", "w");
    int n = 16;
    char a[n];
    fgets(a, n, pfileI);
    printf("strlen(a): %lu\n", strlen(a)); // with ending '\n'
    fputs(a, pfileO);
    printf("%s", a);    // with ending '\n'
    printf("newline\n");
    return 0;
}

输出(getsputs

strlen(a): 13
hello, world
newline

4. 格式化输入和输出

三种格式化输入和输出对应三种输入源和输出目的地:

  1. 标准输入和输出流
  2. 字符串
  3. FILE *
#include   // in C
#include    // in C++

// read from stdin and write to stdout
int scanf(const char * format, arg1, arg2, ...);
int printf(const char * format, arg1, arg2, ...);

// read from str and write to str
int sscanf(const char * str, const char * format, arg1, arg2, ...);
int sprintf(char * str, const char * format, arg1, arg2, ...);

// read from stream and write to stream
int fscanf(FILE * stream, const char * format, arg1, arg2, ...);
int fprintf(FILE * stream, const char * format, arg1, arg2, ...);

格式化输入函数按照format的所指示的规格解读从输入源读取的字符,并将其写入剩余参数arg1, arg2, ...(都是指针,即地址),返回成功匹配并赋值的项目个数。格式化输出函数在format的控制下转化、格式化并向输出目的地输出剩余参数,返回打印的字符个数。

格式化输入函数的格式format字符串可以分为三种字符:

  1. 空白字符(whitespace character):空格符、制表符和换行符(space、tab、newline)。读取并忽略空白字符,直到遇到下一个非空白字符。如果流中有多个连续的空白字符,格式中有一个空白字符即可。
  2. 格式说明符,以百分号%开头。
  3. 除了格式说明符外的非空白字符,需要与流中字符匹配,然后丢弃,继续读取字符。

与格式化输入函数的格式参数相比,格式化输出函数的格式字符串只分为两种字符:

  1. 格式说明符,以百分号%开头。
  2. 非格式说明符,原样输出。
#include 

int main(int argc, char const *argv[]) {
    /* code */
    freopen("1.txt", "r", stdin);
    int a = 0;
    int b = 0;
    int c = 0;
    scanf("%d%d %d", &a, &b, &c);
    printf("%d\n", a - b - c);
    int year = 0;
    int month = 0;
    int day = 0;
    scanf("%d%d-%d", &year, &month, &day);
    printf("%d:%d:%d\n", year, month, day);
    return 0;
}

文本文件"1.txt"

1
        2       // preceding tabs
  3             // preceding spaces
2021-02-15

输出

-4
2021:-2:15

当输入格式说明符为%s(输入字符串)时,遇到第一个空白字符停止(无论字符数组的大小),并在末尾添加无效字符'\0'。(代码示例见5. 命令行重定向输入输出流

5. 命令行重定向输入输出流

在Shell命令行,使用./a.out < input.txt > output.txt,意思是将input.txt作为输入源,将output.txt作为输出目的地。

#include 
#include 

int main(int argc, char const *argv[]) {
    /* code */
    int n = 5;
    char a[n];
    scanf("%s", a);
    printf("%s\n", a);
    printf("strlen(a): %lu\n", strlen(a));
    return 0;
}

Shell命令

user@Laptop C I/O % clang -std=c11 -Wall clt_redirection.c 
user@Laptop C I/O % ./a.out < 1.txt > 2.txt

文本文件"1.txt"

hello, world

文本文件"2.txt"(初始为空)

hello,
strlen(a): 6

字符数组大小是5个字符,但是实际读取了6个字符strlen(a): 6,实际需要字符数组大小为7(加上末尾的无效字符)。


  1. www.cplusplus.com ↩︎

  2. The C Programming Language 2nd Edition by K & R (ANSI C) ↩︎

你可能感兴趣的:(编程,计算机科学,c++,编程语言,字符串,c语言,c++)