当前位置: 首页 > news >正文

第13章 文件输入/输出

目录

  • 13.1 和文件进行通信
        • 13.1.1 文件是什么
        • 13.1.2 文本视图和二进制视图
        • 13.1.3 I/O级别
        • 13.1.4 标准文件
  • 13.2 标准I/O
        • 13.2.1 检查命令行参数
        • 13.2.2 fopen()函数
        • 13.2.3 getc()函数和putc()函数
        • 13.2.4 文件结尾
        • 13.2.5 fclose()函数
        • 13.2.6 标准文件指针
  • 13.3 一个简单的文件压缩程序
  • 13.4 文件I/O:fprintf()、fscanf()、fgets()和fputs()函数
  • 13.5 随机存取:fseek()和ftell()函数
  • 13.6 标准I/O内幕
  • 13.7 其他标准I/O函数
        • 13.7.1 int ungetc(int c,FILE *fp)函数
        • 13.7.2 int fflush()函数
        • 13.7.3 int setvbuf()函数
        • 13.7.4 二进制I/O:fread()和fwrite()函数
        • 13.7.5 size_t fwrite()函数
        • 13.7.6 size_t fread()函数
        • 13.7.7 int feof(FILE *fp)和int ferror(FILE *fp)函数
        • 13.7.8 一个fread()和fwrite()的例子
        • 13.7.9 使用二进制I/O进行随机存取
  • 13.11 编程练习
        • 习题6
        • 习题7
        • 习题12、13

13.1 和文件进行通信

13.1.1 文件是什么
  • 一个文件通常就是磁盘上的 一段命名的存储区
  • 对于操作系统来说,文件会更复杂一些。例如,一个大文件可以存储在一些分散的区段中,或者还会有一些可以使操作系统确定其文件类型的附加数据,但是这些是操作系统而不是程序员(除非你在编写操作系统)要考虑的
  • C将文件看成是连续的字节序列,其中每一个字节都可以单独地读取。因为其他环境中的文件模型可能会有所不同,所以ANSI C提供了文件的两种视图:文本视图和二进制视图
13.1.2 文本视图和二进制视图
  • ANSI要求提供的两种文件视图是文本视图和二进制视图。在二进制视图中,文件中的每个字节都可以为程序所访问。在文本视图中,程序看到的内容和文件的内容有可能不同
13.1.3 I/O级别
  • 除了可以选择文件视图,在大多数情况下,您还可以在两个I/O级别(即两种处理文件存储的级别)中进行选择低级I/O使用操作系统提供的基本I/O服务;标准高级I/O使用一个标准的C库函数包和stdio.h头文件中的定义。因为无法保证所有的操作系统都可以用相同的低级I/O模型表示,所以ANSI C只支持标准I/O包
13.1.4 标准文件
  • C程序自动为您打开3个文件。这三个文件被称为标准输入,标准输出和标准错误输出。默认的标准输入是系统的一般输入设备,通常为键盘;默认的标准输出和标准错误输出是系统的一般输出设备,通常为显示器。
  • 标准错误输出提供一个可供发送错误消息的逻辑上不同的位置。标准错误不受标准输出重定向的影响。例如,如果使用重定向方法将输出发送到一个文件中而不是屏幕上,发送到标准错误输出的输出内容仍然会被发送到屏幕上

    阅读本章“文件是什么”小节时,突然意识到:软件,不管是应用软件,还是操作系统软件,通过各种计算机语言(C、Java等等)在内存中构造数据结构很重要,这些数据结构就是现实世界中元素抽象成计算机世界中的元素保存在内存中,在计算机世界通过指令构建数据结构之间的逻辑关系,然后解决现实世界的问题
    操作系统软件中的,进程、虚拟内存和文件的索引结点inode等概念,都是在内存中创建相应的数据结构来描述(也是对底层的抽象),再结合对应的硬件驱动实现功能。
    应用层软件更加方便地用高级语言创建相应的数据结构描述现实世界中的元素,然后用高级语言构建逻辑关系解决问题。操作系统封装很多常用接口,让应用层调用,操作系统对底层硬件的封装抽象,让应用层更方便的使用硬件


13.2 标准I/O

13.2.1 检查命令行参数
  • exit()函数关闭所有打开的文件并终止程序。exit()函数的参数会被传递给一些操作系统,包括UNIX、Linux和MS DOS,以供其他程序使用。通常约定正常终止传递值0,非正常终止传递非0值。不同的退出值可以用来标识导致程序失败的不同原因
  • ANSI C标准规定使用一个相当有限的最小范围。具体地,该标准要求使用值0或宏EXIT_SUCCESS来指示程序成功终止,使用宏EXIT_FAILURE指示程序非成功终止
  • 按照ANSI C,在最初调用的main()中使用return和调用exit()的效果相同。要注意我们所说的“最初调用”。如果main()在一个递归程序中,exit()仍然会终止程序;但return将控制权移交给递归的前一级,直到最初那一级,此时return才终止程序return和exit()的另一个区别是,即使在除main()以外的的函数中调用exit(),它也将终止程序
13.2.2 fopen()函数

fopen()函数的模式字符串

模式字符串意义
“r”打开一个文本文件,可以读取文件
“w”打开一个文本文件,可以写入文件,先将文件的长度截为0。如果该文件不存在则先创建之
“a”打开一个文本文件,可以写入文件,向已有文件的尾部追加内容。如果该文件不存在则先创建之
“r+”打开一个文本文件,可以进行更新,也即可以读取和写入文件
“w+”打开一个文本文件,可以进行更新(读取和写入),如果文件存在则首先将文件的长度截为0。如果该文件不存在则先创建之
“a+”打开一个文本文件,可以进行更新(读取和写入),向已有文件的尾部追加内容。如果该文件不存在则先创建之;可以读取整个文件,但写入时只能追加内容
“rb”,“wb”,“ab”,“ab+”,“a+b”,“wb+”,“w+b”,“ab+”,“a+b”与前面的模式相似,只是使用二进制模式而非文本模式打开文件
  • 对于像Unix和Linux这样只有一种文件类型的系统,带b字母的模式和对应的不带b字母的模式是相同的
  • 文件指针(比如这个例子中的fp)是一种指向FILE的指针;FILE是stdio.h中定义的一种派生类型指针fp并不指向实际的文件,而是指向一个关于文件的信息的数据包,其中包括文件I/O使用的缓冲区信息。因为标准库的I/O函数使用缓冲区,所以它们需要知道缓冲区的位置,还需要知道缓冲区的当前缓冲能力以及所使用的文件。这样这些函数在必要时可以再次填充或者清空缓冲区。fp指向的数据包中包含全部这些信息。
13.2.3 getc()函数和putc()函数
  • getc()、putc()这两个函数的工作方式和函数getchar()、putchar()非常相似,不同之处在于您需要告诉getc()和putc()函数它们要使用的文件。实际上后者一般通过前者定义
13.2.4 文件结尾
  • 为了避免试图读取空文件带来的问题,应该对文件输入使用入口条件循环(而不是do while循环)。
13.2.5 fclose()函数
  • fclose(fp)函数关闭由指针fp指定的文件,同时根据需要刷新缓冲区。更正规的程序也许还要检查是否成功关闭了文件。如果文件成功关闭,fclose()函数将返回值0,否则返回EOF。
13.2.6 标准文件指针



13.3 一个简单的文件压缩程序

  • 每个文件的打开与关闭都独立于另一个。同时可以打开的文件数目是有限的,这个限制取决于系统和实现,范围通常是10到20之间。可以使用同一个文件指针指向不同的文件,但前提是不能同时打开这些文件



13.4 文件I/O:fprintf()、fscanf()、fgets()和fputs()函数

  • 文件I/O函数fprintf()、fscanf()和printf()、scanf()相似,区别在于前两者需要第一个参数来指定合适的文件。
  • rewind()函数接受一个文件指针参数,使程序回到文件开始处
  • fgets()函数读取到它所遇到的第一个换行符的后面,或者读取比字符串的最大长度少一个的字符,或者读取到文件结尾。然后fgets()函数向末尾添加一个空字符以构成一个字符串。如果fgets()函数在达到字符最大数目之前读完了一整行,它将在字符串的空字符前添加一个换行符以标识一行结束
  • fputs()函数接受两个参数,它们依次是一个字符串的地址和一个文件指针。它把字符串地址指针所指的字符串写入指定的文件。

    打开文件是我们获得一个和文件关联的流,不管是标准输入输出流,还是普通的流,都有各自的缓冲区,我们的IO操作看似在对文件操作,其实是对文件关联的流的缓冲区操作。读取文件是在读取流的缓冲区,如果缓冲区为空,通过磁盘IO操作填充缓冲区,然后再从流的缓冲区读数据;写入文件是将数据写入流的缓冲区,缓冲区要根据自己的刷新策略来刷新缓冲区数据。


13.5 随机存取:fseek()和ftell()函数

  • fseek()函数允许您像对待数组那样对待一个文件,在fopen()打开的文件中直接移动到任意字节处。
    • fseek()接受3个参数,返回一个int指。如果一切正常返回0。如果有错误出现,返回-1。
      • 第一个参数是指向被搜索文件的FILE指针
      • 第二个参数称为偏移量,表示从起点开始要移动的距离。这个参数必须是一个long类型值,可以为正(前移)、负(后移),也可以为零(保持不动)
      • 第三个参数是模式,用来标识起始点
  • ftell()函数以一个long类型值返回一个文件的当前位置,ftell()函数通过返回距文件开始处的字节数目来确定文件的位置。文件的第一个字节到文件开始处的距离是字节0
  • 在 C 语言中,fseek() 和 ftell() 的计数是从 0 开始的(即文件开头的位置是 0,而不是 1)。这与 C 语言的数组索引、指针偏移等概念一致
  • 注意,由于不同操作系统对文件系统的支持不同,有的系统文件格式只有二进制模式,有的系统文件格式有文本模式和二进制格式,导致fseek()和ftell()在处理用文本模式打开的文件会不精确
    • 打开文件时如果以文本模式打开,由于不同系统的文件格式可能不同,ftell()函数在返回文件当前位置时可能出错。例如,有些系统表示文件中的换行符用“\r\n”,C中统一用“\n”表示,这种情况下ftell()返回的当前位置就比文件中少1。所以通常用二进制模式打开文件来进行精确的位置定位
  • fgetpos()和fsetpos()函数
    • fseek()和ftell()的一个潜在问题是:它们限制文件的大小只能在long类型的表示范围内
    • ANSI C引入了两个用来处理较大文件的新的定位函数,fgetpos()和fsetpos()。这两个函数不用long类型值,而是用称为fpos_t(代表file position type,文件定位类型)的新类型来代表位置。
      • fpos_t不是基本类型,而是通过其他类型定义的。fpos_t类型的变量或者数据对象可以用来指定一个文件位置
      • fpos_t类型不能是一种数组类型,但除此之外不再有其他限制
    • fgetpos():保存当前文件位置到 fpos_t。fsetpos():恢复之前保存的文件位置。



13.6 标准I/O内幕

  • fopen()函数打开一个流。如果文件以文本模式打开,可以得到一个文本流;如果以二进制模式打开,那么就得到一个二进制流。
  • fopen()函数不仅打开一个文件,而且建立了一个缓冲区,还创建了一个包含文件和缓冲区相关数据地数据结构。不仅如此,fopen()还返回一个指向该数据结构的指针,供其他函数知道如何找到该结构
    • 这个数据结构通常包括一个文件位置指示器,以确定在流中的当前位置。它还包括错误指示器文件结尾指示器、一个指向缓冲区起始处的指针一个文件标识符,和一个记录实际复制到缓冲区中的字节数的计数器
  • 标准I/O函数如何调用系统调用的?
    • 初次调用标准I/O的任意一个输入函数,因为缓冲区为空,都会通过系统调用把一块数据从文件复制到缓冲区中,再从缓冲区复制到输入函数中参数指定的存储区。除了填充缓冲区外,初次函数调用还将设置文件对应的FILE结构体中的值
    • 当输入函数检测到已经读取了缓冲区中的全部字符时,它会通过系统调用请求系统将下一块缓冲区大小的数据复制到缓冲区。通过这种方式,输入函数可以读入文件中的全部内容,直到文件结尾。函数在读入最后一缓冲区数据中的最后一个字符后,会将文件结尾指示器的值设置为真



13.7 其他标准I/O函数

13.7.1 int ungetc(int c,FILE *fp)函数
  • int ungetc(int c,FILE *fp)函数将c指定的字符放回输入流中。如果向输入流中放入一个字符,下一次调用标准输入函数就会读入那个字符。
  • ANSI C标准保证每次只会放回一个字符。如果一个C实现允许将一行里的多个字符放回输入流,那么输入函数就会以与放回时相反的顺序来读入。
13.7.2 int fflush()函数
  • 调用fflush()函数可以将缓冲区中任何未写的数据发送到一个由fp指定的输出文件中去。
  • 如果fp是一个空指针,将刷新掉所有的输出缓冲
  • 对一个输入流使用fflush()函数的效果没有定义。只要最近一次使用流的操作不是输入操作,就可以对一个更新流(任何读写模式的流)使用这个函数
13.7.3 int setvbuf()函数
  • int setvbuf(FILE * restrict fp,char * restrict buf,int mode,size_t size)函数建立了一个供标准I/O函数使用的替换缓冲区。打开文件后,在没有对流进行任何操作以前,可以调用这个函数。如果成功执行,函数会返回零,否则返回一个非零值。
    • 由指针fp来指定流,
    • buf指向将使用的存储区。如果buf的值不是NULL,就必须创建这个缓冲区。如果buf的值是NULL,函数会自动为自己分配一个缓冲区。size变量为setvbuf()函数指定数组的大小
    • mode将从下列选项中选取:_IOFBF表示完全缓冲(缓冲区满时刷新),_IOLBF表示行缓冲(缓冲区满时或者一个新行写入的时候刷新),_IONBF表示无缓冲
13.7.4 二进制I/O:fread()和fwrite()函数
  • 以前所使用的标准I/O函数都是面向文本的,用于处理字符和字符串。如果要把数字数据保存到一个文件中,该怎么办呢?的确可以使用fprintf()函数和%f格式保存一个浮点值,不过这样就将数字转换成字符存储了。
    • 将数字转换为字符的过程中,占用的空间变了,精度也可能改变了。例如,double类型的数据本身是不精确的,可以通过转换说明修饰符调整输出double类型数据的精度。
    • 最精确和一致的存储数字的方法就是使用与程序所使用的相同的位格式。在内存中怎么存储就应该原样存储到文件中,而不是将原数据通过fprintf()转换成字符,再把字符的二进制表示值存储到文件中。
    • 如果把数据存储在一个使用与程序具有相同表示方法的文件中,就称数据以二进制形式存储。标准I/O中的fread()和fwrite()函数提供了这种二进制服务。
  • 如果文件中的全部数据都以字符编码的形式被解读,我们才称该文件包含文本数据。如果这些数据的部分或者全部以二进制形式的数字数据被解读,就称文件包含二进制数据。
  • 术语“二进制”和“文本”的使用可能造成混淆。ANSI C认可两种打开文件的模式:二进制模式和文本模式。很多操作系统认可两种文件格式:二进制格式和文本格式
    • 信息可以作为二进制数据或者文本数据存储和读取。这些都是互相关联,但又不完全相同。可以用二进制模式打开文本格式的文件,可以将文本存储在二进制格式的文件中,也可以使用getc()函数复制包含二进制数据的文件。
    • 通常情况下,还是使用二进制模式将二进制数据存储到二进制格式的文件中。与之类似,用的最频繁的还是使用以文本模式打开文本文件中的文本数据
13.7.5 size_t fwrite()函数
  • 函数原型是:size_t fwrite(const void * restrict ptr,size_t size,size_t nmemb,FILE *restrict fp);
    • size_t类型是根据标准C类型定义的。它是sizeof运算符返回的类型,通常是unsigned int类型,不过具体的实现中可以选择其他类型
    • 指针ptr是要写入数据库的地址。
    • size表示要写入的数据库的大小(以字节为单位)。
    • nmemb表示数据块的数目。
    • 变量类型很多,占用的字节数不同。利用size和nmemb可以让fwrite()适用不同的数据类型
    • 函数返回成功写入的项目数。正常情况下,它与nmemb相等,如果写入出错,返回值会小于nmemb
  • fwrite()函数的第一个参数不是固定的类型,所以用void *ptr来声明。在ANSI C函数的原型下,传入的实际参数都被转换成指向void的指针,这种指针作为普通的指针类型工作(ANSI之前的C对这一参数使用char *类型,需要将实际参数的类型指派为这一类型)。
13.7.6 size_t fread()函数
  • fread()函数与fwrite()函数的参数相同。fread()函数返回成功读入的项目数。正常情况下,它与nmemb相等;不过如果有读取错误的话,返回值就会小于nmemb
13.7.7 int feof(FILE *fp)和int ferror(FILE *fp)函数
  • 当标准输入函数返回EOF时,通常表示已经到达了文件结尾。也有可能表示发生了读取错误,使用feof()和ferror()函数可以区分这两种可能性
    • 如果最近一次输入调用检测到文件结尾,feof()函数返回一个非零值,否则返回零值。
    • 如果发生读写错误,ferror()函数返回一个非零值,否则返回零值。
13.7.8 一个fread()和fwrite()的例子
13.7.9 使用二进制I/O进行随机存取

    EOF 并不是文件中实际存储的字符,而是由标准I/O库或系统调用返回的一个状态标志(如 fgetc 返回 EOF 宏,通常是 -1)。在底层,文件系统不会在文件末尾存储特殊字符来表示EOF,而是通过文件元数据(如文件大小)确定文件边界


13.11 编程练习

    本章关于对文件的操作有点陌生,花了很多时间才稍微流畅的使用书中的输入输出函数,书中的练习做完后回头看,好像又觉得没那么难。

习题6

    加深了我对字符串处理函数的印象,至少知道了处理字符串的问题,要到string.h头文件中找相关函数。标准输入的内部缓冲区的处理,是不是有换行符,是不是有字符残留,等都需要注意。

#include <stdio.h>
#include <windows.h>
#include <string.h>
#define N 20int combname(char *,const char *);int main(void)
{SetConsoleOutputCP(65001); // 设置为UTF-8  我用的Editplus写代码,需要调用此函数让控制台能支持中文char name[N];FILE * fs,*fr;int len,count;char ch;count=0;fprintf(stdout,"输入要压缩的文件名不要超过%d字符或者输入空行结束程序:\n",N);while (fgets(name,N,stdin)==NULL){fprintf(stdout,"输入要压缩的文件名不要超过%d字符或者输入空行结束程序:\n",N);}if (name[0]=='\n'&&name[1]=='\0')			//输入空行就结束程序{fprintf(stdout,"程序结束!!!\n");exit(0);}if (name[strlen(name)-1]=='\n')			//name字符串以后一个字符是'\n',说明标准输入内部缓冲区读完了{name[strlen(name)-1]='\0';}else						//否则就是没读完,清空标准输入内部缓冲区{while(getchar()!='\n')         continue;}if ((fs=fopen(name,"r"))==NULL){fprintf(stderr,"%s文件打开失败,结束程序!!!\n",name);exit(1);}len=combname(name,".red");if (len>=N){printf("文件名%s长度过长,程序结束!!!\n",name);exit(2);}if ((fr=fopen(name,"w"))==NULL){fprintf(stdout,"文件%s打开失败,程序结束!!!\n",name);exit(1);}while ((ch=getc(fs))!=EOF)             //压缩文件{if (ch=='\n'){putc(ch,fr);continue;}elsecount++;if (count%3==0){putc(ch,fr);}}fclose(fs);fclose(fr);return 0;
}int combname(char * name,const char * back)     //原文件名中插入指定后缀名
{char * point;char temp[N];int len;point=strchr(name,'.');strcpy(temp,point);strcpy(point,back);strcat(name,temp);return strlen(name);
}
习题7

    写程序时,分析程序,划分功能模块,找出模块之间或模块内部元素之间的逻辑关系。针对单独模块开发,不要受其他因素影响,是我要不断练习的。
    fopen()函数以“w”模式打开文件,并不是说每次对文件操作都会导致文件清空,写入新内容。而是指关闭文件后,下次再以“w”模式打开,会清空文件内容,写入新内容。清空是以fopen()打开文件为基准的
    注意feof()和ferror()两个函数的灵活运用。

#include <stdio.h>
#include <windows.h>void printfile(FILE *,FILE *);
void printline(FILE *,FILE *);int main(int argc,char * argv[])
{SetConsoleOutputCP(65001); // 设置为UTF-8  我用的Editplus写代码,需要调用此函数让控制台能支持中文FILE * file1,* file2;if ((file1=fopen(argv[1],"r"))==NULL){fprintf(stderr,"文件%s打开失败,结束程序!!!",argv[1]);exit(1);}if ((file2=fopen(argv[2],"r"))==NULL){fprintf(stderr,"文件%s打开失败,结束程序!!!",argv[2]);exit(1);}printfile(file1,file2);rewind(file1);rewind(file2);printline(file1,file2);fclose(file1);fclose(file2);return 0;
}void printfile(FILE * file1,FILE * file2)
{FILE * file3;char ch1,ch2;char name[]="yb3.txt";if ((file3=fopen(name,"w"))==NULL){fprintf(stderr,"文件%s打开失败,结束本函数调用!!!!\n",name);return;}while (feof(file1)==0||feof(file2)==0){while ((ch1=getc(file1))!=EOF){putc(ch1,file3);if (ch1=='\n'){break;}}while ((ch2=getc(file2))!=EOF){putc(ch2,file3);if (ch2=='\n'){break;}}}
}void printline(FILE * file1,FILE * file2)
{FILE * file4;char name[]="yb4.txt";char ch1,ch2;if ((file4=fopen(name,"w"))==NULL){fprintf(stderr,"文件%s打开失败,结束本函数调用!!!!\n",name);return;}while (feof(file1)==0||feof(file2)==0){while ((ch1=getc(file1))!=EOF){if (ch1=='\n'){break;}putc(ch1,file4);		}while ((ch2=getc(file2))!=EOF){putc(ch2,file4);if (ch2=='\n'){break;}}}
}
习题12、13

    srand()和rand()这两个产生随机数的函数要了解。二维数组的边界处理要注意,我感觉自己的边界处理方法不巧妙,不知道有没有其他方法。

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <time.h>void createnumfile(void);
void printimg(const int[][30]);
void fixnums(int [][30]);int main(void)
{SetConsoleOutputCP(65001); // 设置为UTF-8  我用的Editplus写代码,需要调用此函数让控制台能支持中文int img[20][30];int i,j,num;FILE * fn;createnumfile();if ((fn=fopen("nums.txt","r"))==NULL){fprintf(stderr,"读取%s文件打开失败,程序结束!!!\n","nums.txt");exit(2);}for (i=0;i<20 ;i++ )			//从创建的文件中读取数据初始化二维数组img{for (j=0;j<30 ;j++ ){fscanf(fn,"%d",&num);img[i][j]=num;}}fclose(fn);printimg(img);fixnums(img);fprintf(stdout,"\n\n");printimg(img);return 0;
}void createnumfile()			//创建一个保存二维int数组的文件
{FILE * fn;int i,j;srand(time(0));				//根据时间产生随机数的种子if ((fn=fopen("nums.txt","w"))==NULL){fprintf(stderr,"创建%s文件打开失败,程序结束!!!\n","num.txt");exit(1);}for (i=0;i<20 ;i++ ){for (j=0;j<30 ;j++ ){fprintf(fn,"%d ",rand()%10);    //获得一个0-9之间的随机数}fprintf(fn,"\n");}fclose(fn);}void printimg(const int img[][30])			//把不同数字对应不同的字符输出
{int i,j;for (i=0;i<20 ;i++ ){for (j=0;j<30 ;j++ ){switch (img[i][j]){case 0:putc(' ',stdout);break;case 1:putc('.',stdout);break;case 2:putc('\'',stdout);break;case 3:putc(',',stdout);break;case 4:putc('~',stdout);break;case 5:putc('*',stdout);break;case 6:putc('$',stdout);break;case 7:putc('&',stdout);break;case 8:putc('%',stdout);break;case 9:putc('#',stdout);break;default:break;}}putc('\n',stdout);}
}void fixnums(int img[][30])                  //按照13题的要求修改二维数组中的数据
{int i,j;for (i=0;i<20 ;i++ ){for (j=0;j<30 ;j++ )     //注意二维数组上下左右四个边界的求值{if (i==0&&j==0)		//左上角求值{if (((img[i][j]-img[i][j+1])>1||(img[i][j]-img[i][j+1])<-1)&&((img[i][j]-img[i+1][j])>1||(img[i][j]-img[i+1][j])<-1)){img[i][j]=(img[i][j+1]+img[i+1][j])/2;}}else if (i==0&&j==29)   //右上角求值{if (((img[i][j]-img[i][j-1])>1||(img[i][j]-img[i][j-1])<-1)&&((img[i][j]-img[i+1][j])>1||(img[i][j]-img[i+1][j])<-1)){img[i][j]=(img[i][j-1]+img[i+1][j])/2;}}else if (i==19&&j==0)  //左下角求值{if (((img[i][j]-img[i][j+1])>1||(img[i][j]-img[i][j+1])<-1)&&((img[i][j]-img[i-1][j])>1||(img[i][j]-img[i-1][j])<-1)){img[i][j]=(img[i][j+1]+img[i-1][j])/2;}}else if (i==19&&j==29)	//右下角求值{if (((img[i][j]-img[i][j-1])>1||(img[i][j]-img[i][j-1])<-1)&&((img[i][j]-img[i-1][j])>1||(img[i][j]-img[i-1][j])<-1)){img[i][j]=(img[i][j-1]+img[i-1][j])/2;}}else if (i==0)		//第一行求值{if (((img[i][j]-img[i][j+1])>1||(img[i][j]-img[i][j+1])<-1)&&((img[i][j]-img[i+1][j])>1||(img[i][j]-img[i+1][j])<-1)&&((img[i][j]-img[i][j-1])>1||(img[i][j]-img[i][j-1])<-1)){img[i][j]=(img[i][j+1]+img[i+1][j]+img[i][j-1])/3;}}else if (i==19)	//最后一行求值{if (((img[i][j]-img[i][j+1])>1||(img[i][j]-img[i][j+1])<-1)&&((img[i][j]-img[i-1][j])>1||(img[i][j]-img[i-1][j])<-1)&&((img[i][j]-img[i][j-1])>1||(img[i][j]-img[i][j-1])<-1)){img[i][j]=(img[i][j+1]+img[i-1][j]+img[i][j-1])/3;}}else			//中间的大部分数据求值{if (((img[i][j]-img[i][j+1])>1||(img[i][j]-img[i][j+1])<-1)&&((img[i][j]-img[i-1][j])>1||(img[i][j]-img[i-1][j])<-1)&&((img[i][j]-img[i][j-1])>1||(img[i][j]-img[i][j-1])<-1)&&((img[i][j]-img[i+1][j])>1||(img[i][j]-img[i+1][j])<-1)){img[i][j]=(img[i][j+1]+img[i-1][j]+img[i][j-1]+img[i+1][j])/4;}}}}
}
http://www.lryc.cn/news/609402.html

相关文章:

  • MySQL半同步复制机制详解:AFTER_SYNC vs AFTER_COMMIT 的优劣与选择
  • 前后端交流
  • Git常用命令详解
  • RSA 解密逻辑
  • 微服务的使用
  • AI生成图片工具分享!
  • 常见框架漏洞靶场攻略
  • 【LeetCode刷题指南】--对称二叉树,另一颗树的子树
  • C++入门自学Day5-- C/C++内存管理(续)
  • C语言数据结构(7)贪吃蛇项目2.贪吃蛇项目实现
  • Linux 文件系统基本管理
  • python 12 install jupyter时zmq.h或libzmq报错处理
  • 基于springboot的在线考试系统/考试信息管理平台
  • 苍穹外卖项目学习——day1(项目概述、环境搭建)
  • 团队独立思考的力量
  • 机器学习——决策树(DecisionTree)
  • 波士顿房价预测工具 - XGBoost实现
  • 三、驱动篇-HDF驱动介绍1
  • 【Unity】背包系统 + 物品管理窗口 (上)
  • Python 的标准库 bisect 模块
  • 从WebShell 与 ShellCode 免杀技术 打造适合自己的免杀技术链
  • [Oracle] 获取系统当前日期
  • 使用AssemblyAI将音频数据转换成文本
  • [Oracle] TO_DATE()函数
  • gpu instancer crowd 使用自定义材质并且只修改单个物体的材质参数
  • 机器学习 决策树基本介绍
  • [2025ICCV-目标检测方向]DuET:通过无示例任务算术进行双增量对象检测
  • 数据结构:单向链表的函数创建
  • kubernetes基础知识
  • io_cancel系统调用及示例