binary和text方式打开文件的区别(转)
这个我原来也遇见过,并处理过以binary和text方式打开文件的区别,当时是05年实现发送http数据包,一直有问题,后来截取数据对比发现的,但是当时没有记录,今天发现这篇文章对这里进行了总结,故收集下来
发信人: LosComet (水母第3不厚道男), 信区: CProgramming
标 题: binary和text方式打开文件的区别
发信站: 水木社区 (Mon Apr 3 17:55:20 2006), 站内
这里并不是要说文本文件和二进制文件有什么区别,这两种文件之间的界限本来就很模糊
,事实上,把所有文件当成二进制文件就可以了。在这个层次上,一个文件和一块内存没
有什么区别,都是一个字节序列,一个字节就是一个介于0x00~0xFF之间的值
但是在Windows/DOS下,用fopen等函数打开文件的时候,最后一个参数里可以加上一个
"b"或者"t",用来告诉程序这个文件应该用什么方式打开。关于他们的区别,在MSDN上是
这样说的:
t
Open in text (translated) mode. In this mode, CTRL+Z is interpreted as an
end-of-file character on input. In files opened for reading/writing with "a+",
fopen checks for a CTRL+Z at the end of the file and removes it, if
possible. This is done because using fseek and ftell to move within a file
that ends with a CTRL+Z, may cause fseek to behave improperly near the end
of the file. Also, in text mode, carriage return–linefeed combinations
are translated into single linefeeds on input, and linefeed characters are
translated to carriage return–linefeed combinations on output. When a
Unicode stream-I/O function operates in text mode (the default), the
source or destination stream is assumed to be a sequence of multibyte
characters. Therefore, the Unicode stream-input functions convert
multibyte characters to wide characters. For the same reason, the Unicode
stream-output functions convert wide characters to multibyte characters.
b Open in binary (untranslated) mode; translations involving carriage-return
and linefeed characters are suppressed. If t or b is not given in mode, the
default translation mode is defined by the global variable _fmode. If t or b
is prefixed to the argument, the function fails and returns NULL.
为此,我做了如下的试验:
======================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fwrite_test(){
FILE * ft;
FILE * fb;
int i;
unsigned char b;
// 用两种模式打开两个文件写
ft = fopen("t.txt", "wt");
fb = fopen("b.txt", "wb");
// 将0x00-0xFF写到两个文件中
for(i = 0; i < 256; i++){
b = (unsigned char)i;
fwrite(&b, 1, 1, ft);
fwrite(&b, 1, 1, fb);
}
fclose(ft);
fclose(fb);
}
void fread_test(const char * filename){
FILE * ft;
FILE * fb;
// 用来存储从文件中读出来的数据
unsigned char buf_t[300] = {0};
unsigned char buf_b[300] = {0};
size_t cnt_t;
size_t cnt_b;
int i;
// 用两种方式打开同一个文件读
ft = fopen(filename, "rt");
fb = fopen(filename, "rb");
// 把数据分别读到buffer中,实际读取的字节数放到变量中
cnt_t = fread(buf_t, 1, 300, ft);
cnt_b = fread(buf_b, 1, 300, fb);
// 输出实际读取的字节数
printf("t mode read:%d\nb mode read:%d\n", cnt_t, cnt_b);
// 输出实际读取的内容
printf("\nbuffer t:\n");
for(i = 0; i < 300; i++){
printf("%X : 0x%X\n", i, buf_t[i]);
}
printf("\nbuffer b:\n");
for(i = 0; i < 300; i++){
printf("%X : 0x%X\n", i, buf_b[i]);
}
fclose(ft);
fclose(fb);
}
int main (int argc, char **argv)
{
// 用两种方式写两个文件
fwrite_test();
// 用两种方式读取binary方式写入的文件
fread_test("b.txt");
printf("\n-------------------------------------\n"); // 分割线
// 用两种方式读取text方式写入的文件
fread_test("t.txt");
};
======================================================================
编译、运行上述代码,生成test.exe,然后运行:
test.exe > output.txt
这样会生成3个文件,b.txt、t.txt、result.txt
首先察看文件大小,b.txt是256字节、t.txt是257字节
然后用16进制编辑器(我用的WinHex)打开两个文件以对比
对比之后发现,b.txt是从00-ff的256个字节
而t.txt中,在0x09和0x0A之间多了一个字节,就是0x0D
然后看result.txt的内容:
首先是,读取那个用b方式生成的文件
用t方式打开的,只读取了26个字节
而用b方式打开的,完整地读取了256个字节
看看buffer中的内容,buf_t中,只有前26个自节有值,后面都是0
也就是说,从第27个字节(0x1A)开始,后面的都没有读取
然后找到分割线,继续看下面的内容,读取t.txt的内容,也就是用t方式生成的多一个
0x0D的那个文件
这个文件共有257字节,而用b方式打开之后也是读取了257字节
但是看看t方式打开之后,仍然是读取了26字节
看看buf_t里面的内容,仍然是从0x00-0x19的这26个字节
======================================================================
总结:
0x0A是字符"\n"(换行,有时被称为line feed、lf)、0x0D是字符"\r"(回车,carriage
return)、0x1A是什么呢?我用VIM打开一个有0x1A的文件看到的是^Z
在t方式打开文件的时候,写入一个0x0A,会被扩展为0x0D、0x0A,这个在以前的讨论中
说过,也就是把\n转化成\r\n,在t方式读取的时候,0x0D、0x0A会被当作只有一个0x0A。
Ctrl-Z就是在Windows下的控制台下输入EOF是要按的键,也就是说,在读取文件的时候
用t方式打开的话,会把0x1A当作是一个文件的结尾,也就是EOF,后面的内容将不会视为
该文件的内容。
而用b方式打开文件的时候,会原汁原味地交换内存和文件中的数据,没有任何转换。
在Windows下,如果不加b或者t,或者在C++里面不加ios::binary或ios::text
默认是按照t方式打开的文件(这个应该所使用的库有关,反正VC是这样的)
这一点要注意。
另外,我把刚才的程序拿到Linux下编译运行了一次,结果是b和t没有区别,表现出来的
行为和Windows下用b方式打开文件一样,0x0A、0x1A等数据进行特别处理。
贴上Linux下的部分man page的内容:
man fopen:
The mode string can also include the letter ``b'' either as a last
character or as a character between the characters in any of the two-
character strings described above. This is strictly for compatibility
with ANSI X3.159-1989 (``ANSI C'') and has no effect; the ``b'' is
ignored on all POSIX conforming systems, including Linux. (Other sys-
tems may treat text files and binary files differently, and adding the
``b'' may be a good idea if you do I/O to a binary file and expect that
your program may be ported to non-Unix environments.)
也就是说,Linux下b和t是没有区别的
windows/dos真麻烦^_^ ——某斑竹语
发信人: LosComet (水母第3不厚道男), 信区: CProgramming
标 题: binary和text方式打开文件的区别
发信站: 水木社区 (Mon Apr 3 17:55:20 2006), 站内
这里并不是要说文本文件和二进制文件有什么区别,这两种文件之间的界限本来就很模糊
,事实上,把所有文件当成二进制文件就可以了。在这个层次上,一个文件和一块内存没
有什么区别,都是一个字节序列,一个字节就是一个介于0x00~0xFF之间的值
但是在Windows/DOS下,用fopen等函数打开文件的时候,最后一个参数里可以加上一个
"b"或者"t",用来告诉程序这个文件应该用什么方式打开。关于他们的区别,在MSDN上是
这样说的:
t
Open in text (translated) mode. In this mode, CTRL+Z is interpreted as an
end-of-file character on input. In files opened for reading/writing with "a+",
fopen checks for a CTRL+Z at the end of the file and removes it, if
possible. This is done because using fseek and ftell to move within a file
that ends with a CTRL+Z, may cause fseek to behave improperly near the end
of the file. Also, in text mode, carriage return–linefeed combinations
are translated into single linefeeds on input, and linefeed characters are
translated to carriage return–linefeed combinations on output. When a
Unicode stream-I/O function operates in text mode (the default), the
source or destination stream is assumed to be a sequence of multibyte
characters. Therefore, the Unicode stream-input functions convert
multibyte characters to wide characters. For the same reason, the Unicode
stream-output functions convert wide characters to multibyte characters.
b Open in binary (untranslated) mode; translations involving carriage-return
and linefeed characters are suppressed. If t or b is not given in mode, the
default translation mode is defined by the global variable _fmode. If t or b
is prefixed to the argument, the function fails and returns NULL.
为此,我做了如下的试验:
======================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fwrite_test(){
FILE * ft;
FILE * fb;
int i;
unsigned char b;
// 用两种模式打开两个文件写
ft = fopen("t.txt", "wt");
fb = fopen("b.txt", "wb");
// 将0x00-0xFF写到两个文件中
for(i = 0; i < 256; i++){
b = (unsigned char)i;
fwrite(&b, 1, 1, ft);
fwrite(&b, 1, 1, fb);
}
fclose(ft);
fclose(fb);
}
void fread_test(const char * filename){
FILE * ft;
FILE * fb;
// 用来存储从文件中读出来的数据
unsigned char buf_t[300] = {0};
unsigned char buf_b[300] = {0};
size_t cnt_t;
size_t cnt_b;
int i;
// 用两种方式打开同一个文件读
ft = fopen(filename, "rt");
fb = fopen(filename, "rb");
// 把数据分别读到buffer中,实际读取的字节数放到变量中
cnt_t = fread(buf_t, 1, 300, ft);
cnt_b = fread(buf_b, 1, 300, fb);
// 输出实际读取的字节数
printf("t mode read:%d\nb mode read:%d\n", cnt_t, cnt_b);
// 输出实际读取的内容
printf("\nbuffer t:\n");
for(i = 0; i < 300; i++){
printf("%X : 0x%X\n", i, buf_t[i]);
}
printf("\nbuffer b:\n");
for(i = 0; i < 300; i++){
printf("%X : 0x%X\n", i, buf_b[i]);
}
fclose(ft);
fclose(fb);
}
int main (int argc, char **argv)
{
// 用两种方式写两个文件
fwrite_test();
// 用两种方式读取binary方式写入的文件
fread_test("b.txt");
printf("\n-------------------------------------\n"); // 分割线
// 用两种方式读取text方式写入的文件
fread_test("t.txt");
};
======================================================================
编译、运行上述代码,生成test.exe,然后运行:
test.exe > output.txt
这样会生成3个文件,b.txt、t.txt、result.txt
首先察看文件大小,b.txt是256字节、t.txt是257字节
然后用16进制编辑器(我用的WinHex)打开两个文件以对比
对比之后发现,b.txt是从00-ff的256个字节
而t.txt中,在0x09和0x0A之间多了一个字节,就是0x0D
然后看result.txt的内容:
首先是,读取那个用b方式生成的文件
用t方式打开的,只读取了26个字节
而用b方式打开的,完整地读取了256个字节
看看buffer中的内容,buf_t中,只有前26个自节有值,后面都是0
也就是说,从第27个字节(0x1A)开始,后面的都没有读取
然后找到分割线,继续看下面的内容,读取t.txt的内容,也就是用t方式生成的多一个
0x0D的那个文件
这个文件共有257字节,而用b方式打开之后也是读取了257字节
但是看看t方式打开之后,仍然是读取了26字节
看看buf_t里面的内容,仍然是从0x00-0x19的这26个字节
======================================================================
总结:
0x0A是字符"\n"(换行,有时被称为line feed、lf)、0x0D是字符"\r"(回车,carriage
return)、0x1A是什么呢?我用VIM打开一个有0x1A的文件看到的是^Z
在t方式打开文件的时候,写入一个0x0A,会被扩展为0x0D、0x0A,这个在以前的讨论中
说过,也就是把\n转化成\r\n,在t方式读取的时候,0x0D、0x0A会被当作只有一个0x0A。
Ctrl-Z就是在Windows下的控制台下输入EOF是要按的键,也就是说,在读取文件的时候
用t方式打开的话,会把0x1A当作是一个文件的结尾,也就是EOF,后面的内容将不会视为
该文件的内容。
而用b方式打开文件的时候,会原汁原味地交换内存和文件中的数据,没有任何转换。
在Windows下,如果不加b或者t,或者在C++里面不加ios::binary或ios::text
默认是按照t方式打开的文件(这个应该所使用的库有关,反正VC是这样的)
这一点要注意。
另外,我把刚才的程序拿到Linux下编译运行了一次,结果是b和t没有区别,表现出来的
行为和Windows下用b方式打开文件一样,0x0A、0x1A等数据进行特别处理。
贴上Linux下的部分man page的内容:
man fopen:
The mode string can also include the letter ``b'' either as a last
character or as a character between the characters in any of the two-
character strings described above. This is strictly for compatibility
with ANSI X3.159-1989 (``ANSI C'') and has no effect; the ``b'' is
ignored on all POSIX conforming systems, including Linux. (Other sys-
tems may treat text files and binary files differently, and adding the
``b'' may be a good idea if you do I/O to a binary file and expect that
your program may be ported to non-Unix environments.)
也就是说,Linux下b和t是没有区别的
windows/dos真麻烦^_^ ——某斑竹语