binary和text方式打开文件的区别

binary和text方式打开文件的区别
 
 
发信人: 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真麻烦^_^  ——某斑竹语
 
<think>好的,用户现在想知道如何用C++的ofstream以二进制模式打开文件。之前他们问了关于fputws的换行符转换问题,现在转向了C++文件操作,可能是在处理文件输出时遇到了类似的问题,或者在项目中需要处理二进制数据。 首先,我需要确认用户对ofstream的了解程度。他们可能已经知道基本的文件操作,但需要更深入的二进制模式使用。要解释清楚二进制模式与文本模式的区别,特别是换行符的处理,但这次在C++的上下文中。 记得用户之前的问题是关于C的fputws,现在转向C++的ofstream,可能需要说明C++中流类的不同之处。需要强调在C++中使用open方法或构造函数时指定模式参数,比如ios::binary。 另外,用户可能关心二进制模式的实际应用场景,比如处理图片、音频或自定义数据结构。需要给出具体的例子,展示如何写入二进制数据,比如结构体或原始字节,并对比文本模式与二进制模式的结果差异。 还要注意潜在的问题,比如不同平台上的换行符转换,内存对齐的问题,以及字符编码的问题,尤其是在处理宽字符时。比如,在二进制模式下,直接写入内存内容,可能需要注意大小端填充字节。 最后,总结使用二进制模式的步骤注意事项,确保用户能够正确应用,并避免常见错误。可能需要提醒用户在读写时要保持模式一致,否则会导致数据损坏。</think>以下是关于如何使用 C++ `ofstream` 以二进制方式打开文件的详细说明: --- ### 一、核心语法 在 C++ 中,使用 `ofstream` 打开二进制文件需 **显式指定 `std::ios::binary` 模式**: ```cpp #include <fstream> std::ofstream file("filename", std::ios::binary); // 构造函数方式 // 或 file.open("filename", std::ios::binary); // open() 方法方式 ``` --- ### 二、关键特性 #### 1. **二进制模式与文本模式的区别** | 模式 | 行为 | |--------------|----------------------------------------------------------------------| | **二进制模式** | 直接写入原始字节,无任何转换(如 `\n` 不会转为 `\r\n`) | | **文本模式** | 可能进行平台相关转换(如 Windows 下 `\n` → `\r\n`,Linux/macOS 无转换) | #### 2. **典型使用场景** - 写入非文本数据(如图片、音频、视频等二进制文件) - 直接保存内存中的数据结构(如结构体、二进制协议数据) - 需要精确控制文件字节内容的场景 --- ### 三、具体示例 #### 示例 1:写入二进制数据(基本类型) ```cpp #include <fstream> #include <iostream> int main() { std::ofstream file("data.bin", std::ios::binary); if (!file) { std::cerr << "打开文件失败!" << std::endl; return 1; } int num = 0x12345678; // 十六进制数据 file.write(reinterpret_cast<const char*>(&num), sizeof(num)); return 0; } ``` **文件内容(十六进制)**:`78 56 34 12`(小端系统,如 x86) --- #### 示例 2:写入结构体 ```cpp #include <fstream> struct Person { char name[20]; int age; double height; }; int main() { Person p = {"张三", 30, 1.75}; std::ofstream file("person.dat", std::ios::binary); file.write(reinterpret_cast<const char*>(&p), sizeof(Person)); return 0; } ``` **文件内容**:直接存储结构体的内存映像(包含可能的对齐填充字节) --- ### 四、注意事项 1. **数据类型转换** 使用 `reinterpret_cast<const char*>` 将任意类型指针转换为 `char*` 指针,这是二进制读写的必要操作。 2. **跨平台问题** - 字节序(大端/小端)会影响多字节数据的存储顺序 - 结构体对齐方式可能因编译器设置不同而异 3. **文件打开验证** 始终检查文件是否成功打开: ```cpp if (!file.is_open()) { /* 错误处理 */ } ``` 4. **混合模式使用** 可组合其他模式标志(如追加模式): ```cpp std::ofstream file("data.bin", std::ios::binary | std::ios::app); ``` --- ### 五、对比测试 #### 测试代码 ```cpp #include <fstream> int main() { // 文本模式写入(对比用) std::ofstream text_file("text.txt"); text_file << "Line1\nLine2\n"; // 二进制模式写入 std::ofstream bin_file("binary.bin", std::ios::binary); bin_file << "Line1\nLine2\n"; return 0; } ``` #### 结果分析(Windows 环境) | 文件 | 实际内容(十六进制) | |---------------|-----------------------------------------------| | `text.txt` | `4C 69 6E 65 31 0D 0A 4C 69 6E 65 32 0D 0A` | | `binary.bin` | `4C 69 6E 65 31 0A 4C 69 6E 65 32 0A` | - 文本模式下 `\n` 被转换为 `\r\n`(`0D 0A`) - 二进制模式下保持原始 `\n`(`0A`) --- ### 六、总结 | 操作要点 | 说明 | |------------------------------|----------------------------------------------------------------------| | **必须指定 `std::ios::binary`** | 避免换行符等自动转换 | | **使用 `write()` 方法** | 而不是 `<<` 运算符(后者可能因重载导致意外格式化) | | **注意内存对齐** | 结构体中的内存布局可能与磁盘存储不一致 | | **跨平台需谨慎** | 字节序、对齐方式、基本类型大小(如 `long` 在不同系统的长度)可能不同 | 如果需要进一步了解二进制文件读取或其他细节,请随时追问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值