标准I\O与文件I\O,静态库和动态库

标准IO

标准IO
  • 输入输出设备

    • 字符输入设备:键盘

    • 图像输入设备:鼠标,扫描仪

    • 图像输出设备:显示器,打印机

    • 输入输出设备:USB,u盘

  • 标准IO:表示程序与操作系统或其他程序之间进行数据交换的方式。

    • 标准输入(stdin)是程序从外部获取数据的接口,通常指键盘输入
    • 标准输出(stdout)是程序向外部输出数据的接口,通常指屏幕输出
  1. 标准I/O-:FILE被称为流(stream)

    • 文本流:以逐行或逐字符的方式处理文本数据

    • 二进制流:以字节为单位处理数据

    • 注意:文本流可以直接用于处理普通的文本文件,例如.txt文件。而二进制流适用于处理任意类型的文件。由于字符编码的原因,文本流在跨平台和多语言处理时可能存在一些问题。而二进制流不受字符编码的限制,可以更好地进行数据传输和处理。

  2. 流的缓冲类型

    • 无缓冲流:无缓冲流将数据直接从输入设备读取或直接将数据写入输出设备,没有中间缓冲区。每个读写操作都直接与底层设备进行交互,读写效率较低。如:System.inSystem.out 在某些环境下就是无缓冲的。

    • 行缓冲流:行缓冲流会在遇到换行符时进行数据的读取或写入,否则将数据存储在内部缓冲区中。当达到一行结束时,或者遇到缓冲区已满的情况,才进行实际的I/O操作。如:使用BufferedReaderBufferedWriter包装的字符流对象

    • 缓冲流(全缓冲流):缓冲流缓存一定量的数据,减少了实际I/O操作的次数,提高了读写的效率。在从输入设备读取数据或向输出设备写入数据时,缓冲流会先将数据存储在内部缓冲区中,当缓冲区满时才进行实际的读写操作。如:BufferedInputStreamBufferedOutputStream用于处理二进制数据,以及BufferedReaderBufferedWriter用于处理字符数据

#include <stdio.h>     //标准IO的头文件
int main(int argc,char*argv[]){
    printf("Hello world1\n");//遇到换行符,输出
    printf("Hello world2");//程序结束后,会清空缓存,输出
}
  1. 标准I/O-stdin,stdout,stderr

    • stdin(Standard Input)0:标准输入流是程序从用户获取输入数据的通道。通过stdin,程序可以读取用户在终端键盘上输入的数据。

    • stdout(Standard Output)1:标准输出流是程序向用户输出结果的通道。通过stdout,程序可以将文本、数据等输出到终端显示器上。

    • stderr(Standard Error)2:标准错误流是程序在发生错误或异常时输出错误信息的通道。与stdout类似,stderr也将输出内容发送到终端显示器上。

文件的打开和关闭
  1. 打开文件FILE * fopen(const char * filename, const char * mode)

    • 该函数用于打开一个文件,并返回一个指向 FILE 结构体的指针,表示文件流

    • filename:要打开的文件的路径字符串。可以是相对路径或绝对路径。

    • mode:打开文件的模式字符串,指定打开方式。常用的模式包括:

    • "r":以只读方式打开文件。如果文件不存在,打开失败。

    • "w":以写入方式打开文件。如果文件存在,则清空文件内容;如果文件不存在,则创建新文件。

    • "a":以追加方式打开文件。如果文件存在,则在文件末尾追加内容;如果文件不存在,则创建新文件。

mode模式描述
" r"只读方式打开文件。如果文件不存在,打开失败“rb”
“ r+”读写方式打开文件,其他同“r”“r+b”
" w"写入方式打开文件。如果文件存在,则清空文件内容;如果文件不存在,则创建新文件“wb”
" w+"读写方式打开文件,其他同“w”“w+b”
" a"追加方式打开文件。如果文件存在,则在文件末尾追加内容;如果文件不存在,则创建新文件“ab”
" a+"读写方式打开文件,其他同“a”“a+b”
  1. 关闭文件:int fclose(FILE * stream)

    • 该函数用于关闭指定的文件流。它接受一个 FILE 结构体指针作为参数,表示要关闭的文件流
    • 如果关闭成功,则返回 0 ;否则返回 EOF(-1),并设置errno
    • 流关闭时自动刷新缓冲中的数据并释放缓冲区
    • 当一个程序正常终止时,所有打开的流都会被关闭
  2. 处理错误信息

  • perror(const char* str)
    • perror 函数将上一个发生的系统错误转换为人类可读的错误描述,并将其输出到标准错误流(stderr)。
    • 可以传递一个字符串参数 str,用于在错误描述之前添加额外的说明信息。
    • 如果发生了错误,它会打印类似于 “str: 错误描述” 的内容,其中 “str” 是传递的字符串参数,“错误描述” 是根据错误代码生成的具体错误信息。
  • char* strerror(int errnum)
    • strerror 函数将给定的错误号 errnum 转换为对应的错误描述字符串。
    • 它返回一个指针,指向一个包含错误描述的静态字符数组。
    • errnum 通常是 errno 全局变量的值,用于表示最近发生的错误。
#include <stdio.h>		 //标准IO的头文件
#include <errno.h>		//perror,streeeor
#include <string.h>
 int main(int argc,char*argv[]){
     FILE *fp;
     fp = fopen("1.txt","r");//以只读方式打开当前文件夹下的1.txt文件
     if(fp==NULL){
         perror("Open file failed:");//若文件不存在,输出“Open file failed:No such file or directory”
         printf("Open file failed:%s\n",strerror(errno));//strerror 函数将 errno 的值转换为相应的错误描述字符串,并将其打印出来
     }else{
         printf("Open file success\n");
         fpret = fclose(fp);//关闭文件
         if(fpret == 0){
             printf("File close sucess\n");
         }else{
             perror("fclose error:");
         }
     }
  return 0;   
 }
标准IO的读写
  1. 流支持不同的读写方式:
  • 读写一个字符:fgetc()/fputc()一次读/写一个字符
  • 读写一行:fgets()/fputs一次读/写一行
  • 读写若干个对象:fread()/fwrite()每次读/写若干个对象,而每个对象具有相同的长度
  1. 读写单个字符
  • int fgetc(FILE* stream)

    • fgetc,从指定的文件流 stream 中读取下一个字符,并将其作为无符号字符转换为整数类型返回。

    • 如果读取成功,则返回读取的字符的整数表示。

    • 如果到达文件末尾或发生错误,则返回 EOF

    • 等同于getc

    • getchar等同于fgetc(stdin),标准输入(键盘)

int rec;
rec = fgetc(fp);	//取出文件的一个字符
printf("Get char=%c\n",rec);
rec = getchar();	//阻塞,等待键盘输入
printf("Get STD input=%c\n",rec);
  • int fputc(int c, FILE* stream)

    • fputc,将整数 c 对应的字符写入到指定的文件流 stream 中。

    • 如果写入成功,则返回写入的字符的整数表示。

    • 如果发生错误,则返回 EOF

    • 等同于putc

    • putchar(c)等同于 fputc(c,stdout)

int rec,wrc = 'w';
rec = fputc(wrc,fp);//文件需有写入格式打开才可写入,否则报错
if (rec==-1){	//出错处理
    perror("fputc:");
    fclose(fp);
    return 0;
}
fputchar(wrc);
  1. 按行输入
  • char *fgets(char *str, int num, FILE *stream)

    • fgets,从给定的文件流中读取一行文本,并将其存储在指定的字符数组str中。
    • 它会读取最多num-1个字符或者遇到换行符为止,然后在末尾添加null终止符(‘\0’)。
    • 如果成功读取一行,则返回指向str的指针;否则返回NULL
    • 注意:gets函数已被淘汰,因为会导致缓存区溢出
  • int fputs(const char *str, FILE *stream)

    • fputs,将给定的字符串str写入到指定的文件流中。
    • 它会将字符串中的所有字符都写入文件,直到遇到null字符为止。
    • 如果成功写入,则返回非负值;否则返回EOF。
ret = fgets(buff,5,stdin);//键盘输入
ret = fgets(buff,5,pf);//文件读取
if(ret==NULL){
    perror("fgets:");
    fclose(fp);
    return 0;
}
printf("buff=%s\n",buff);

puts("hello world1");//将缓冲区总督字符串输出到stdout,并追加'\n'
fputs("hello world2",stdout);//不最加'\n'
fputs("hello world2",fp);//写入到文件(文件以可写方式打开)
  1. 二进制读写
  • size_t fread(void *ptr, size_t size, size_t count, FILE *stream)
    • fread,从给定的文件流中读取二进制数据,并将其存储到指定的内存块ptr中。
    • 读取count个数据项,每个数据项的大小为size字节,并将读取的数据存储在ptr中。
    • 如果成功读取了全部或部分数据,则返回实际读取的数据项个数,否则返回一个小于count的值
char *buff;
buff = (char*)malloc(100);//申请内存
ret = fread(buff,10,1,fp);
printf("buff=%s\n",buff);
free(buff);
  • size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream)
    • fwrite,将二进制数据从给定的内存块ptr写入到指定的文件流中。
    • count个数据项,每个数据项的大小为size字节,从ptr中写入到文件流中。
    • 如果成功写入了全部或部分数据,则返回实际写入的数据项个数,否则返回一个小于count的值。
struct student{
    char name[10];
    int age;
    char sex[8];
}stu;
strcpy(stu.name,"zhangsan");
stu.age = 28;
strcpy(stu.sex,"male");

ret = fwrite(&stu,sizeof(stu),1,fp); 
流刷新定位、格式化输入输出
  1. 流的刷新:fflush函数
  • int fflush(FILE *fp);
    • 刷新输出缓冲区,以确保将缓冲区中的数据立即写入到目标设备,如文件或终端。
    • 成功时返回0,出错放回EOF
    • Linux下只能刷新输出缓冲区,输入缓冲区丢弃
  1. 流的定位:ftell、fseek和rewind函数
  • 进行文件流的定位操作。

  • ftell函数:long ftell(FILE *stream);

    • ftell函数用于获取当前文件指针的位置

    • 参数stream是一个指向FILE对象的指针,该对象表示要进行定位操作的文件流。ftell函数返回一个long类型的值,表示当前文件指针相对于文件起始位置的偏移量(以字节为单位)。如果调用失败,它将返回-1。

    • 注意:以 a 模式打开文件,fseek无效

  • fseek函数:int fseek(FILE *stream, long offset, int origin);

    • fseek函数用于将文件指针移动到指定位置

    • 参数stream是一个指向FILE对象的指针,offset是一个长整型的值,表示要移动的偏移量,可正负。origin指定了基准点,可以取:SEEK_SET,SEEK_CUR,SEEK_END

    • SEEK_SET:从文件起始位置开始计算偏移量。

    • SEEK_CUR:从当前文件指针位置开始计算偏移量。

    • SEEK_END:从文件末尾位置开始计算偏移量。 fseek函数返回0表示成功,返回非零值表示失败。

  • rewind函数:void rewind(FILE *stream);

    • rewind函数用于将文件指针重置到文件起始位置

    • 参数stream是一个指向FILE对象的指针。该函数不返回任何值。

    • 这些定位函数在文件操作中非常有用。通过ftell函数可以获取当前文件指针的位置,fseek函数可以将文件指针移动到指定位置,而rewind函数则可以将文件指针重置到起始位置。这样就可以根据需要随时定位文件流的读写位置。

    • rewind(fp)相当于fseek(fp,0,SEEK_SET)

  • 注意:这三个函数只适用2G以下的文件

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
        printf("Failed to open the file.");
        return 1;
    }

    long position = ftell(file); //ftell函数获取当前文件指针的位置
    printf("Current position: %ld\n", position);
    
    fseek(file, 10L, SEEK_SET); //fseek函数将文件指针移动到从文件起始位置偏移10个字节的位置
     rewind(file); //rewind函数将文件指针重置到文件起始位置

    fclose(file);
    return 0;
}
  1. 格式化输出
  • printf函数:int printf(const char *fmt,...);
    • printf函数将格式化的字符串输出到控制台
  • fprintf函数:int fprintf(FILE *stream,const char *fmt,...);
    • fprintf函数将格式化的字符串写入到文件中
  • sprintf函数:int sprintf(char *s,const char *fmt,..);
    • sprintf函数将中格式化的字符串写入到字符数组str
  • 成功返回输出的字符个数,出错时返回EOF
#include<stdio.h>

int main(){
    int num=42;
    char str[100];
    
    sprintf(str,"The number is :%d",num);
    printf("%s\n",str);
    
    FILE *fp = fopen("output.txt","w");
    if (fp == NULL){
        printf("Failed to open the file.");
        return 1;
    }
    fprintf(file,"The number is:%d\n",num);
    
    fclose(fp);
    return 0;
}
  1. 格式化输入
  • fscanf函数:int fscanf(FILE *stream,const char *format,...);
    • fscanf函数从文件中读取格式化的内容
  • sscanf函数:int sscanf(const char *str,const char *format,...);
    • sscanf函数从格式化的字符串中读取内容
#include <stdio.h>

int main() {
    FILE *fp = fopen("input.txt", "r");
    if (fp == NULL) {
        printf("Failed to open the file.");
        return 1;
    }

    int num;
    fscanf(fp, "%d", &num);
    printf("The number is: %d\n", num);

    fclose(file);
    
    char str[] = "The number is: 42";
    sscanf(str, "The number is: %d", &num);
    printf("The number is: %d\n", num);
    
    return 0;
}

文件I/O

  • 文件 I/O(Input/Output)是指在计算机系统中对文件进行读取和写入的操作。它允许程序从外部存储器(如硬盘或网络)读取数据,并将数据写入到文件中。

    • 文件 I/O 的实现方式因编程语言而异。在 Python 中,可以使用内置的 open() 函数打开文件,然后使用文件对象的方法读取或写入数据。
    • 不提供缓冲机制
  • POSIX(Portable Operating System Interface for Unix)是一个面向操作系统接口的标准。定义了许多操作系统功能和服务的接口,包括进程管理、线程、文件系统、I/O、网络、信号处理、时间和日期等。它还提供了用于编译和链接的宏和常量,以及用于命令行和脚本编程的工具。

    • 提高程序的可移植性、促进代码重用、促进互操作性、
标准I/O文件I/O(低级IO)
打开fopen,freopen,fdopenopen
关闭fcloseclose
getc,fgetc,getchar; fgets,gets; greadread
putc,fputs,putchar; fputs,puts; fwritewrite
  1. open
include<fcntl.h>
int open(const char*pathname,int flags);//打开文件
int open(const chat*pathname,int flags,made_t mode);//创建文件,mode指定新文件的权限
//真正建文件时的权限会受到umask值影响,实际权限时mode-umaks
  • 成功时返回文件描述符,出错时返回EOF

  • 可以打开设备文件,但不能创建设备文件
    在这里插入图片描述

  • umask:用来设定文件或目录的初始权限

    • 文件或目录的初始权限 = 文件或目录的最大默认权限 - umask权限
  1. close
#include<unistd.h>
int close(int fd);//用于关闭一个打开的文件
  • 成功时返回0,出错时返回EOF
  • 程序结束时自动关闭所有打开的文件
  • 文件关闭后,文件描述符不再代表文件
  1. read、write
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t count);//从文件读取数据
ssize_t write(int fd,void *buf,size_t count);//向文件写入内容
  • 成功时返回实际读取的字节数;出错时返回EOF
  • 读到文件末尾时返回0
  • buf是接收数据的缓冲区
  • count不应超过buf大小
  1. lseek
#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
  • 成功时返回当前的文件读写位置,出错时返回EOF

  • 参数offset和参数whence同fseek完全一样

标准I/O与文件I/O
  • 相同点:

    • 数据交互:都是用于数据的输入和输出,实现程序与外部环境的数据交互。
    • 数据流:两者都是通过数据流的方式进行读取和写入操作。
  • 不同点:

    • 数据来源和去向:标准I/O主要用于从标准输入设备(例如键盘)读取数据或将数据输出到标准输出设备(例如终端),而文件I/O主要用于读取和写入文件中的数据。
    • I/O对象:标准I/O使用标准输入流(stdin)、标准输出流(stdout)和标准错误流(stderr)作为I/O对象,通过这些对象进行输入和输出。文件I/O使用文件对象作为I/O对象,通过打开和操作文件对象来进行读写操作。
    • 编程接口:标准I/O通常以函数调用的形式提供,例如在C语言中使用scanf()printf()等函数。文件I/O则常通过文件对象的方法和属性进行操作,例如在Python中使用read()write()等方法。
    • 数据存储:标准I/O的数据通常是临时性的,不会被永久保存,而文件I/O的数据通常会被写入到文件中,进行持久化存储。
    • 使用环境:标准I/O是在终端或控制台等交互式环境中使用较多,而文件I/O则适用于需要读写文件的情况,比如处理大量数据、配置文件操作等。
文件属性和目录操作
  1. 访问目录
  • opendir :函数用于打开一个目录文件
#include<dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd);//使用文件描述符,要配合opendir使用
  • DIR是用来描述一个打开的目录文件的结构体类型
  • 成功时返回目录流指针,出错时返回NULL
  1. chmod / fchmod:修改文件访问权限
#include<sys/stat.h>
int chmod(const char*path,mode_t mode);
int fchmod(int fd, mode_t mode);
  • 成功时返回0;出错时返回EOF
  • root和文件所有者修改文件的访问权限
    • chmod(“test.txt”, 0666);
  1. stat / lstat / fstat:获取文件属性
#include<sys/stat.h>
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd,struct stat *buf);
  • 成功时返回0;出错时返回EOF

  • 如果path是符号链接,stat获取的是目标文件的属性,而lstat获取的是链接文件的属性

  • stat结构体中包含了多个字段来描述文件的各种属性信息,如:

    • st_dev:文件所在设备的ID。

    • st_ino:文件的inode节点号。

    • st_mode:文件的类型和访问权限。

    • st_nlink:文件的硬链接数目。

    • st_uid:文件所有者的用户标识符。

    • st_gid:文件所有者的组标识符。

    • st_size:文件大小(字节数)。

    • st_blksize:文件系统的I/O缓冲区大小。

    • st_blocks:分配给文件的块数。
      在这里插入图片描述

静态库和动态库
  1. 库的概念

    • 库是一个二进制文件,包含的代码可被程序调用

    • 标准C库,数学库,线程库

    • 库有源码,可下载编译,也可以直接安装二进制文件

    • /lib、/usr/lib

    • Linux下包含静态库和动态库

  2. 静态库

    • 编译(链接)时静态库中相关代码复制到可执行文件中;程序中已包含代码,运行时不再需要静态库;程序运行时无需加载库,运行速度更快

    • 占用更多磁盘和内存空间;静态库升级后,程序需要重新编译链接

  • 静态库的创建

    1. 确定库中函数的功能、接口
    2. 编写库源码hello.c
    #include<stdio.h>
    void hello(void){
      printf("hello world\n");
      return;
    }
    
    1. 编译生成.o目标文件:$ gcc -c hello.c -Wall
    2. ar命令创建静态库hello:$ ar -rsv libhello.a hello.o
    • 静态库文件名: 前缀(lib)、后缀(.a(Unix/Linux 平台).lib(Windows 平台))
ar参数描述ar参数描述
c创建一个新的静态库文件,如果该文件已存在,则先删除旧的静态库文件再创建v将建立新库的逐个文件的详细描述写至标准输出
r将指定的文件添加到静态库中,如果文件已存在于库中,则替换原有的文件q快速追加文件到静态库中,不对库文件进行索引,适用于对已存在的库进行追加操作
s创建一个包含符号表的静态库,用于保存对象文件中的全局符号信息t列出静态库中包含的文件名列表
  • 链接静态库

    1. 编写运用程序test.c
    #include<stdio.h>
    void hello(void);
    int main(){
    hello();
    return;
    }
    
    1. 编写test.c并链接静态库libhello.a
    • $ gcc -o test test.c -L. -lhello
    • $ gcc -o 目标文件 源码 -L路径 -l库名称
  1. 动态库
    • 程序不包含库中代码,尺寸小
    • 多个程序可共享同一个库
    • 程序运行时需要加载库
    • 库升级方便,无需重新编译程序
    • 使用更加广泛
  • 创建动态库

    1. 生成位置无关代码的目标文件:gcc -c -fPIC xxx.c xxxx.c ...
    2. 生成动态库:gcc -shared -o libxxx.so xxx.o xxx.o ...
    • 动态库文件名:前缀(lib),后缀(.so(Unix/Linux 平台),.dll(Windows 平台))
  • 链接动态库

    1. 编写运用程序test.c
    #include<stdio.h>
    #include "common.h"
    int main(){
    bye();
    return;
    }
    
    1. ​ 编写test.c并链接静态库libcommon.so
    • $ gcc -o test test.c -L. -lcommon

    • $ gcc -o 目标文件 源码 -L路径 -l库名称

  • 共享库:让系统能找到要加载的共享库

    • 把库拷贝到/usr/lib和/lib目录下
    • 把LD_LIBRARY_PATH环境变量中添加库所在路径:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库目录
    • 添加再~/.bashrc 文件里面,使用 source ~/.bashrc生效
    • 添加/etc/ld.so.conf.d/*.conf文件,执行Idconfig刷新
  • 查看可执行文件使用的动态库

    • Ldd命令:ldd 可执行文件
      xx.o xxx.o …`

    • 动态库文件名:前缀(lib),后缀(.so(Unix/Linux 平台),.dll(Windows 平台))

  • 链接动态库

    1. 编写运用程序test.c
    #include<stdio.h>
    #include "common.h"
    int main(){
    bye();
    return;
    }
    
    1. ​ 编写test.c并链接静态库libcommon.so
    • $ gcc -o test test.c -L. -lcommon

    • $ gcc -o 目标文件 源码 -L路径 -l库名称

  • 共享库:让系统能找到要加载的共享库

    • 把库拷贝到/usr/lib和/lib目录下
    • 把LD_LIBRARY_PATH环境变量中添加库所在路径:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库目录
    • 添加再~/.bashrc 文件里面,使用 source ~/.bashrc生效
    • 添加/etc/ld.so.conf.d/*.conf文件,执行Idconfig刷新
  • 查看可执行文件使用的动态库

    • Ldd命令:ldd 可执行文件
  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懵逼树下的懵逼果

谢谢谢谢谢,好人一生平安

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值