Linux文件IO学习/复习

一、Linux目录结构以及文件操作

1.Linux命令操作的目的

Windows采用的命令:DOS命令
Linux采用的命令:Shell命令

我们采用命令,是为了操作文件。比如访问某个目录,访问某个文件,查找某个文件等等。

2.Linux文件目录分布

​ Linux文件目录是呈现一个树状的数据结构。

​ "/“根目录下有"bin”、“boot”、“dev”、"home"等目录,这些目录下又有很多目录。

​ 根目录:/ 就是目录的一个源头。用户目录:~或者/home/username

3.常用Linux命令

​ cd+文件名:访问某个文件夹
​ ls:显示当前所在目录的文件
​ touch+文件名:创建文件
​ rm+文件名:删除文件
​ mkdir+目录名:创建目录
​ rm -rf +目录名:删除目录
​ TAB键:补全
​ sudo+命令:用管理员权限执行命令
​ pwd:现实当前目录绝对路径

4.vi编辑器的简单使用

​ vi和vim的区别:vim是vi的升级版,基础功能两者一致,不过在嵌入式开发板中只有vi没有vim,但是在ubuntu上我们可以使用vim编辑器,他的功能更加丰富。

​ vi+文件名 用vi打开/创建某个文本文件

5.vi常用两种模式

1.命令行模式:按ESC进入,在这个状态下可以输入常用命令:

​ :行号 跳转到某一行
​ G 跳转到文本末尾
​ yy 复制该行
​ yx 复制若干行,x代表行数
​ p 粘贴
​ :wq 保存文本并退出
​ :q 正常退出文本
​ :q! 强制退出文本
​ :set nu 程序显示行号
​ :dd 删除一行
​ gg = G 自动整理代码

2.文本输入模式:按i或a进入

二、Linux文件

open函数

​ 高频使用的Linux系统调用:open write read close

​ Linux自带的工具:man手册

​ man 1是普通的shell命令
​ man 2是系统调用函数,如open,write说明。man 2 open查看open函数说明

在Linux系统库的定义:

int open(const char *pathname,intflags);//常用
int open(const char *pathname,intflags,mode_tmode);
//需包含的头文件
#include <sys/types.h>//这里提供类型pid_t和size_t的定义
#include <sys/stat.h>
#include <fcnt1.h>c

返回值:

成功,返回文件句柄,我们后面对于文件的读写和关闭等 都通过文件句柄来错做。
失败,返回-1

参数说明:

pathname:文件的路径名,如果只写文件名,就默认当前目录,如果在文件名加上目录,就按照绝对路径来打开文件。

flags:表示打开文件后用的操作。O_RDONLY只读模式、O_WRONLY只写模式 和 O_RDWR可读可写。

文件权限

Linux系统中采用三位十进制数表示权限,如0755,0644

ABCD:A-0表示十进制,B-用户,C-组用户,D-其他用户。

7表示1+2+4可读可写可执行,5表示1+4可读可执行。

close函数

int close(int fd);

write函数

write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。

ssize_t write(int fd, const void *buf, size_t count);

fd:文件描述符
*buf:写入的数据的首地址
count:写入数据个数
返回值:如果顺利write()会返回实际写入的字节数(len)。错误则返回-1,错误代码存入errno中。

read函数

从打开的fd设备或文件中读取count个字节到buf中

ssize_t read(int fd, void *buf, size_t count);

fd:文件描述符
*buf:读数据的首地址
count:读数据的个数
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调用read之前已经到达文件末尾,则这次read返回0。

lseek函数

off_t lseek(int fd, off_t offset, int whence);

fd:文件描述符
offset:偏移量
whence:
SEEK_SET:参数offset即为新的读写位置
SEEK_CUR:以目前的读写位置往后增加offset个偏移量
SEEK_END:将读写位置指向文件尾后再增加offset个偏移量,当whence值为CUR或END时,参数offset允许负值的出现。

返回值:文件读写距离文件开头的字节大小,出错返回-1

main函数参数

int main(int argc,char *argv[])
{
    return 0;
}

C语言规定了main函数的参数只能有两个,一个是argc,一个是argv,并且argc只能是整数,第二个必须是指向字符串的指针数组。

argc:表示行中参数的个数,argc的值是在输入命令行时由系统按实际参数个数自动赋值的。

argv:参数是字符串指针数组,各元素值为命令行中各字符串

cp指令的实现

执行流程:打开源文件,读取文件缓存倒数组,打开目标文件,写入目标文件,关闭文件句柄。

#include <stdio.h>
#incldue <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    int src_fd;
    int dev_fd;
    char readBuff[128]={0};
    int nRet = 0;
    if(argc!=3){
        printf("error\n");
        return -1;
    }
    src_fd = open(argv[1],O_RDWR);
    if(src_fd<0){
        printf("open file %s failed\n",argv[1]);
        return -2;
    }
    des_fd = open(argv[2],O_RDWR|O_CREAT,0755);
    if(des_fd<0){
        printf("open file %s failed\n",argv[2]);
        return -3;
    }
    
    while(1)
    {
        nRet = read(src_fd,&readBuff[0],128);
        if(nRet<128)
            break;
        write(des_fd,readBuff,nRet);
        memset(readBuff,0,128);//清零读缓存区
    }
    write(des_fd,readBuff,nRet);
    close(src_fd);
    close(des_fd);
    return 0;
}

这里介绍以下memset函数

void *memset(void *buffer,int c,int count);
/*
buffer:为指针或数组
c:是赋给buffer的值
count:是buffer的长度
*/
memset(void *buff,0,sizeof(buff));//用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为''或'\0'

fopen函数

fopen fwrite fread fclose等 属于标准C库函数

open close write read属于Linux系统调用。

可移植性:fopen强于open

fopen在用户态是缓存的,open在用户态没有缓存的

FILE *fopen(const char *pathname, const char *mode);

第一个参数为想打开文件的文件路径及文件名,第二个参数表示打开方式

mode:
r:只读,文件必须存在
r+:可读写,文件必须存在
rb+:打开二进制文件,可读写
w:只写,文件存在则文件长度清0,文件不存在则建立该文件
w+:可读写

返回值:成功,返回文件指针;失败返回NULL

fread

从文件中读入数据到指定的地址中

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

第一个参数为接收数据的指针buf,也即数据存储的地址。

第二个参数为单个元素的大小,即由指针写入地址的数据大小,注意单位是字节

第三个参数为元素个数,即要读取的数据大小为size的元素个数

第四个参数为提供数据的文件指针,该指针指向文件内部数据

返回值:读取的总数据元素个数

fwrite

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

fseek

重定位文件内部的指针

int fseek(FILE *stream, long offset, int whence);

参数:第一个为文件指针,第二个是指针偏移量,第三个是指针偏移起始位置。

返回值:重定位成功返回0,否则返回非零值。

常用3个宏:
SEEK_SET 0 文件开头
SEEK_CUR 1 文件当前位置
SEEK_END 2 文件末尾

例如:

fseek(fp,100L,SEEK_SET);把fp指针移动到离文件开头100字节处。

fseek(fp,100L,SEEK_CUR);把fp指针移动到离文件当前位置100字节处。

fclose

​ 关闭一个文件流,使用fclose可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区。

int fclose(FILE* stream);成功返回0,失败返回EOF

示例:

#include <stdio.h>
#include <string.h>
int main()
{
    FILE *fp = NULL;
    int nRet = 0;
    char readBuff[12];
    memset(readBuff,0,12);//初始化数组为0
    char *writeBuff = "hello world";
    
    fp = fopen("mm","r+");
    if(fp == NULL){
        printf("fopen failed\n");
        return -1;
    }
    printf("open succeed\n");
    
    nRet = fread(readBuff,4,2,fp);
    if(nRet<=0){
        printf("fread failed\n");
        return -2;
    }
    printf("read %s\n",readBuff);
    
    nRet = fseek(fp,1,SEEK_SET);
    if(nRet){
        printf("fseek failed\n");
        return -3;
    }
    
    nRet = fwrite(writeBuff,4,1,fp);
    if(nRet <= 0){
        printf("fwrite failed\n");
        return -4;
    }
    
    nRet = fclose(fp);
    if(nRet){
        printf("close failed\n");
        return -5;
    }
    printf("close succeed\n");
    return 0;
}

文件IO和标准IO的区别

linux标准文件描述符

文件描述符缩写描述
0STDIO标准输入
1STDOUT标准输出
2STDERR标准错误输出

文件IO:是指调用内核提供的系统调用函数,头文件是unistd.h

标准IO:是间接调用系统调用函数,头文件是stdio.h

缓存的概念

1.我们程序中的缓存,局势你想从内核读写的缓存(数组)—用户空间的缓存

2.每打开一个文件,内核在内核空间中也会开辟一块缓存,这个叫内核空间的缓存

​ 文件IO中的写 即是将用户空间中的缓存写到内核空间的缓存中。
​ 文件IO中的读 即是将内核空间中的缓存写到用户空间的缓存中。

3.标准IO的库函数也有一个缓存,这个缓存称为 库缓存。

C库缓存的特点:

​ 1.遇到 \n 时,会将库缓存的内容写到内核缓存中,即调用了系统调用函数。
​ 2.库缓存写满时,会调用系统调用函数,将库缓存内容写到内核缓存中。1024

//例如
int main()
{
	char buf[20]="hello world";
	printf("%s",buf);//此时是不会有标准输出的,因为没有\n字符结束
	while(1);
	return 0;
}
三类读写函数
1.行缓存,遇到换行符(\n),或者写满用户缓存时,即调用系统调用函数
读:fgets,gets,printf,fprintf,sprintf
写:fputs,puts,scanf

一个字符的读写,是否也有行缓存

读:fgetc,getc,getchar
写:fputc,putc,putchar
2.无缓存,只要用户调用这个函数,就会写到内核中
stderr
这个可以用在fputs里,fputs(buf,stderr);
3.全缓存,只有写满缓存再调用系统调用函数
读:fread
写:fwrite
行缓存的读写函数fgets和fputs
char *fgets(char *s,int size,FILE *stream);
int fputs(const char *s,FILE *stream);

fgets示例:

#include <stdio.h>
int main(int argc,char *argv[])
{
    FILE *fp;
    char writeBuff[] = "hello world";
    
    fp = fopen("mm.c","w+");
    if(fp == NULL){
        printf("fopen failed\n");
        return -1;
    }
    fputs(writeBuff,fp);
    
    fseek(fp,0,SEEK_SET);//将光标指向第一个位置,因为fputs执行完毕后光标会移动到字符串的末尾,会导致下面的fgets函数输出为空。
    fgets(readBuff,128,fp);
    
    printf("readBuff is %s\n",readBuff);
    fclose(fp);
    return 0;
}

fputs示例:

#include <stdio.h>
int main(int argc,char *argv[])
{
    char writeBuff[] = "hello world";
    FILE *fp;
    fp = fopen("a.c","w+");
    if(fp == NULL){
        printf("fopen failed\n");
        return -1;
    }
    fputs(writeBuff,fp);//因为fputs是行缓存的输出函数,所以运行此代码时,shell界面并看不到输出writeBuff的字符串。
    
    fclose(fp);//运行到这里才会输出writeBuff,因为fclose关闭文件指针后,会把缓冲区内最后剩余的数据输出到磁盘文件中。
    return 0;
}
刷新缓存函数
fflush(FILE *fp);

一般使用方法:fflush(stdout);

调整读写位置指针函数

fseek():

​ fseek()参数与lseek是一样的但是返回值不一样

​ lseek()函数返回值是:当前文件的位置指针值;

​ fseek()返回值:成功返回0,失败返回-1;

rewind(FILE *fp):用于设定流的文件位置为文件开头,该函数调用成功无返回值

实例:

#include <stdio.h>
int main(int argc, char *argv)
{
    char writeBuff[] = "hello world";
    char readBuff[128] = {0};
    FILE *fp;
    
    fp = fopen("a.c","w+");
    if(fp == NULL){
        printf("fopen failed\n");
        return -1;
    }
    //fputs(fp,0,SEEK_SET);
    rewind(fp);
    fgets(readBuff,128,fp);
    
    printf("readBuff is %s\n",readBuff);
    fclose(fp);
    return 0;
}
gets和puts

行缓存读写函数

char *gets(char *s);
int puts(const char *s);

gets与fgets区别:

1.gets()不能指定缓存长度,这样会造成缓存越界
2.gets()只能从标准输入中读
3.gets()不将新行符存入缓存中,fgets将新行符存入缓存中

puts与fputs区别:

1.puts()只能向标准输出中写
2.puts()输出时会添加一个新行符,fputs不会添加。

fprintf、sprintf

行缓存函数

#include <stdio.h>
int main()
{
    char buf[128]={0};
    int i = 10;
    sprintf(buf,"i = %d\n",i);
    printf("buf is %s\n",buf);
    return 0;
}
fputc和fgetc

行缓存函数

fputc写一个字符到文件中和fgetc从文件中读一个字符。

返回值都是成功则返回输入或读取的字符,出错返回EOF。-1

例:

#include <stdio.h>
int main(int argc,char *argv[])
{
    FILE *fp;
    int nRet;
    char writeBuff[] = "hello world";
    fp = fopen("a.c","w+");
    if(fp == NULL){
        printf("open file failed\n");
        return -1;
    }
    fputc('a',fp);
    rewind(fp);
    nRet = fgetc(fp);
    printf("nRet is %c\n",nRet);
    fclose(fp);
    return 0;
}
其他feof、ferror、clearerr
int feof(FILE *stream);//判断是否已经到文件结束,若是则返回非0,没有返回0
int ferror(FILE *stream);//判断是否读写错误,若是读写错误返回非0,无错误返回0
void clearerr(FILE *stream);//清除流错误
cat指令实现
#include <stdio.h>
int main(int argc,char *argv[])
{
    FILE *fp;
    int nRet = 0;
    
    if(argc!=2){
        printf("failed");
        return -1;
    }
    fp = fopen(argv[1],"r");
    if(fp == NULL){
        printf("fopen failed\n");
        return -2;
    }
    while(1)
    {
    	nRet = fgetc(fp);
        if(feof(fp))//到文件末尾则返回非0
            break;
        fputc(nRet,stdout);
    }
    
    fclose(fp);
    return 0;
}

Linux下静态库和动态库制作与使用

Linux操作系统支持的函数库分为:

静态库,libxxx.a,在编译时就将库编译进可执行程序中。优点:程序的运行环境中不需要外部的函数库。缺点:可执行程序大

动态库,libxxx.so,在运行时将库加载到可执行程序中。优点:可执行程序小。缺点:程序的运行环境中必须提供响应的库。

静态库的制作

1.生成目标文件:gcc -c file.c

2.静态函数库创建命令ar

ar -cr libfile.a file.o

例如写一个main.c和sub.c两个c文件:

gcc sub.c -c -o sub.o生成sub.o文件

ar -cr -o libsub.a sub.o生成libsub.a静态函数库文件

gcc main.c -L. -lsub静态库的编译,就会把静态函数库整合进main。-L.表示在本目录下查找静态函数库。-l指定了静态函数库名,由于静态函数库命名方式是“lib***.a”,其中的lib和.a忽略。

动态函数库的制作

1.生成目标文件:gcc -c file.c

2.gcc -shared -fpic -o libfile.so file.o

-fpic:产生位置无关代码
-shared:生成共享库
用上述命令生成libaddsub.so动态函数库
gcc -o out main.c -L. -lfile

此时还不能立即./out,因为在动态函数库使用时,会查找/usr/lib,/lib目录下的动态函数库,而我们的动态库不在里面

3.解决方法:

​ 1.libaddsub.so放到/usr/lib或/lib中去;
​ 2.假设libfile.so在/home/linux/file 环境变量方法
export LD_LIBRARY_PATH=/home/linux/file
​ 3.在/etc/ld.so.conf文件里加如我们生成的库的目录,然后执行/sbin/ldconfig。
​ /etc/ld.so.conf是非常重要的一个目录,里面存放的是链接器和加载器搜索共享库时要检查的目录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值