资讯详情

资讯详情

C程序设计基础_文件简介

文件简介Part 1 文件概念引例操作系统中的文件一切皆文件文件是操作系统用于数据管理的基本单位为什么需要文件特性寄存器内存 (RAM)外存如硬盘、SSD缓存 (Cache)主要用途存储 CPU 中临时数据和指令存储正在运行的程序和数据存储长期数据、程序、文档提高 CPU 访问速度存储临时数据存取速度极快皮秒级快速纳秒级较慢毫秒级非常快纳秒级存储介质CPU 内部的硬件电路半导体材料如 DRAM 芯片磁盘、固态硬盘Flash 存储高速静态随机存取存储器SRAM易失性易失性易失性非易失性非易失性容量极小较小通常 GB 级较大通常 GB/TB 级较小KB/MB 级成本极高较高较低非常高数据访问方式随机访问随机访问顺序访问 / 随机访问随机访问写 建立 内存 ⇌ 文件 ⇌ 外存 读 找到文件的基本概念文件存储于外存上已命名的相关数据的有序集合命名称为文件名结构一般为主文件名.扩展名如stdio.h。其中主文件名是必须的扩展名则可有可无。扩展名只影响了操作系统对该文件的默认运行方式与文件本身的内容无关修改扩展名也不会破坏文件内容。文件名长度是有限制的。早期Windows及DOS系统对文件名采用8.3规则即主文件名最多8个字符扩展名最多3个字符。现代操作系统则放宽了对文件名长度限制最大长度一般可达255个字符。文件名命名规则同样存在限制由操作系统设定不同操作系统对文件名命名限制不同。一般而言限制包括特殊字符、保留符号等。为了用户高效管理文件文件名命名应做到英文为主见名知义。文件的基本操作创建文件、写文件、读文件、重定向文件、删除文件、截断文件文件的操作流程创建/打开文件、I/O 文件、关闭文件文件分类按格式分文本文件ASCII 码、二进制文件机器码/数值按属性分普通文件、设备文件文本文件与二进制文件以 12345 为例内存中的 123452 个字节00110000 00111001文本文件中的 12345逐字符存储共 5 个字节字符‘1’‘2’‘3’‘4’‘5’ASCII 编码0011000100110010001100110011010000110101二进制文件中的 123452 个字节连续存放与内存中相同00110000 00111001标准文件设备文件标准文件文件指针常用设备文件描述符标准输入stdin键盘0标准输出stdout显示器1标准错误stderr显示器2键盘、显示器等设备通常称为终端设备。文件系统的层次结构路径描述文件或目录名称指向文件系统中唯一位置根目录最顶层目录Linux/Windows驱动器号:\子目录与当前目录直接邻接的下层目录父目录与当前目录直接邻接的上层目录路径分隔符Linux/Windows\路径分类绝对路径路径下的绝对位置从根目录起的完整路径相对路径相对于当前目录的简化路径当前目录.父目录..通常情况下.可省略注意在程序中\必须转义后使用即写为\\缓冲机制的引入为什么需要缓冲机制特性寄存器内存 (RAM)外存如硬盘、SSD缓存 (Cache)主要用途存储 CPU 中临时数据和指令存储正在运行的程序和数据存储长期数据、程序、文档提高 CPU 访问速度存储临时数据存取速度极快皮秒级快速纳秒级较慢毫秒级非常快纳秒级存储介质CPU 内部的硬件电路半导体材料如 DRAM 芯片磁盘、固态硬盘Flash 存储高速静态随机存取存储器SRAM易失性易失性易失性非易失性非易失性容量极小较小通常 GB 级较大通常 GB/TB 级较小KB/MB 级成本极高较高较低非常高数据访问方式随机访问随机访问顺序访问 / 随机访问随机访问缓冲机制核心思想在传输速度存在明显差异的存储器间引入一段速度介于二者之间的中间区域某一存储器发出数据在中间区域积累待达到一定量或满足一定条件时中间区域批量传输给另一存储器并自我刷新缓冲区缓冲区缓冲机制的具体实现一般用内存作为缓冲区缓冲策略全缓冲达到容量、行缓冲遇换行符、无缓冲缓冲区刷新时机缓冲区满、行缓冲模式下遇换行符、关闭文件、程序正常结束、手动刷新fflush手动刷新缓冲区一般只用于输出流intfflush(FILE*stream);缓冲区溢出固定大小的缓冲区写入的数据超出其容量剩余数据覆盖相邻内存区域且无有效机制阻止导致内存的非法越界访问。常见诱发函数gets、strcpy文件缓冲区文件缓冲区缓冲机制在文件系统中的具体应用用于在程序与磁盘文件之间优化数据传输I/O操作 自动完成 程序 ⇌ 文件缓冲区 ⇌ 磁盘文件 程序控制 操作系统分类输入缓冲区、输出缓冲区普通文件流通常为全缓冲stdin/stdout 连接终端时为行缓冲stderr 为无缓冲思考为什么在终端输入数据时在本行正在输入的数据可被修改按下回车后就无法修改了以文件缓冲区看文件操作流程创建文件操作系统返回一个文件描述符用于标识被创建的文件打开文件操作系统自动在内存中创建文件缓冲区缓冲区大小由编译器/标准库决定如 glibc 中BUFSIZ为 8192 字节早期 UNIX 系统常见为 512B。执行打开操作后需对文件进行测试。I/O 文件读数据时操作系统自动将数据从磁盘扇区导入文件缓冲区程序处理完后会继续导入下一个扇区的数据写数据时程序将数据写入文件缓冲区缓冲区满后操作系统自动写入磁盘扇区并清空缓冲区。关闭文件不及时关闭文件就会造成资源泄露关闭文件同时会强制将文件缓冲区的数据写入文件以免数据丢失。Part 2 C 语言对文件的处理C 语言中的文件文件的逻辑结构流式文件字节流每个字节都能被单独处理以文件结尾标识符EOF结束文件访问模式文本模式自动处理换行符和字符编码、二进制模式不自动进行任何转换和处理FILEstdio.h中定义的结构体将文件流抽象表示用于描述文件的基本信息和内部结构文件指针FILE*stream;文件的打开、重定向与关闭以文件指针角度看文件的打开与关闭打开文件将文件指针指向该文件关闭文件断开文件指针与文件间的联系fopen打开文件FILE*fopen(char*filename,char*mode);filename文件名字符串包含文件路径mode文件打开方式字符串组成操作类型 模式类型若打开成功返回指向该文件的指针否则返回空指针模式类型模式类型含义t(text)文本模式b(binary)二进制模式缺省则以文本模式打开操作类型操作类型含义读写若文件不存在若文件已存在文件当前位置指针r(read)以只读方式打开已存在的文件✓×出错正常打开文件首r以读写方式打开已存在的文件✓✓出错正常打开文件首w(write)以只写方式打开文件×✓创建新文件清空原文件文件首w以读写方式打开文件✓✓创建新文件清空原文件文件首a(append)以追加方式打开文件×✓创建新文件从文件尾继续写无法覆盖原内容文件尾a以追加/读写方式打开文件✓✓创建新文件从文件尾继续写无法覆盖原内容文件尾位于操作类型/文件类型之后均可注意a/a的写入始终发生在文件尾a打开后读取的起始位置由实现定义读取前通常需要fseek/rewind明确定位。fopen 返回 NULL 的可能原因以只读方式打开时文件不存在路径不正确文件已被打开测试文件是否正常打开FILE*fpfopen(...);if(!fp){...}freopen文件的重定向FILE*freopen(char*filename,char*mode,FILE*stream);流的重定向替换原流指向将原流指向其他流。后续对原流的操作全部被作用到新流上freopen重定向stream到filename常用于重定向设备文件stdin、stdout 等到普通文件若重定向成功则返回该流指针否则返回 NULLfclose关闭已打开的文件intfclose(FILE*stream);若关闭成功则返回 0否则返回 EOF文件当前位置指针文件的访问形式顺序访问、随机访问文件的前方为指向文件头的方向文件的后方为指向文件尾的方向文件当前位置指针记录在 FILE 结构体内部具体成员名由实现定义不对外暴露它是相对于文件头的偏移字节数用于指向文件的当前 I/O 位置打开文件时编译器根据文件的打开方式自动将文件当前指针设置为文件首或文件尾并随 I/O 操作自动向后移动文件当前位置指针范围0 - 文件长度关于文件当前位置指针中的指针文件当前位置指针中的指针只是一个为了表示当前文件处理进度的形象化模型它并非真正的内存地址指针而是一个逻辑位置帮助我们理解如何在文件中移动和访问数据。文件结尾标识符文件结尾标识符 (EOF)stdio.h中定义的值为 -1 的符号常量用于表示文件结束或发生文件 I/O 错误。空文件的文件头与 EOF 重合手动插入 EOFWindowsCtrlZLinuxCtrlD注意EOF 是一种抽象概念并非实际字符或数据而是指代文件中最后一个字节的之后位置的逻辑状态用于告知进程何时停止读取以下两种写法是等价的while(scanf(...)!EOF){...}while(~scanf(...)){...}ferror检测文件 I/O 是否发生错误intferror(FILE*stream);若发生文件 I/O 错误则返回非零值真否则返回 0假feof检测文件当前指针是否指向文件尾intfeof(FILE*stream);在 I/O 操作后若因文件当前位置指针指向文件尾而返回 EOF 导致 I/O 失败则返回真否则返回假典型滥用feof 并不主动检测文件#includestdio.hintmain(){FILE*fpfopen(1.txt,r);// 文件是否为空无关紧要fseek(fp,0,SEEK_END);// 移动当前位置指针至文件尾if(feof(fp))printf(Yes.);// 典型滥用 feofelseprintf(No.);// 由于 feof 原理本例永远只输出 Nofclose(fp);return0;}feof表示 I/O 之后的状态仅当因文件当前位置指针指向文件结尾而导致的 I/O 失败后调用feof才返回真绝对禁止在无任何 I/O 就调用 feof否则会出错文件的随机访问ftell获取文件当前位置指针longftell(FILE*stream);若文件当前位置有效则返回文件当前位置指针否则返回 -1Lrewind移动文件当前位置指针到文件头voidrewind(FILE*stream);fseek移动文件当前位置指针intfseek(FILE*stream,longoffset,intwhence);若操作成功则返回 0否则返回 -1offset相对于 whence 的偏移offset 取值范围含义 0相对于 whence 向文件尾方向移动 0相对于 whence 不动 0相对于 whence 向文件头方向移动文件的前方为指向文件头的方向文件的后方为指向文件尾的方向whence移动后的文件当前位置指针的起始点位置模式由stdio.h中定义了模式的符号常量模式符号常量起始位置整数值字面常量SEEK_SET文件首0SEEK_CUR文件当前位置1SEEK_END文件尾2实例文件处理简易进度条思路分析打开文件fopen以r模式打开文件获取文件长度fseek移动文件当前位置指针到文件尾ftell获取文件当前位置指针此处即为文件长度rewind/fseek将文件当前位置指针归位到文件首循环处理开始处理文件中的每个字符直到 EOF 结束在每次循环处理中用ftell获取文件当前位置指针并与文件大小相除即为文件当前处理进度条关闭文件fclose关闭文件文件 I/O格式化 I/Oscanf fprintf 终端 ⇌ 内存 ⇌ 文件 printf fscanffscanf格式化读取文件intfscanf(FILE*stream,char*format[,…]);若读取成功返回读取的数据个数若读到文件尾或出错返回 EOFfprintf格式化写入文件intfprintf(FILE*stream,char*format[,…]);若写入成功返回写入的数据个数若写入出错返回 EOF扩展内容字符串的格式化 I/Oscanf sprintf 终端 ⇌ 内存 ⇌ 内存中的字符串 printf sscanfsscanf从字符串中格式化读取intsscanf(char*source,char*format[,...]);若读取成功返回成功读取个数若到达文件尾或出错返回 EOFsprintf格式化输出到字符串intsprintf(char*buffer,char*format[,...]);若写入成功返回写入字符总数 count−1除去末尾\0否则返回 EOF字符 I/Ogetchar/fgetchar putc/fputc 终端 ⇌ 内存 ⇌ 文件 putchar/fputchar getc/fgetcgetc/fgetc字符方式读文件intfgetc(FILE*stream);若读取成功返回读取字符的 ASCII 码值否则返回 EOFputc/fputc字符方式写入文件intfputc(intc,FILE*stream);若写入成功返回写入字符的 ASCII 码值否则返回 EOF字符串 I/Ogets fputs 终端 ⇌ 内存 ⇌ 文件 puts fgetsfgets字符串方式读文件char*fgets(char*buffer,intn,FILE*stream);中止条件换行符 / EOF / 读入 n-1 个字符结尾\0占用不丢弃\n若读取成功返回字符串指针若读到文件尾或出错返回 NULLfputs字符串方式写文件intfputs(char*buffer,FILE*stream);不写入\n。若成功返回最后写入字符 ASCII 码否则返回 EOF文件的二进制 I/Ofread以数据块方式读文件size_tfread(void*ptr,size_tsize,size_tn,FILE*stream);从文件当前位置指针处起连续读取 n 个大小为 size 字节的数据块存储到 ptr 中。返回值为成功读取块的数量0-nfwrite以数据块方式写文件size_tfwrite(void*ptr,size_tsize,size_tn,FILE*stream);将 ptr 中连续 n 个大小为 size 字节的数据块写入到文件当前位置指针处。返回值为成功写入块的数量0-n总结I/O 函数函数名功能scanf、printfstdin、stdout 格式化 I/Ofscanf、fprintf文件格式化 I/Ogetchar、putcharstdin、stdout 以单个字符 I/Ogetc/fgetc、putc/fputc文件中以单个字符 I/Ogets、putsstdin、stdout 以字符串 I/Ogets丢弃\nputs加\nfgets、fputs文件中以字符串 I/Ofgets读\nfputs不加\nsscanf、sprintf字符串中 I/Ofread、fwrite文件的二进制 I/OPart 3 文件对程序设计结构的影响多文件程序按源代码文件数量分单文件程序、多文件程序多文件程序的设计思想高度模块化一个文件只实现一个/一类功能多个文件组合完成复杂功能模块划分.h 头文件包含程序文件模块的共享信息如头文件、符号常量定义、全局变量定义、函数原型、数据结构的定义等.c/.cpp 程序源代码文件包含main函数和其他函数定义。有且仅能有一个程序源代码文件包含main函数作用域、链接、存储期标识符用于区分和标识不同对象包括变量名、函数名等作用域程序中标识符的可访问区域包括块作用域块/循环头、函数作用域、函数原型作用域函数形参、文件作用域全局链接程序中对标识符的可引用情况外部链接文件作用域标识符可在所有文件中使用通过extern关键字声明内部链接文件作用域仅在当前文件中有效通过static关键字声明无链接块作用域、函数作用域和函数原型作用域标识符仅在其对应的块或函数内有效外部无法访问存储期程序中标识符的生存周期包括静态存储期文件作用域、线程存储期、自动存储期块作用域/auto、动态分配存储期手动申请到手动释放/进程结束存储类别修饰符变量auto自动变量默认省略auto关键字。若未进行初始化将会被填充随机的垃圾数据register寄存器变量一种直接放入寄存器中的无地址变量使用register关键字请求将该变量存放在寄存器中能否放入寄存器取决于编译器和操作系统extern外部变量static静态变量函数extern外部函数static静态函数inline内联函数旨在尽可能快速调用函数常见实现是将函数调用替换函数体内容存储类别关键字关键字存储类别存储期作用域链接声明方式auto自动自动块无块内register寄存器自动块无块内extern静态外部链接静态文件外部所有函数外static静态内部链接静态文件内部所有函数外static静态无链接静态块无块内extern、static声明可进行多次仅告知编译器某个标识符的存在但并不分配内存定义只能进行一次通常需要先声明再定义定义包含声明自动在相应内存区域中申请内存并与标识符绑定extern声明一个对象表明其来自外部通常用于扩展对象作用域变量外部链接静态变量简称外部变量即默认定义的全局变量函数外部函数即默认定义的函数extern定义的对象可被所有文件所访问定义时默认省略extern关键字在所需文件中使用extern声明从而扩展对象作用域static表示一个对象为静态在进程运行前其就被存储于内存静态存储区中变量局部静态变量块内定义的、用static修饰的局部变量仅被初始化一次进程运行期间持续存在退出作用域后其值保持不变内部链接静态变量简称静态全局变量将作用域限制于当前文件其他文件无法调用函数内部函数用static修饰的函数将作用域限制于当前文件其他文件无法调用预处理命令define、undef预处理器执行预处理命令、处理注释所有注释替换为空格等预处理命令以#开头的指令define宏符号常量定义命令宏是一种文本替换替换过程为宏展开替换列表中可使用先前定义的宏类对象宏将宏调用替换为替换列表中的内容#defineMACRO_NAMEREPLACEMENT_LIST替换列表为数值时应当添加括号#includestdio.h#defineMOD10000000007intmain(){printf(%lld,(longlong)MOD*2);// 宏展开为 (long long)10000000007*2 1000000014return0;}为避免出现此情况应对数值宏加括号#defineMOD(10000000007)// 展开为 (long long)(10000000007)*2 2000000014类函数宏具有形参表的宏接受传入实参对应地传递到替换列表之后宏调用会被替换列表替代#defineMACRO_NAME(PARAMETER_LIST)REPLACEMENT_LIST类函数宏举例#defineSUM(X,Y)(XY)// 宏定义SUM(2,3)// 宏调用替换列表为数值时应当添加括号#includestdio.h#defineCALC(X)X*Xintmain(){printf(%lld,100/CALC(23));// 宏展开后为 100/23*23return0;}为避免出现此情况应对数值宏加括号#defineSQUARE(X)((X)*(X))注意即使加了括号类函数宏也只是文本替换传入带副作用的参数如SQUARE(i)会使i被求值两次应避免这样使用。undef取消定义宏#undefMACRO_NAME适用于类对象宏、类函数宏includeinclude文件包含命令#includeHEADER// 系统目录#includeHEADER// 指定路径 系统目录条件编译嵌套包含同一个头文件被重复包含条件编译常与宏配合使用根据条件选择性地编译代码#if … #endif 命令检查宏值或宏是否被定义#ifCondition1// 布尔表达式// Compiled Code 1#elifCondition2// Compiled Code 2...#else// Compiled Code n#endif#elif、#else可省略defined检查宏是否定义预处理运算符常作为#if … #endif的条件#ifdefined MACRO_NAME1// Compiled Code 1#elifdefined MACRO_NAME2// Compiled Code 2...#else// Compiled Code n#endif#ifdef … #endif 命令检查宏是否已定义#ifdefMACRO_NAME1// Compiled Code 1#elifMACRO_NAME2// Compiled Code 2...#else// Compiled Code n#endif与#if defined MACRO_NAME等价#elif、#else可省略#ifndef … #endif 命令检查宏是否未定义#ifndefMACRO_NAME1// Compiled Code 1#elifMACRO_NAME2// Compiled Code 2...#else// Compiled Code n#endif与#if !defined MACRO_NAME等价#elif、#else可省略实例#ifndef 检查是否在 OJ 评测环境多数 OJ 在评测环境中会通过编译命令行定义宏ONLINE_JUDGE用于表明在评测环境。因此可将调试代码放在条件编译中避免提交代码时反复删除调试信息。举例#ifndef freopenI/O本地 → 普通文件OJ → stdin/stdout#ifndefONLINE_JUDGEfreopen(1.in,r,stdin);freopen(1.out,w,stdout);#endif编译指示#pragma向编译器发送指令#pragmaPARA(...)常用命令#pragmaonce// 用于头文件中确保其只能被包含一次#pragmawarning(disable:4996)// 忽略 MSVC 编译器对不安全函数检查#pragmaGCCoptimize(2)// 手动开启 O2 优化
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →