标准IO
1. 基本概念
Linux 标准 IO 是指在 Linux 操作系统中用于处理输入和输出的三个流:标准输入流(stdin)、标准输出流(stdout)和标准错误流(stderr)。
-
标准输入流(stdin)通常从键盘读取输入,并将其发送到程序。
-
标准输出流(stdout)通常用于将程序的输出发送到屏幕。
-
标准错误流(stderr)通常用于将程序的错误信息发送到屏幕。
设备(流) | 文件描述符(int) | 文件指针(FILE *) |
---|---|---|
标准输入设备(键盘) | STDIN_FILENO:0 | stdin |
标准输出设备(屏幕) | STDOUT_FILEMO:1 | stdout |
标准出错设备(屏幕) | STDERR_FILEMO:2 | stderr |
这三个流的处理方式可以通过重定向来改变,例如,可以将标准输出重定向到一个文件中,或者将标准输入重定向到一个文件中读取数据。
总之,Linux 标准 IO 是 Linux 操作系统中用于处理输入和输出的一组标准流,它们可以通过重定向来改变处理方式。
2. 打开文件
fopen()
函数用于在 Linux 系统中打开文件。函数原型为:
FILE *fopen(const char *path, const char *mode);
该函数接受两个参数:
path
:指定要打开的文件的路径。mode
:指定文件打开模式,比如:读、写。如果打开文件成功,则该函数返回文件指针。否则,返回 NULL。
注意:在使用完文件后,需要使用 fclose()
函数关闭文件。重复打开一个已经打开的文件或者打开不存在的文件可能导致错误。
mode 模式可选的参数有:
mode | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在, 则该会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
3. 关闭文件
fclose()
函数用于在 Linux 系统中关闭文件。函数原型为:
FILE *fclose(FILE *stream);
该函数接受一个参数:
stream
:指定要关闭的文件的文件指针。如果关闭文件成功,则该函数返回 0。否则,返回 EOF。
注意:重复关闭一个已经关闭了的文件或者尚未打开的文件是安全的。但是,在使用完文件后,应该尽量及时使用 fclose()
函数关闭文件,以释放系统资源。
4. 判断文件情况
feof()
函数用于判断文件是否已经到达末尾。函数原型为:
int feof(FILE *stream);
该函数接受一个参数:
stream
:指定要判断的文件的文件指针。该函数返回一个非零值(true),表示文件已经到达末尾;如果文件还未到达末尾,则返回零(false)。
ferror()
函数用于判断文件是否出现错误。函数原型为:
int ferror(FILE *stream);
该函数接受一个参数:
stream
:指定要判断的文件的文件指针。该函数返回一个非零值(true),表示文件出现错误;如果文件没有出现错误,则返回零(false)。
5. 获取或设置文件偏移量
5.1 设置文件偏移量
fseek()
函数用于将文件指针移动到指定的位置。函数原型为:
int fseek(FILE *stream, long offset, int whence);
该函数接受三个参数:
stream
:指定要移动指针的文件的文件指针。offset
:指定要移动的字节数。如果为正数,则文件指针向后移动;如果为负数,则文件指针向前移动。whence
:指定文件指针移动的起始位置,可以取以下值:
SEEK_SET
:文件开头。SEEK_CUR
:当前位置。SEEK_END
:文件末尾。
fseek()
函数返回 0 表示成功,返回非零值表示失败。
rewind()
函数用于将文件指针移动到文件开头。函数原型为:
void rewind(FILE *stream);
该函数接受一个参数:
stream
:指定要移动指针的文件的文件指针。该函数无返回值。
注意:rewind(fp); 相当于 fseek(fp, 0L, SEEK_SE);
5.2 获取文件偏移量
ftell()
函数用于获取文件指针当前位置相对于文件开头的偏移量。函数原型为:
long ftell(FILE *stream);
该函数接受一个参数:
stream
:指定要获取指针位置的文件的文件指针。该函数返回文件指针当前位置相对于文件开头的偏移量,如果出现错误,则返回 -1。
下面是一个使用 ftell()
函数获取文件指针当前位置的示例代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// 定义文件指针
FILE *fp;
// 使用 fopen() 函数打开文件,并获取文件指针
fp = fopen("test.txt", "w+");
if (fp == NULL) {
// 打开文件失败,输出错误信息
perror("open() file");
exit(EXIT_FAILURE);
}
// 向文件中写入字符
if (fputs("AAAA", fp) == EOF) {
// 写入失败,输出错误信息
perror("fputc() error");
}
// 获取文件指针当前位置
long pos = ftell(fp);
if (pos == -1) {
// 获取失败,输出错误信息
perror("ftell() error");
} else {
// 输出文件指针当前位置
printf("current file position: %ld\n", pos);
}
// 使用 fclose() 函数关 闭文件
fclose(fp);
return 0;
}
6. 读写文件(一个字符)
标准IO的读写接口是非常丰富的。
6.1 一个字符的读取
fgetc()\getc()\getchar()
函数用于从文件中读取一个字符。函数原型为:
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
该函数接受一个参数:
stream
:指定要读取的文件的文件指针。void
:无。该函数返回读取到的字符,如果读取失败或者到达文件末尾,则返回 EOF。
6.2 一个字符的写入
fputc()\putc()\putchar()
函数用于向文件中写入一个字符。函数原型为:
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(void);
该函数接受两个参数:
c
:指定要写入文件中的字符。stream
:指定要写入的文件的文件指针。void
:无。该函数返回写入的字符,如果写入失败,则返回 EOF。
6.3 注意点
-
fgec()\getc()\getchar()
返回值是 int,而不是 char,原因是因为他们在出错或者读到文件末尾的时候需要返回一个值为-1的EOF标记,而char型数据有可能因为系统的差异而无法表示负整数。 -
当
fgec()\getc()\getchar()
返回EOF时,有可能是发生了错误,也有可能是读到了文件末尾,可以用feof()\ferror()
函数接口来进一步加以判断。 -
getchar()
缺省从标准输入设备读取一个字符,putchar()
缺省从标准输出设备输出一个字符。 -
fgetc()\fputc()
是函数,getc( )\putc()
是宏定义。 -
两组输入输出函数一般成对地使用,
fgetc()
和fputc()
,getc()
和putc( )
,getchar()
和putchar()
。
6.4 示例代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// 定义文件指针
FILE *fp;
// 使用 fopen() 函数打开文件,并获取文件指针
fp = fopen("test.txt", "w+");
if (fp == NULL) {
// 打开文件失败,输出错误信息
perror("open() file");
exit(EXIT_FAILURE);
}
// 向文件中写入字符
if (fputc('A', fp) == EOF) {
// 写入失败,输出错误信息
perror("fputc() error");
exit(EXIT_FAILURE);
}
// 将文件指针移动到文件开头
if (fseek(fp, 0, SEEK_SET) != 0) {
// 移动文件指针失败,输出错误信息
perror("fseek() error");
exit(EXIT_FAILURE);
}
// 循环读取文件中的每一个字符
int c;
while ((!feof(fp)) && ((c = fgetc(fp)) != EOF)) {
// 判断文件是否出现错误
if (ferror(fp)) {
// 出现错误,输出错误信息
perror("fgetc() error");
break;
}
printf("%c", c);
}
// 判断是否到达文件末尾
if (feof(fp)) {
printf("\nEOF reached.\n");
}
// 使用 fclose() 函数关闭文件
fclose(fp);
return 0;
}
7. 读写文件(一行数据)
标准IO的读写接口是非常丰富的。
7.1 一行数据的读取
fgets()\gets()
函数用于从文件中读取一行字符串。函数原型为:
char *fgets(char *str, int n, FILE *stream);
char *gets(char *str);
该函数接受三个参数:
str
:指向要存储读取到的字符串的缓冲区的指针。n
:指定缓冲区的大小。stream
:指定要读取的文件的文件指针。该函数返回读取到的字符串,如果读取失败或者到达文件末尾,则返回
NULL
。
7.2 一行数据的写入
fputs()
函数用于将一个字符串写入文件。函数原型为:
int fputs(const char *str, FILE *stream);
int puts(const char *str);
该函数接受两个参数:
str
:指定要写入文件的字符串。stream
:指定要写入的文件的文件指针。该函数返回写入的字符数,如果写入失败,则返回
EOF
。
7.3 注意点
fgets()
跟fgetc()
一样,当其返回NULL时并不能确定究竟是达到文件末尾还是 碰到错误,需要用feof( )/ferror()来进一步判断。fgets()
每次读取至多不超过size个字节的一行,所谓“一行”即数据至多包含一个换行符'\n'。gets()
是一个已经过时的接口,因为他没有指定自定义缓冲区s的大小,这样很容易造成缓冲区溢出,导致程序段访问错误。fgets()
和fputs()
,gets()
和puts()
一般成对使用,鉴于gets()
的不安全性,一般建议使用前者。
7.4 示例代码
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 256
int main(int argc, char *argv[])
{
// 打开文件
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
// 文件打开失败,退出程序
perror("fopen() error");
exit(EXIT_FAILURE);
}
// 跳过文件的前 4 个字符
if (fseek(fp, 4, SEEK_SET) != 0) {
perror("fseek() error");
exit(EXIT_FAILURE);
}
// 循环读取文件的每一行
char buffer[BUFFER_SIZE];
while (!feof(fp) && !ferror(fp)) {
// 使用 fgets() 读取文件的一行
if (fgets(buffer, BUFFER_SIZE, fp) != NULL){
// 使用 fputs() 打印读取到的一行
fputs(buffer, stdout);
}
}
// 关闭文件
fclose(fp);
return 0;
}
该程序打开了一个名为 "test.txt" 的文件,然后跳过了文件的前 4 个字符。然后,它使用 fgets()
读取文件的每一行,并使用 fputs()
函数将读取到的内容输出到标准输出。最后,它关闭了打开的文件。
8. 读写文件(若干数据)
8.1 读取若干数据
fread()
函数用于从文件中读取数据。函数原型为:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
该函数接受四个参数:
ptr
:指向要存储读取到的数据的缓冲区的指针。size
:指定每个数据项的大小,以字节为单位。nmemb
:指定要读取的数据项的个数。stream
:指定要读取的文件的文件指针。该函数返回读取的数据项的数量。如果发生错误,则返回一个小于
nmemb
的值。