linux文件信息详解,linux文件内容操作命令
标准输入输出库
标准I/O库及其头文件为底层I/O系统调用提供了一个通用接口。这个库不是ANSI标准C的一部分,我们前面提到的系统调用也不是,但是这个库提供了许多复杂的函数来处理打印格式和描述输入。同时,它会小心处理设备所需的缓冲区。
在许多方面,我们可以通过使用低级文件描述符来使用这个库。我们需要打开文件来建立一个访问路径。这将返回一个值,该值将被用作调用其他I/O库函数的参数。这相当于低级文件描述符,称为流,实现为指向结构FILE*的指针。
当一个程序启动时,它会自动打开三个文件流。它们是标准输入、标准输出、标准错误。这些在stdio.h中定义,分别代表标准输入、标准输出和标准错误输出。相反,它们分别对应于低级文件描述符0、1和2。
在下一部分中,我们将看到以下内容:
福彭,弗克洛斯
弗瑞德,弗赖特
清空缓冲区
设置文件位置指示器
fgetc,getc,getchar
fputc,putc,putchar
fgets,gets
printf,fprintf,sprintf
scanf、fscanf、sscanf
打开文件
fopen库函数是对低级开放系统调用的模拟。我们主要用于文件或终端输入输出。但是,在需要显示控制设备的地方,我们最好使用低级别的系统调用,因为它们可以消除库带来的潜在不良因素,比如输入/输出缓冲区。
其语法格式如下:
#包含stdio.h
FILE *fopen(const char *filename,const char * mode);
Fopen打开由filename参数指定的文件,并建立与之相关的流。mode参数指示如何打开文件。它可以是下列字符串之一:
“r”或“rb”:以只读方式打开。
“w”或“wb”:以只写方式打开。
a 或 ab :以阅读方式打开并添加到文件末尾。
“r”或“rb”或“r b”:打开更新(读和写)
w 或 wb 或 w b :打开更新并使其长度为零。
a 或 a b 或 ab :打开更新并将其添加到文件的末尾。
b表示该文件是二进制文件,而不是文本文件。
这里要注意的是,与MS-DOS不同,Unix和Linux不区分文本文件和二进制文件。Unix和Linux对所有文件都一视同仁,尤其是二进制文件。另一点需要注意的是,模式参数必须是字符串,而不是字符。我们应该总是用r而不是r。
如果函数调用成功,fopen将返回一个非空的文件指针。如果失败,它将返回NULL,这是在stdio.h中定义的.
从文件中读
fread库函数可用于从文件流中读取数据。从流中读取的数据将被放置在prt指定的数据缓冲区中。fread和fwrite都处理数据记录。这些由块大小和读取单元的次数指定。如果成功,返回值是实际读入数据缓冲区的块数,而不是字节数。在文件末尾,它可能返回一个小于nitems的值,并且
其语法格式如下:
#包含stdio.h
size_t fread(void *ptr,size_t size,size _ t nitems,FILE * stream);
与所有要写入缓冲区的标准I/O函数一样,程序员负责分配数据空间和检查错误。
写入文件
Fwrite调用类似fread的函数接口。它将从指定的数据区读取数据记录,并将它们写入输出流。他的返回值是成功写入的记录数。
其语法格式如下:
#包含stdio.h
size_t fwrite (const void *ptr,size_t size,size _ t nitems,FILE * stream);
这里需要注意的是,在使用结构化数据时,我们不建议使用fread和fwrite。部分原因是用fwrite编写的文件在不同的机器之间可能不兼容。
关闭文件
fclose库函数关闭指定的文件流,并将所有未写入的数据写入文件。使用fclose相当重要,因为stdio库缓存数据。如果一个程序需要确保所有的数据都被完全写入,它应该调用fclose。然而,当一个程序正常结束时,fclose将被自动调用,从而关闭所有仍然打开的文件流。当然,在这种情况下,我们没有机会检查fclose报告的错误。像文件描述符的所有限制一样,可用流的数量也是有限的。实际限制是FOPEN_MAX,它是在stdio.h中定义的,它至少是8。
其语法格式如下:
#包含stdio.h
int fclose(FILE * stream);
清空缓冲区
fflush库函数可以使未写入文件流中的所有数据立即写入文件流。例如,我们可以使用这个函数来确保在尝试读取输入之前,交互提示已经发送到终端。此功能确保在继续操作之前,所有重要数据都已写入磁盘文件。在调试一个程序的时候,我们有时候可以用这个函数来保证程序是在写文件,而不是执行一个空操作。还有一点需要注意的是,我们调用fclose的时候是隐式调用flush操作,所以不需要在fclose之前调用fflush。
其语法格式如下:
#包含stdio.h
int fflush(FILE * stream);
设置文件位置指示器
fseeek函数是一个相当于lstat系统调用的函数操作。它将被设置在该文件流中要读取或写入的下一个位置。offset和whence的含义和用法与前面提到的lseek相同。但lseek返回off_t,而fseek返回整数:成功则返回0,失败则返回-1,用errno表示错误。因此,这将更加重要。
其语法格式如下:
#包含stdio.h
int fseek(FILE *stream,long int offset,int where);
fgetc,getc,getchar
Fgets函数将文件流中的下一个字节作为字符返回。当它到达文件的末尾或发生错误时,它将返回EOF。我们必须用ferror或feof来区分这两种情况。
其语法格式如下:
#包含stdio.h
int fgetc(FILE * stream);
int getc(FILE * stream);
int getchar();
getc函数与fgetc函数的功能相同,只是前者是以宏的形式实现的。在这种情况下,流参数必须没有副作用(例如,它不能影响局部变量或作为参数传递给函数的变量)。同时也不能用getc的地址作为函数指针。
Getchar函数与getc(stdin)的功能相同,从标准输入中读取下一个字符。
fputc,putc,putchar
fputc函数将一个字符写入输出文件流。它将返回它所写的值,如果失败,则返回EOF。
其语法格式如下:
#包含stdio.h
int fputc(int c,FILE * stream);
int putc(int c,FILE * stream);
int putchar(int c);
与fgetc/getc类似,putc函数与fputc的功能相同,但也许会实现为宏。GNU C编译器会这样做,我们可以在stdio.h头文件中看到它的定义。
putchar函数与putc(c,stdout)的功能相同,都是将单个字符写入标准输出。这里要注意的是,putchar把字符当作整数而不是字符,这和getchar返回字符的结果是一样的。这允许当文件结束标记(EOF)超出字符数边界时用-1标记。
fgets,gets
Fgets函数从输入文件流中读取一个字符串。它会把读取的字符放在S指向的位置,直到遇到新的一行,传输n-1个字符,或者遇到文件结尾时返回,这是首先会发生的事情。遇到的任何换行符都将被传输到接收到的字符串,并添加一个结束字节/0。在任何一次调用中最多只能传输n-1个字符,因为需要
其语法格式如下:
#包含stdio.h
char *fgets(char *s,int n,FILE * stream);
char * gets(char * s);
如果函数调用成功,它将返回一个指向s的指针,如果流到达文件的末尾,它将为流设置EOF标记并返回一个空指针。如果发生读取错误,fgets将返回一个空指针,并设置errno来指示错误类型。
gets函数类似于fgets,只是前者从标准输入中读取字符串,并忽略任何换行符。它给接收到的字符添加一个终止符。
这里要注意的是,gets函数并不限制传输的字符数,所以会超过他们的传输缓冲区。所以要避免使用这个函数,改用fgets函数。所以我们要谨慎使用这个功能。
格式和输出
有许多库函数可以按照我们想要的方式产生输出,如果我们有一些C编程的经验,我们就会熟悉这些格式。这些函数包括将数据写入文件流的prinf和其他函数,以及从文件流读取数据的scanf和其他函数。
printf,fprintf,sprintf
printf函数族可以格式化和输出不同类型的可变参数。输出流中表示的每一个函数的工作模式都是由format参数控制的,format参数包含了要打印的常用字符串和代码,也就是称为转义符的部分,用来指示如何以及在哪里打印其余的参数。
其语法格式如下:
#包含stdio.h
int printf(const char *格式,);
int sprintf(char *s,const char *format,);
int fprintf(文件*流,常量字符*格式,);
printf函数在标准输出上生成其输出。fprintf函数在指定的流上生成其输出,而sprintf函数将其输出和一个结束空字符写入字符串S,该字符串作为参数传递。这个字符必须足够大,以包含所有的输出。printf函数族中还有其他函数可以用来以不同的方式处理不同的参数。我们可以通过查看printf手册页获得更多的详细信息。
普通字符在传递到输出后不会改变。转义字符将导致printf检索并格式化其余传递的参数。它们通常以%字符开头,如下例所示:
printf("某些数字:%d、%d和%d/n ",1、2、3);
他的标准输出结果如下:
一些数字:1、2和3
如果我们想打印一个%字符,我们必须使用%%,这样它就不会与转义字符混淆。
以下是一些最常见的转义字符:
%d,%i:将整数打印为小数
%o,%x:以八进制和十六进制数字打印。
%c:打印一个字符
%s:打印字符串。
%f:打印浮点数(单精度数)
%e:以定点格式打印一个双精度数。
%g:以正常格式打印一个双数。
与格式字符串中传递给printf函数的转义字符相匹配的参数的类型和数量非常重要。可选的大小标识符可用于指示整数参数的类型。这可以是h,例如,% hd表示短整型,也可以是l,例如,%ld表示长整型。有些编译器可以检查printf的这些参数,但它们并不绝对可靠。如果我们正在使用GNU编译器gcc,我们可以使用-Wformat来
比如下面这个例子:
char initial= A
char * surname=" Matthew
双倍年龄=14.5;
printf("您好小姐%c %s,年龄%g/n ",首字母,姓氏,年龄);
该示例的结果如下:
你好,马修小姐,14.5岁
如果我们使用域标识符,我们可以更好地控制打印模式。这些扩展了转义字符,以便控制输出中的空格。常见的用法是为打印浮点数指定十进制数字空间,或为字符串指定打印空间。
在转义字符的%字符后用数字指定域。下表包含一些转义字符及其输出结果的示例。
格式参数输出
s打招呼你好
%-10s“你好”你好
d 1234 1234
%-10d 1234 1234
0d 1234 0000001234
.4f 12.34 12.3400美元
%*s 10,"你好" 你好
所有这些示例都以10个字符的宽度打印。这里要注意的是,字段宽度中的负数表示打印内容要左对齐。可以用通配符*指定变化的区域宽度。在这种情况下,下一个参数用于指定宽度。第一个0用于从0开始的打印内容。根据POSIX的说明,printf函数不会截断一个要打印的字段,而是扩展它来填充它。因此
如下表所示:
格式参数输出
s " hellotherepeeps " hellotherepeeps
Printf将返回一个整数,表示写入的字符数。这不包括sprintf函数中的结束字符null。如果发生错误,将返回一个负数并设置errno。
scanf、fscanf、sscanf
scanf函数族的工作方式类似于printf组,只是这些函数从流中读取内容,或者将变量值放在作为参数传递的指针地址上。它们使用格式字符串以同样的方式控制输入转换,并且这些转义字符中有许多是相同的。
其语法格式如下:
#包含stdio.h
int scanf(const char *格式,);
int fscanf(FILE *stream,const char *format,);
int sscanf(const char *s,const char *format,);
这里重要的一点是,用于存储scanf函数读取的值的变量必须是正确的类型,并且与格式字符串完全匹配。否则,我们的内存将会泄漏,我们的程序可能会崩溃。不会出现编译错误,如果幸运的话,我们可能会得到一个警告消息。
scanf及其相关函数的格式字符串包含普通字符和转义字符,与printf类似。但是,普通字符用于指定必须出现在输入中的字符。
下面举个简单的例子:
int num
scanf("Hello %d ",num);
只有在标准输入中接下来的五个字符匹配Hello时,这个scanf调用才会成功。然后,如果下一个字符形成一个可识别的十进制数,这个数将被读入,其值将被赋给变量num。格式字符串中的空格用于忽略输入中转义字符之间的任何空格字符(空格、制表符或换行符)。这意味着,如果我们指定下面的任何输入形式,它将成功,1234将被存储。
你好1234
你好1234
通常,当转义开始时,空格字符也会被忽略。这意味着总是从输入中读入格式字符串%d,跳过任何空格和新行,直到找到一个数字。如果所需的字符没有出现,转义将失败,scanf函数将返回。
如果我们不小心,这将导致问题。如果我们在程序中读取整数,而输入中没有数字字符,就会导致无限循环。
的其他一些转义字符如下:
%d:读入十进制整数
%o,%x:读入一个八进制和十六进制整数。
%f,%e,%g:读入一个浮点数
%c:读入一个字符(不跳过空格)
%s:读入一个字符串
% []:读入一个字符集
%%:读入%字符
与printf类似,scanf转义字符也有一个宽度字段来限制输入的数量。大小表示接收的参数是短于还是长于默认条件(H代表shor,而L代表long)。这意味着%hd代表短整型,%ld代表长整型,% LG代表前面提到的双精度浮点数。
如果标识符以*开头,则意味着所有内容都将被忽略。这意味着输入的信息不会被保存,所以我们不需要变量来接收它。
我们使用%c从输入中读取单个字符,它不会跳过起始空格字符。
我们使用%s来读取一个字符串,但是我们必须小心。它将跳过开头的空格字符,但会在字符串中的第一个空格字符处停止。所以我们最好用它来读一个单词,而不是通常的字符串。同时,他可以阅读的字符串长度没有限制。因此,接收到的字符串必须足够大,以存储输入流中最长的字符串。我们最好使用区域宽度标识符或fgets和sscanf的组合来读入一行输入。这可以尽可能地防止恶意用户造成的缓冲区溢出。
我们可以使用% []标志来读取由一组字符组成的字符串。格式字符串%[A-Z]可以读取由大写字母组成的字符串。如果该集合中的第一个字符为,则将读取由不在该集合中的字符组成的字符串。因此,如果我们想读取一个包含空格但以第一个逗号结尾的字符串,我们可以格式化字符串% [,]。
我们可以输入下面的输入行:
你好,1234,5.678,X,串到行尾
这个scanf调用将正确读取四个内容:
char s[256];
int n;
浮动f;
char c;
scanf("Hello,%d,%g,%c,%[^/n "),n,f,c,s);
scanf函数将返回成功读取内容的数量,如果第一个内容失败,则返回零值。如果在匹配第一个内容之前已经到达输入的末尾,它将返回EOF。如果文件流中存在读取错误,将设置文件流错误标志,并设置错误变量errno来指示错误类型。
在正常情况下,不建议使用scanf和一些相关功能,原因如下:
传统的原因是这些功能的实现存在一些bug。
它们的使用不灵活。
它们将使理解分析的确切过程变得困难。
我们可以尝试使用一些其他的函数,比如fread或者fgets。
的一些其他流函数
还有许多其他stdio库函数使用流参数或标准的stdin、stdout、stderr和stderr参数:
获取文件流中的当前地址。
Fsetpos:设置文件流中的当前地址。
Ftell:返回流中当前文件的偏移量。
Rewind:重置流中的文件地址。
重复使用文件流
Setvbuf:设置流的缓冲方案。
Remove:等同于unlink,只是它的参数是一个目录。在这种情况下,它的功能与rmdir相同。
手册页的第三部分详细解释了这些库函数。
现在我们可以使用文件流函数来重新实现一个文件复制程序。这里我们将使用库函数。让我们来看看下面的示例程序copy_stdio.c .
这个程序和之前版本的程序差不多,但是现在这里的逐字符复制是通过stdio.h中的函数引用来实现的:
#包含stdio.h
#包含stdlib.h
int main()
{
int c;
文件*in,* out
in=fopen("file.in "," r ");
out=fopen("file.out "," w ");
while((c=fgetc(in))!=EOF)
fputc(c,out);
退出(0);
}
如果我们想像以前一样运行这个程序,我们可以得到以下输出信息:
$ TIMEFORMAT="" time copy_stdio
0.29用户0.02系统0:00.35消耗了87%的CPU
.
这次这个程序的运行时间是0.35秒,没有底块版快,但是比一次抄一个字好很多。这是因为stdio库使用文件结构来维护内部缓冲区,只有当这个缓冲区满了才会调用底层系统调用。我们可以用逐行复制和块复制来实现,这样就可以和这里发货的几个版本进行对比。
流错误
为了识别错误,许多stdio库函数会返回一个越界值,比如空指针或固定值EOF。在这些情况下,这些错误由外部变量errno:
#包含错误号h
外部int errno
这里要注意,很多函数都会改变errno的值。只有当函数调用失败时,它的值才可用。我们应该在函数识别失败后立即检查errno的值。我们应该在使用它之前将它的值复制到另一个变量中,因为有些打印函数,比如fprintf,可能会修改它的值。
我们还可以通过检测文件流的状态来确定是否发生了错误或者是否到达了文件的末尾。
#包含stdio.h
int ferror(FILE * stream);
int feof(FILE * stream);
void clearerr(FILE * stream);
Ferror函数将检测流的错误标识符,如果设置了错误标识符,将返回零值,否则将返回非零值。我们可以如下使用该函数:
if(feof(some_stream))
/*我们到终点了*/
Clearerr函数将清除流指针指向的文件流的文件结束或错误标识符。这个函数没有返回值,也没有定义错误。我们可以使用这个函数从流的错误状态中恢复。此功能的一个例子可能是当磁盘已满时将数据重写到文件流中。
和文件描述符。
每个文件流都对应于底层文件描述符。我们可以把底层的输入输出和高层的文件流操作混在一起,但是一般来说是不明智的,因为buffer的影响是不可预知的。
#包含stdio.h
int fileno(FILE * stream);
FILE *fdopen(int fildes,const char * mode);
通过调用fileno函数,我们可以知道文件流正在使用哪个底层文件描述符。他将返回指定文件流的文件描述符,如果失败,返回-1。如果我们需要底层访问一个开放流,我们可以使用这个函数,比如fstat。
我们可以通过调用fdopen函数,基于已经打开的文件描述符创建一个新的文件流。本质上,这个函数将为一个已经打开的文件描述符提供一个stdio缓冲区,这可能是解释它的一种更简单的方式。
fdopen函数的操作方式与fopen类似,只是它使用了一个低级文件描述符。如果我们需要用open来创建一个文件,也许是为了更好的权限控制,但又想用文件流来写,这个功能会特别有用。mode参数与fopen函数的参数相同,并且必须与文件第一次打开时建立的文件访问方法兼容。如果失败,fdopen将返回一个新的文件流。
文件和目录维护
标准库和系统调用提供了对文件创建和维护的完全控制。
改变文件权限
我们可以使用chmod系统调用来改变一个文件或目录的权限,这就构成了Shell编程的基本内容。
其语法如下:
#包含系统/统计信息
int chmod(const char *path,mode _ t mode);
由path指定的文件将具有由mode指定的权限。此处指定的模式与开放系统调用中的模式相同,是所需权限的一个位或。除非程序被指定了适当的权限,否则只有文件的所有者或超级用户可以更改其权限。
改变文件的所有者
超级用户可以使用chown系统调用来更改文件的所有者。
其语法如下:
#包括unistd.h
int chown(const char *path,uid_t owner,GID _ t group);
这个调用使用用户ID或组ID的值(可以由getuid和getgid调用)和一个常数来确定谁可以更改文件的所有者。如果设置了适当的权限,我们可以更改文件的用户和组。
取消链接,链接,符号链接
我们可以使用unlink来删除文件。
Unlink可以删除文件的目录实体,并减少其连接数。函数调用成功则返回0,失败则返回-1。我们必须拥有在目录中编写和执行命令的权限,因为文件对于这个函数调用有自己的目录实体。
其语法如下:
int unlink(const char * path);
int link(const char *path1,const char * path 2);
int symlink(const char *path1,const char * path 2);
如果连接数达到0并且没有进程打开该文件,则该文件将被删除。事实上,一个目录实体总是会被删除,但是文件的空间直到最后一个相关的进程关闭才会被回收。rm程序使用这个调用。正常情况下,我们可以用ln程序为一个文件创建一个链接。我们可以使用链接系统有计划地创建一个文件的链接。
link系统调用为现有文件path1创建新链接。新的目录实体由path2指定。我们可以使用symlink以类似的方式创建一个符号链接。这里要注意的是,文件的符号链接不会像硬链接一样阻止文件的删除。