下面的程序获取附属于stdin
的文件并将其复制到附属于stdout
的文件中。如果我使用fgets()
和fputs()
复制的文件损坏,但当我使用getc()
和putc()
时,一切都很好。为什么呢?
int main(int arg, char *argv[]) {
int c;
char buff[1024];
char *p;
while ((p = fgets(buff, sizeof(buff), stdin)) != NULL) {
fputs(buff, stdout);
}
return 0;
}
命令类型:
./buff < cat.jpeg > cp_cat.jpeg
之后文件cp_cat.jpeg
被损坏。
复制的JPEG文件损坏有两个原因:
-
文件在遗留系统上以文本模式打开,其中行结束序列从输入流(删除CR/LF对中的CR或可能所有CR字节)和输出流(将
'n'
转换为CR LF对)中进行翻译。在这样的系统上,stdin
和stdout
默认处于文本模式,您必须使用非标准api(如_set_fmode(_O_BINARY)
)来更改这一点。然而,当使用getc()
和putc()
时,这也会破坏JPEG文件,所以你可能使用macOS或其他unix系统。 -
fgets()
/fputs()
不能用于复制包含空字节的文件,因为fgets()
存储到缓冲区中的空字节被fputs()
解释为字符串的结尾,导致缓冲区的其余部分被忽略。这个问题没有解决的办法。为了缓冲复制过程,您可以使用fread()
和fwrite()
来读取和写入字节块,并且必须确保流以二进制模式运行。
下面是一个例子:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char buff[1024];
size_t nread;
#ifdef _MSC_VER
_set_fmode(_O_BINARY); // default to binary I/O
#endif
while ((nread = fread(buff, 1, sizeof buff, stdin)) != 0) {
if (fwrite(buff, 1, nread, stdout) != nread) {
fprintf(stderr, "error writing to stdout: %sn", strerror(errno));
return 1;
}
}
return 0;
}