Linux C语言 20-文件I/O

Linux C语言 20-文件I/O

本节关键字:Linux、C语言、标准输入(stdin)、标准输出(stdout)、文件读写
相关C库函数:getchar、putchar、putc、getc、gets、puts、printf、scanf、fopen、fclose、fputc、fgetc、fputs、fgets、fprintf、fscanf、fwrite、fread

什么叫做IO

I:input,表示输入
O:output,表示输出
在编写程序时,我们经常会用到交互,怎么样做到交互呢。很简单,就是在程序需要用户输入一些数据时,就从标准输入或者文件读取,执行任务后再从标准输出或文件将执行结果告知用户。

标准文件

C语言中把所有的设备都当作文件,而Linux内核也是使用C语言进行编写的,因此在Linux中也有一句名言叫做“一切皆文件”。在C语言程序执行过程中,会自动打开三个文件:

标准文件文件指针设备
标准输入stdin键盘
标准输出stdout屏幕
标准错误stderr屏幕

这三个文件指针定义在头文件 stdio.h 中:

#include <stdio.h>
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

C语言的输入和输出

在C语言中,默认输入设备对应键盘,默认输出设备对应屏幕。

相关库函数
#include <stdio.h>
// 标准输出
int putc(int c, FILE *stream);
int putchar(int c);
int puts(const char *s);
int printf(const char *format, ...);
// 标准输入
int getc(FILE *stream);
int getchar(void);
char *gets(char *s); // 不建议使用
int scanf(const cjar *format, ...);

C语言中的I/O(输入/输出)一般使用printf()和scanf(),scanf()函数从标准输入(键盘)读取内容并格式化,printf()函数发送格式化输出到标准输出(屏幕)。

格式化参数
参数格式描述
%a浮点数、十六进制数和p-计数法(c99)
%A浮点数、十六进制数和p-记法(c99)
%c一个字符(char)
%C一个ISO宽字符
%d有符号十进制整数(int)
%ld有符号十进制长整数(long)
%e浮点数,e-计数法
%E浮点数,E-计数法
%f单精度浮点数(默认float,十进制计数法 %.nf 中的n标识精确到小数位后n位)
%lf双精度浮点数(默认double)
%g根据数值不同自动选择%f或%e
%G根据数值不同自动选择%f或%e
%i有符号十进制数(与%d相同)
%o无符号八进制整数
%p指针
%s对应字符串char*,%ms最多右对齐输出字符串中的前m列字符,%-ms最多左对齐输出字符串中的前m列字符,%m.ns右对齐输出占m列,但只取字符串中前n个字符
%S对应宽字符串WCHAR*
%u无符号十进制整数(unsigned int)
%lu无符号十进制长整数(unsigned long)
%x无符号十六进制形式输出整数,字母小写(a - f)
%lx无符号十六进制形式输出长整数,字母小写(a - f)
%X无符号十六进制形式输出整数,字母大写(A - F)
%lX无符号十六进制形式输出长整数,字母大写(A - F)
-表示左对齐输出,省略默认右对齐输出
0表示空位用0填充,省略默认空位不填
m.nm指域宽(输出项所占字节数);n指精度,小数的位数,默认n=6
l对应整型的long,对应实型的double
h将整型的格式字符修正为short型
API使用示例
#include <stdio.h>

int main()
{
    int iNum, iAge;
    char c;
    char sAge[16] = {0};
    char *rc = NULL;
    
    printf("this is putc  ");
    putc(59, stdout);    // 59对应的ASCII码为 ;
    printf("\n");
    
    printf("this is putchar  ");
    putchar(59);        // 59对应的ASCII码为 ;
    printf("\n");
    
    puts("this is puts()");
    printf("this is printf()\n");
    printf("\n");
    
    printf("iNum=%d, 请输入新的值:", iNum);
    iNum = getc(stdin);
    getchar();            // 读取回车
    printf("getc()获取新的 iNum=%d\n", iNum);
    printf("\n");
    
    printf("请再次输入新的值: ");
    iNum = getchar();
    printf("getchar()获取新的 iNum=%d\n", iNum);
    printf("\n");
    
    printf("请输入一串字符:");
    rc = gets(sAge);
    printf("gets()返回rc: %s\n", rc);
    printf("gets()获取sAge: %s\n", sAge);
    printf("\n");
    
    printf("请输入字符串:");
    scanf("%s", sAge);
    printf("scanf()获取的字符串为: %s\n", sAge);
    printf("\n");
    
    printf("iAge=%d, 请输入新的年龄: ", iAge);
    scanf("%d", &iAge);
    printf("scanf()获取新的年龄为: %d\n", iAge);
    
    return 0;
}

/** 
编译时提示:
/tmp/ccQOB4Oh.o: In function main': 
Linux_C_020.c:(.text+0x138): warning: the gets' function is dangerous and should not be used.

运行结果:
this is putc  ;
this is putchar  ;
this is puts()
this is printf()
iNum=-588257952, 请输入新的值:1
getc()获取新的 iNum=49
请再次输入新的值: 2
getchar()获取新的 iNum=50
请输入一串字符:gets()返回rc:
gets()获取sAge:
请输入字符串:abc
scanf()获取的字符串为: abc
iAge=0, 请输入新的年龄: 25
scanf()获取新的年龄为: 25
*/

文件的读写

在C语言中,可以使用相关接口函数操作文本文件和二进制文件,这些操作包含:创建、打开、读写、关闭等操作。

相关的库函数
文本文件模式描述
r打开文本文件,用于读。流被定位于文件的开始。
r+打开文本文件,用于读。流被定位于文件的开始。
w将文件长度截断为零,或者创建文本文件,用于写。流被定位于文件的开始。
w+打开文件,用于读写。如果文件不存在就创建它,否则将截断它。流被定位于文件的开始。
a打开文件,用于追加 (在文件尾写)。如果文件不存在就创建它。流被定位于文件的末尾。
a+打开文件,用于追加 (在文件尾写)。如果文件不存在就创建它。读文件的初始位置是文件的开始,但是输出总是被追加到文件的末尾。

字符串 mode 也可以包含字母 ‘b’ 作为最后一个字符,或者插入到上面提到的任何双字符的字符串的两个字符中间(例如:rb,wb,ab,rb+,r+b,wb+,w+b,ab+,a+b)。这样只是为了和 ANSI X3.159-1989(ANSI ‘C’) 标准严格保持兼容,没有实际的效果;在所有的遵循 POSIX 的系统中,‘b’ 都被忽略,包括 Linux。(其他系统可能将文本文件和二进制文件区别对待,如果在进行二进制文件的 I/O,那么添加 'b’是个好主意,因为你的程序可能会被移植到非 Unix 环境中。)
任何新建的文件将具有模式 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH (0666),然后以进程的掩码值 umask 加以修改。

文件流光标位置

起始位置常量名常量值
文件开头SEEK_SET0
当前位置SEEK_CUR1
文件末尾SEEK_END2
#include <stdio.h>
// 打开文件
// 创建或打开一个文件,类型FILE包含了所有用来控制流的信息
FILE *fopen(const char *path, const char *mode);

// 流光标定位
// 移动文件中的打开文件读写位置指针,offset为偏移量,whence为偏移起始位置(SEEK_SET文件开头、SEEK_CUR当前位置、SEEK_END文件末尾)
int fseek(FILE *stream, long offset, int whence);
// 将文件移动到文件末尾位置,返回文件长度
long ftell(FILE *stream);
// 将当前文件指针移动到文件开始位置
void rewind(FILE *stream);
// 获取文件指针当前相对文件开始位置的偏移量
int fgetpos(FILE *stream, fpos_t *pos);
// 将文件指针移动到相对文件开始位置偏移量的位置
int fsetpos(FILE *stream, fpos_t *pos);

// 关闭文件
// 关闭文件,成功返回0,失败返回EOF
int fclose(FILE *fp);

// 写文件
// 将字符写入到流中,成功返回写入的字符,失败返回EOF
int fputc(int c, FILE *stream);
// 将一个以null结尾的字符串写入到流中,成功返回非负值,失败返回EOF
int fputs(const char *s, FILE *stream);
// 将目标内容按照指定格式写入流中,成功返回写入的字节数(不包含null结束符),失败返回负值
int fprintf(FILE *stream, const char *format, ...);
// 将nmmb个数据元素(每个大小字节长)写入流指向的流,并从ptr给定的位置获取它们。成功返回写入字节数,失败返回0
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

// 读文件
// 从流中读取单个字符,成功返回读取到的字符,失败返回EOF
int fgetc(FILE *stream);
// 从流中读取一个长度为size-1的字符串,读取到的字符串存储在s中,遇到'\n'和EOF会停止
char *fgets(char *s, int size, FILE *stream);
// 从流中按照指定格式读取一个字符串,遇到第一个空格和换行符时会停止
int fscanf(FILE *stream, const char *format, ...);
// 从流指向的流中读取nmmb个数据元素,每个大小字节长,并将它们存储在ptr给定的位置。成功返回写入字节数,失败返回0
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

在文件读写的实际应用中使用较多的函数有两组(四个)函数:
文本:fprintf()函数和fscanf()函数,通常用于日志记录。
二进制:fwrite()函数和fread()函数,通常用于数组或结构体等数据结构的存储。

API使用实例
#include <stdio.h>
#include <string.h>

int main()
{
    char c;
    int rc;
    FILE *fp = NULL;
    
    // 文本文件
    fp = fopen("/home/iscs/TEST/text", "a+");
    if (fp)
    {
        c = 1;
        rc = fputc(c, fp);
        printf("fputc rc: %d\n", rc);
        c = '\n';
        rc = fputc(c, fp);
        printf("fputc rc: %d\n", rc);
        
        rc = fputs("fputs\n", fp);
        printf("fputs rc: %d, len: %d\n", rc, sizeof("fputs\n"));
        
        rc = fprintf(fp, "%s\n", "fprintf");
        printf("fprintf rc: %d, len: %d\n", rc, sizeof("fprintf\n"));
        
        fclose(fp);
    }
    
    fp = fopen("/home/iscs/TEST/text", "a+");
    if (fp)
    {
        rc = fgetc(fp);
        printf("fgetc rc: %d\n", rc);
        rc = fgetc(fp);
        printf("fgetc rc: %d\n", rc);
        
        char s[64] = {0};
        char *ps = NULL;
        ps = fgets(s, 64, fp);
        printf("fgets ps: %s, s: %s\n", ps , s);
        
        memset(s, 0, sizeof(s));
        rc = fscanf(fp, "%s\n", s);
        printf("fscanf rc: %d, s: %s\n", rc, s);
    }
    
    // 二进制文件
    typedef struct info_s {
        int         no;
        char     name[64];
        char    sex;
        short    age;
        char    major[128];
        float   score;
    } info_t;
    info_t wStudent = {1713080405, "Yunxi", 1, 25, "电子信息工程", 99.99};
    info_t rStudent;
    memset(&rStudent, 0, sizeof(info_t));
    
    fp = fopen("/home/iscs/TEST/binary", "ab+");
    if (fp)
    {
        rc = fwrite(&wStudent, sizeof(info_t), 1, fp);
        printf("fwrite rc: %d, len: %d\n", rc, sizeof(info_t));
        fclose(fp);
    }
    
    fp = fopen("/home/iscs/TEST/binary", "ab+");
    if (fp)
    {
        rc = fread(&rStudent, sizeof(info_t), 1, fp);
        printf("fread rc: %d, len: %d\n", rc, sizeof(info_t));
        fclose(fp);
        
        printf("wStudent: %d, %s, %d, %d, %s, %.02f\n", wStudent.no, wStudent.name, wStudent.sex, wStudent.age, wStudent.major, wStudent.score);
        printf("rStudent: %d, %s, %d, %d, %s, %.02f\n", rStudent.no, rStudent.name, rStudent.sex, rStudent.age, rStudent.major, rStudent.score);
    }
    
    return 0;
}

/** 运行结果:
fputc rc: 1
fputc rc: 10
fputs rc: 1, len: 7
fprintf rc: 8, len: 9
fgetc rc: 1
fgetc rc: 10
fgets ps: fputs
, s: fputs
fscanf rc: 1, s: fprintf
fwrite rc: 1, len: 204
fread rc: 1, len: 204
wStudent: 1713080405, Yunxi, 1, 25, 电子信息工程, 99.99
rStudent: 1713080405, Yunxi, 1, 25, 电子信息工程, 99.99
*/

拓展:可以将本节的内容与链表一节的简单学生信息管理程序相结合,将输入的信息保存至本地文件中,下次启动时从文件读取,这样就可以避免数据丢失了。

文件IO使用示例

Linux C语言 43-读取配置文件(ini文件)
Linux C语言 44-日志记录

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值