linux文件IO操作之一:文件读写操作
文章目录
- 一、文件I/O基本概念
- 二、demo举例
- 2.1 demo1:四大接口基本用法
- 2.2 demo2:四大接口在工程中常见用法
- 2.2.1关于read(),主要是下面这一段代码:
- 2.2.2 关于write(),主要是下面这段代码
一、文件I/O基本概念
关于文件IO,涉及以下基本概念:文件描述符&系统调用接口
下面以思维导图的形式梳理这几个知识点:
思维导图源文件在此: Linux_文件IO.xmind
二、demo举例
下面记录linux文件操作如下四个基本接口:
- 备注:全文分为两个demo。demo1为接口的基本使用方法,供学习理解使用。demo2考虑了所有的错误场景处理,一般工程中会使用demo2 *
open() /* 打开文件 */
read() /* 读文件 */
write() /* 写文件 */
close() /* 关闭文件 */
2.1 demo1:四大接口基本用法
- 注意:此demo仅仅是当做学习理解读写文件接口的基本操作来使用。
一般情况下,由于读写文件时可能会出现各种错误,正常工程中需要考虑处理所有的错误,如果要全部读取文件,一直到文件末尾(EOF),此demo并不能适用于工程中,常见工程中的用法请移步之后的demo。
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd_src = 0;
int fd_dest = 0;
ssize_t len_src = 0;
ssize_t len_dest = 0;
char buf[1000];
printf("start:\r\n");
/* open file */
fd_src = open("src.txt", O_RDONLY);
if (fd_src == -1) {
perror("Open src failed:");
return -1;
}
fd_dest = open("dest.txt", O_RDWR | O_TRUNC | O_CREAT, 0664);
if (fd_dest == -1) {
perror("Open dest failed:");
return -1;
}
/* read file to buff */
memset(buf, 0, sizeof(buf));
len_src = read(fd_src, (void *)buf, sizeof(buf));
if (len_src == -1) {
perror("Read failed:");
return -1;
}
/* print buff */
printf("%s\r\n", buf);
/* write file */
len_dest = write(fd_dest, (const char *)buf, strlen(buf));
if (len_dest == -1) {
perror("Write failed:");
return -1;
}
memset(buf, 0, sizeof(buf));
/* close file */
close(fd_src);
close(fd_dest);
printf("end!\r\n");
return 0;
}
2.2 demo2:四大接口在工程中常见用法
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(void)
{
int fd_src = 0;
int fd_dest = 0;
int len; /* 想要读取or写入的字节长度 */
char buf[1000]; /* 缓冲区:保存读出的内容 */
char *pbuf = buf;
ssize_t ret;
printf("start:\r\n");
/* 打开文件 */
fd_src = open("src.txt", O_RDONLY);
if (fd_src == -1) {
perror("Open src failed:");
return -1;
}
fd_dest = open("dest.txt", O_RDWR | O_TRUNC | O_CREAT, 0664);
if (fd_dest == -1) {
perror("Open dest failed:");
return -1;
}
/* 读文件(src.txt)
* len:需要读取的字节长度
* ret:读取到的字节长度
*/
len = sizeof(buf);
memset(buf, 0, sizeof(buf));
while ((len != 0) && ((ret = read(fd_src, (void *)buf, len)) != 0)) {
if (ret == -1) {
if (errno == EINTR) {
continue;
}
perror("read:");
break;
}
len = len - ret;
pbuf = pbuf + ret;
}
/* 写文件(dest.txt) */
pbuf = buf;
len = strlen(buf);
while ((len != 0) && ((ret = write(fd_dest, buf, len)) != 0)) {
if (ret == -1) {
if (errno == EINTR) {
continue;
}
perror("write:");
break;
}
len = len - ret;
pbuf = pbuf + ret;
}
/* 关闭文件 */
close(fd_src);
close(fd_dest);
printf("end!\r\n");
return 0;
}
2.2.1关于read(),主要是下面这一段代码:
/* len_src:要读取的数据长度
* ret:读文件接口read()的返回值,表示读取到的数据长度
*/
while ((len_src != 0) &&
((ret = read(fd_src, (void *)pbuf, len_src)) != 0)) {
if (ret == -1) {
if (errno == EINTR) {
continue;
}
perror("read:");
break;
}
len_src = len_src - ret;
pbuf = pbuf + ret;
}
释义:
- (1)read函数表示:从文件描述符指向位置开始读取文件到buff中,返回值为读取的长度,如果返回-1表示读取失败,错误码在errno中保存,如果为0表示读取0个字节。
- (2)如果要读取数据长度len_src为0,那直接跳过,不如不读
- (2)从fd_src文件中读取数据到pbuf缓冲区中,需要读出len_src长度
- (3)返回值ret如果为-1,读取失败则处理失败
- (4)如果读取成功,判断【要读取的长度 - 已读取到的长度】是否为0,如果为0则读取结束(因为有时候可能会读取失败或者读取中断,所以减去读取到的长度跟需要读的长度进行判断)
- (5)碰到EOF也表示读取文件到末尾了,需要结束了。
2.2.2 关于write(),主要是下面这段代码
/* 写文件(dest.txt) */
/* 将buf中的字符串写入到fd_dest中 */
pbuf = buf;
len = strlen(buf);
while ((len != 0) && ((ret = write(fd_dest, buf, len)) != 0)) {
if (ret == -1) {
if (errno == EINTR) {
continue;
}
perror("write:");
break;
}
len = len - ret;
pbuf = pbuf + ret;
}
释义:
- (1)write函数表示:从文件描述符fd引用文件的当前位置开始,将buf中的最多count个字节写入文件中。写入错误返回-1,成功时返回写入的字节数并更新文件位置。
- (2)要写入的长度不为零,就开始写入,写入之后返回值ret不为0表示写入成功或失败,进循环,循环中进行-1判错。
- (3)写入ret个字节以后,将想要写入的长度减去已写入的长度ret,并且缓冲区指针偏移ret为已经写入的长度。
- (4)再次循环时,如果len为0了表示写够了,跳出循环。
做而论道_CS: 一开始,计算机专家就定义了:符号位! 由此,原码反码,就都出现了:正负两个零。 而实际上,数字,只有: 正数、负数、零。 注意: 零,只有一个! 上过小学的,都知道这事。 而计算机专家,竟然弄出来两个零! 然后,他再怎么弄,显然都是错误的。 这些计算机专家,上过小学吗? 值得怀疑。
做而论道_CS: 如果从:符号位原码反码 ..., 开始学习 “补码”,就误入歧途了。 一个谬论接一个谬论,根本就无法自圆其说。 因为,计算机中,根本就没有: 符号位原码反码补码。 你看过《卖拐》吧? 要论忽悠,老赵,远远比不上这些计算机老师! 没影的事,他们能说的,和真的一样。
做而论道_CS: 另外,由补码换算到十进制数,也极其简单。 你只需记住:【补码首位的权,是负数】。 一般的八位二进制数,各个位的权是: 128、64、32、16、8、4、2、1; 如果是八位的补码,各个位的权则是: -128、64、32、16、8、4、2、1。 例如,有一个补码:1110 0001, 它代表的十进制是:-128 + 64 + 32 + 1= -31。 再看,另一个补码:0110 0001, 它代表的十进制是:0 + 64 + 32 + 1 = +97。 仅仅使用【进制转换】,不就完事了! ---------------------- 那么,所谓的: 机器数真值符号位原码反补码正数三码与正数相同 负数取反加一符号位不变符号位也参加运算模同余 ... 这一大堆乱七八糟的概念,不都是垃圾嘛! 进位,是小学二年级的知识点吧? 舍弃进位,很难理解吗? 老外竟然能弄出那么大一堆概念! 老外的数学水平,由此可见一斑。 谁要是跟老外学算术,立刻、马上,直接就掉沟里去了! 我们的计算机老师,也不懂数学,只知道跟风。 一天一天的,在大学里面,兜着圈子讲小学的知识。 真是毁人不倦坑人不浅! 这些老师,捡个鞋拔子就当成玉如意了。 天天蒙骗学生,自己赚取名声和丰厚的讲课费。 顺便再抓几个学生挂科,抖一下威风! 现在知道我们缺芯片用的原因了吧!
做而论道_CS: 计算机的字长,是固定的。 八位机,它只会做: 八位 + 八位 = 一个进位、八位的和。 八位二进制数是:0000 0000 ~ 1111 1111。 也就是十进制数:0 ~ 255。 如果有进位,就是:2^8 = 256。 在两位十进制运算中,舍弃的进位,是:一百。 那么: 加 99,再减 100,当然就是 -1 了。 加 98,再减 100,必定就是 -2。 。。。 八位二进制的进位,是:2^8 = 256。 加 255 (1111 1111),再减 256,也就是 -1 ! 同理: +254 (1111 1110),就是 -2。 +253 (1111 1101),就是 -3。 。。。 +128 (1000 0000),就是 -128。 以上这些正数,就是计算机专家 “发明” 的补码! 你如果上过中学,你一定能推出关系式: 负数的补码 = 256 + 负数。 一般化: 负数的补码 = 2^n + 负数。 n,是补码的位数。 例:求-31 的补码是什么? 解:256-31 = 225 = 1110 0001 (二进制)。 这不就求出来了吗? 哪里还用什么 “原码取反加一”! ------------- 求正数的补码,公式也是雷同的: 正数的补码 = 256 + 该正数。 要知道,加上 256,就是出现一个进位。 进位必须舍弃,所以,256 就不用加了。 于是,公式可以简化为: 正数的补码 = 该正数。 在此处,这就证明了: 零和正数的补码,就该数字本身。 举例,就不用了吧。 计算机专家也是说: 正数的补码,就是该正数。 但是,他们并没有给出证明。 为什么不加以证明呢? 因为这些专家: 弄不懂什么是进位, 更不懂什么是舍弃进位!
做而论道_CS: 计算机,使用的是二进制数。 作为数字,二进制、十进制,都是一样的。 数,它们就是数,并不是什么“原码反码补码”。 如果用数字,代表 “非数字”,才能叫做 “码”。 如:学号、门牌车牌电话性别英文汉字声音颜色 ... 在码长八位时,各码的范围如下: 原码:-127 ~ +127; 反码:-127 ~ +127; 补码:-128 ~ +127。 看到了吧? -128 只有补码,没有原码和反码。 没有原码,拿什么取反? 没有反码,拿什么加一? 取反加一,不可用! -128 的补码,又是怎么来的? 是特殊规定的吗? 不是的! 所有的补码,都是算出来的。 但是,并不是用 “原码取反加一” 算的。 ------------------- 所谓的 “补码”,并非二进制才有。 在任何进制中,都是存在的。 补码,也并非是计算机专家的发明。 你看十进制数,两位的:0 ~ 99。 可以有: 27 + 99 = (一百) 26 27 - 1 = 26 只要你忽略进位,仍旧保持两位数, 这两种算法的功能,就是相同的! 即,舍弃了进位: 正数,就能当负数使用! 加法,也就可以实现减法运算! 在计算机中舍弃进位,会怎样? 可以省掉减法器,简化硬件! 只用一个加法器,就可横行天下! ================== 你如果明白什么是【忽略进位】: 你就能懂得 “补码” 的来源与意义。 ==================