精华内容
下载资源
问答
  • 二进制文件和十六进制文件读取什么区别,binary是二进制模式吧,那么十六进制模式用什么模式呢?
  • 二进制文件格式设计

    千次阅读 2017-08-09 22:31:06
    比如一个矢量绘图程序,需要将用户绘制的每个图元都保存到文件中,以后再次打开。应该优先考虑文本格式,文本格式容易测试和编辑。更应该优先考虑通用的文本格式,比如 XML, JSON, Lua 等等。这些通用的文本格式已经...

    文章转自:https://zhuanlan.zhihu.com/p/20693043

    程序时常需要保存自身的文档数据。比如一个矢量绘图程序,需要将用户绘制的每个图元都保存到文件中,以后再次打开。应该优先考虑文本格式,文本格式容易测试和编辑。更应该优先考虑通用的文本格式,比如 XML, JSON, Lua 等等。这些通用的文本格式已经存在大量的工具和库,可以省下很多功夫。

    文本格式读取慢,并且文件尺寸也比较大(就算经过 zip 压缩),大多数情况下这都不是什么问题。但一些场合,要求更快读取速度,更小文件尺寸,这时就需要自己来设计一种二进制文件格式。游戏中的模型数据,就要求读取速度快;而经常通过网络传输的文件,就要求减少文件尺寸,比如 swf 格式。

    具体的二进制文件格式,要根据具体的程序需求来设计。但有些设计思路,是所有二进制格式都通用的。了解这些,对将来分析其它的二进制格式也会有帮助。

    整体文件结构

    常见二进制文件格式,时常采用 文件头 + 分区 的结构:

    file header 
    section 0
    section 1
    section 2
    section 3
    ....
    section N
    

    文件头描述了文件的整体信息,常见的字段有魔数、版本号、检验码、文件大小等等。文件头根据文件的具体用途会有额外的字段,比如一张图片,文件头当中就可以含有表示图片尺寸的字段。

    文件的整体信息通常放在文件的最前面,所以才叫文件头。但少数情况下可以放到文件的最后面,变成文件尾,但基本上不会放在文件中间。什么情况下会放到文件最后面呢?可以参考后面「回写和流写」那个小节。

    分区的结构通常会是:

    tag + length
    section data
    

    tag 和 length 合起来是分区头部,后面紧跟着分区的具体数据。

    tag 可以是一个整数,也可以是一个字符串。tag 用来标识分区,不同的 tag 表示不同的分区种类,不同的分区种类有各自不同的读取方式。比如:

    #define kPicShapeTag   1
    #define kPreivewTag    2
    

    当 tag 为 1 时,就表示是这个分区存放的是图元数据,当为 2 是表示这个分区存放一张预览图。

    length 是个整数,表示分区数据的具体长度(不包括分区头部)或者表示整个分区的长度(包括分区头部)。

    这种分区结构使得文件格式容易扩展,有新需求时就定义一个新的分区类型,原来的文件结构不需要修改。也容易「向上兼容」。

    这里需要解释什么叫文件的「向上兼容」。「向上兼容」跟「向下兼容」对应。「向下兼容」指旧版本程序产生的旧版本文件格式,可以使用新版本的程序打开。比如程序 1.0 生成文件格式 1.0,新版本程序 1.2 可以打开文件格式 1.0。「向上兼容」指新版本程序生成的新版本格式,可以使用旧版本程序打开。比如新版本程序 1.2 最新定义了文件格式 1.1,旧版本程序 1.0 虽然早就发布了,但还是可以打开新版本文件格式 1.1,虽然可能会缺少一些新版本的功能。

    一个应用程序升级,「向下兼容」是最起码的要求,但「向上兼容」就并非所有程序都可以做到的。使用分区的结构,旧版本程序读取二进制格式时,一旦遇到不认识的分区类型可以直接忽略掉,这样就更容易实现「向上兼容」。比如 Flash 的 swf 文件格式,新版本格式新添了滤镜功能,滤镜数据放到一个新定义的分区当中。这样旧版本的 Flash 播放器不能识别滤镜分区,但还是可以播放新版本的 swf 格式,只是不能显示滤镜。而新版本的 Flash 播放器可以正确处理滤镜。

    注意程序的版本号跟文件格式的版本号可以是不对应的。比如程序版本是 1.2,但文件格式可以还是停留在 1.1。

    有时候分区数据本身再细分,再次采用这种分区结构。比如一个矢量绘图程序,需要记录下图元。可以设计成:

    绘图 Header
    绘图分区
    预览图分区
    

    绘图分区再划分成具体的图元分区,仍然采用 tag + length 的结构。

    #define kShapeCircle    1
    #define kShapeRect      2
    #define kShapeBezier    3
    

    这样就可以不断地增加各种图元。

    文件头魔数(magic number)

    文件头当中,会有一个数字作为文件格式的标识。这个数字可以随意选定任何值,也可以占据任何字节(通常是 4 字节或者 8 字节),但这个数字选定之后就会固定下来,基本上不会再有变化。在编程领域,一些说不清来历比较任意的数字会被称呼为魔数( magic number)。因此这个随意选定用于标识文件格式的数字,就叫文件格式魔数;这个数字通常放在文件头当中,有时也就称为文件头魔数。

    文件格式魔数可以随意选取,看设计者自身的喜好。有些设计者会取自己名字的缩写,有些会取自己的生日,有些会取当前日期,有些取文件格式的后缀名,有些仅仅是抛色子得到的随机数字。

    比如 zip 格式的魔数在字节序是小端机器上是 0x04034b50,这个整数表示成 char[4] 就是 "PK\x03\x04",其中 PK 就是设计者 Philip Katz 的名字首字母。

    为了方便处理,避免数字在不同字节顺序的机器上有所区别,有时文件头魔数会定义成多字节格式,比如:

    struct Header
    {
        uint8_t md5[16]; // md5 作为 检验码,
        char magic[8];   // 魔数
    }
    
    Header header;
    memcpy(header.magic, "vecpaint", 8);
    

    文件头魔数无论被当成整数还是多个字节处理,它的作用都是相同的,只是作为一个文件格式的标识。

    比如一个矢量绘图软件定义的文件格式魔数为 "vecpaint",位于文件开始的第 16 个字节处(最前面 16 个字节放 md5)。这个软件保存数据输出文件 example.vecpaint,重新读取这个文件时,就首先判断文件对应位置,对应的魔数是否为 "vecpaint",假如是的话,就进一步读取解析数据;假如并非这个魔数,就读取失败。

    你可能会说,这个 example.vecpaint 文件既然是软件产生的,从后缀名就可以关联到对应的程序,当然一定会读取正确,为什么还需要先判断魔数呢?那我举个反面例子,我有一张 example.png 图片,之后我将它的后缀名改成 vecpaint,再用这个矢量绘图软件打开。假如不判断魔数当成正常文件处理,就很有可能出问题,甚至会引起程序崩溃。

    另外一个软件可以处理多种文件格式,比如 Photoshop 可以处理 png 图片格式,也可以处理 jpg 图片格式。这样就通过魔数判断出各种文件格式,再进入对应的读取流程。

    常见的文件格式 png, jpg,exe,swf,psd,pdf,gif,zip 等等,魔数的位置和值都有所不同,这样就可以区分出各类不同的二进制格式。而单纯采用后缀名来判断是不准确的。

    需要注意的是,假如魔数正确,文件格式并非一定能够读取正确,还需要进一步判断。但假如魔数错误的话,这个文件就一定会读取失败。

    检验码

    文件头通常还会有个检验码,用于检验文件是否完整并且没有经过修改的。这个检验码可以使用 crc, 可以使用 md5,也可以使用其它算法。只要达到这个目的就行。

    假如文件都是在本机写入和读取,这个检验码没有什么大作用。但假如文件格式经过网络传输,这个检验码就十分有用了。网络传输经常会发生数据不全,或者某些字节被改变了,导致文件数据不完整。通过这个检验码可以检测出这种问题,以便再做进一步处理(比如重新下载一次)。

    你可能会说,现在的网络这样可靠,为什么还需要检验啊?举个我遇到的例子,4 年前我设计过一个涂鸦文件格式,最初的文件格式是不带检验码的。这样当用户将涂鸦文件上传到服务器时,上传到一半,用户断网了。这时服务器上的涂鸦文件就只有一半。以后下载读取时,因为文件不完整就一直读取失败,有时甚至会引起客户端崩溃。不要问我为什么文件会上传了一半,服务器不是我写的,具体原因我也不知道。设计这个涂鸦格式的第二版时,我学乖了,放一个 md5 作为检验码,就算服务器出错,客户端也可以一开始就识别出来进行异常处理。假如没有这个检验字段,客户端是没有办法知道数据是否完整的。

    我自己设计的二进制文件格式时,经常会在最开头放 16 个字节的 md5。

    char md5[16];
    // 文件的剩余数据
    

    用文件的剩余数据计算出它的 md5, 存放在文件最开头。这个 md5 一方面可以作为这个文件的检验码,另一方面可以作为这个文件的 key。读取文件格式的时候,先判断魔数是否正确,再重新计算出 md5 进行比较。md5 出错,表示文件不完整或者经过改动。在需要更安全的场合,md5 可能被人伪造,但平常应用基本足够了。

    版本号

    文件头通常还会包含版本号。版本号不同的文件格式,读取方式可能会有所不同。不支持「向上兼容」的软件,碰到比它可以支持的更高版本的文件格式,就直接读取失败,并返回一个错误信息。

    版本号有时只是单独一个数字,不断往上递增。有时也会拆分成两个数字,为主版本号和次版本号。主版本号修改,通常表示文件格式发生大变动。而次版本号修改,通常只是表示添加了一些小功能。

    补充一些闲话,软件的版本号制定方式是多种多样的。有些软件会直接采用发布年份作为版本号,比如 Windows 98,Office 2013。大部分软件的版本号采用三个数字,用小数点分隔,格式为:

    主版本号.次版本号.补丁版本号
    

    主版本号通常表示功能有很大改动,甚至界面都改掉了;次版本号用于表示添加了一些小功能;补丁版本号只是用了 fix bugs。iOS 系统的采用这种版本表示方式。

    当判断版本高低的时候,需要将三个数字分隔开依次判断,这个应该是常识了。但偏偏就有人缺乏这种常识,我就修正过一个 bug,有人将版本号直接转成浮点数,之后判断浮点数大小来判断版本高低,比如:

    • 版本号 2.10 从字符串转成浮点就是 2.10,也就是 2.1。
    • 版本号 2.9 从字符串转成浮点数就是 2.9。

    因为 2.1 比 2.9 要小,就判断得出 2.10 的的版本要比 2.9 要低。

    字节顺序

    字节顺序有大端字节序和小端字节序。不同的机器字节序有可能不同,设计文件格式时需要考虑文件用什么字节序保存数据的。不然有可能在这一台机器上生成的文件,传输到另一台机器上就打开失败了。

    有些人不注意字节顺序,用类似下面的代码去读写数据:

    static void writeI32(std::vector<uint8_t>& data, int32_t val)
    {
        uint8_t* ptr = (uint8_t*)&val;
        data.insert(data.end(), ptr, ptr + 4);
    }
    
    static uint32_t readI32(uint8_t*& ptr)
    {
        int32_t val = *((int32_t*)ptr);
        ptr += 4;
        return val;
    }
    

    这样的代码初看起来没有什么错误,但这些代码时依赖机器字节序的。同样的代码,保存一个 4 字节整数 0x01020304,在大端机器,会保存为:

    0x01 0x02 0x03 0x04
    

    但在小端机就会被保存成:

    0x04 0x03 0x02 0x01
    

    这样就引起读写不一致。设计自己的文件格式或者去分析其它的文件格式,一定要注意字节序。有些文件格式,可以同时支持大端和小端字节序。它有文件头中有个字段指明文件保存的时候是采用什么方式保存。那为什么文件格式需要同时支持两种字节方式呢?那不是自己来找麻烦吗?

    以前大端小端字节序的机器都比较常见,同时支持两种字节序,就可以根据机器情况选择对应的保存方式。当软件在大端机器上运行,就选择保存成大端字节序,小端机器上就保存成小端字节序。这样文件在读取的时候,不用交换字节,读取速度会快一些。

    但现在绝大多数情况下,我们用到的机器都是小端字节序的。文件格式倾向于小端存储。这样读写的时候,会更加方便些。

    字节或者位

    现在的计算机会将 8 bit 划分成一个字节,平时计算和处理数据都用字节作为最小的单位,有些语言甚至不支持位运算。文件格式也倾向于采用字节的存储方式。一个整数存储成 1 字节,2 字节,4 字节或者 8 字节。

    但情况总有例外,有些场合需要更高效地减少文件大小。保存数字时用字节为单位存储也觉得浪费,这是可以将数字按位存储,读入或者写入的时候,都使用位运算。比如保存一串数字:

    10, 11, 12, 14, 15, 16, 18, 19, 14, 30
    

    这串数字都不会大于 32,可以 5 位表示。先保存这个数字 5,表示之后的数字以多少位保存。再依次用位操作,将每个数字用 5 位保存。

    比如典型的是 swf 文件格式的设计。它大量使用了位存储,使得 swf 文件的格式压缩得很小。但这样写入和读取都会有点麻烦,更加耗费计算资源。

    需要看你的设计目的,假如需要大量压缩尺寸,可以采用位存储,但读取速度就慢。采用字节保存,读取速度会快,但文件尺寸通常会比按位保存要大些。这个其实就是空间和时间的取舍,空间换时间或者是时间换空间。

    swf 的一个目标设计是方便经过网络传输,当时的网络条件比较差,它这样使用位存储是合理的。

    字节对齐

    现在很多程序员都不知道字节对齐是什么了。

    假设机器是 32 位(也就是 4 字节),当数据的地址为 4 的倍数时,计算机的读取速度会更快。你可以将计算机的地址,以 4 字节为单位,从 0 开始,将地址划分成一个个小格子,每个格式都可以放 4 字节,格子开始地址都是 4 的倍数。计算机读取一个格子的东西是最快的。假如一个整数是 4 字节,它处的地址是 4 字节对齐的话,就刚好占据一个格子。但假如这个整数并非 4 字节对齐,就跨越了格子边界,占据了两个格子,计算机就需要读取两个格子的信息,再将这个整数的值拼出来,这样读取就会慢了。假如机器是 64 位(也就是 8 字节),这是它就用 8 字节来划分成格子,需要 8 字节对齐。

    C/C++ 编译器编译代码时,也会尽量使得数据字节对齐,比如下面结构:

    struct Test
    {
        int a;
        double b;
        int c;
        double d;
    };
    

    编译器为使得数据不跨越格子边界,整个结构在 64 位机上占据 32 个字节。但稍微调整一下:

    struct Test
    {
        int a;
        int c;
        double b;
        double d;
    };
    

    就这样交换一些数据定义顺序,整个结构在 64 位机上占据 24 个字节,比原来节省了 8 个字节。

    文件格式的数据最终需要载入到内存中读取,为加快读取速度,设计的时候应该考虑字节对齐。比如当采用 4 对齐时,当写入字符串只有 31 个字符,可以在最后面再写入一个字节 0,这样使得下一个数字是 4 字节对齐。

    分析已有的文件格式时,也需要注意字节对齐。很多旧文件格式,就经常会在字符串后面填充一些 0 字节的。有些文件格式,甚至可以调整字节对齐方式。在文件头中有个对齐字段,指定读取写入时对齐字节数。可以是是 1 字节对齐(1 字节对齐其实就是不对齐),有时是 2 字节对齐,有时是 4 字节对齐。

    回写和流写

    「回写」是指数据写入之后,可以回头再修改。比如分区数据最通常以 tag + length 开头,但最开始是不知道最终的分区数据长度的。这样当写入分区头的 length 字段时,就只能先随意写些临时值。当写完最后的分区数据,知道数据长度了,再回头修改长度。

    但有些情况下,数据写入之后就不能回头修改了。比如将数据写入网络当中,不可能回头修改网络上的数据。这种一直写,不能回头修改的就叫「流写」。「流写」是我自己取的名字,我其实不清楚这叫什么。计算机中,经常有流这个概念,英文为 stream, 也就是数据像流水一样,只能向前,不能回头。

    有些约束条件下,二进制文件格式就需要支持「流写」,比如在网络一端生成数据,在网络另一边读取数据。这种情况下,可以将 文件头 + 分区数据 稍微调整一下变成分区数据 + 文件尾。当按顺序写完所有分区数据,也就知道文件的整体信息,就可以依次写入文件尾的各字段。

    另外分区格式,就不能采用 tag + length 的方式了,长度不太可能预先知道。这种情况下,,可以在分区开始时先写入一个特殊的开始符号,分区结束之后再重复这个特殊符号。读取的时候,遇到这个特殊符号,就表示分区要开始了,再次遇到这个符号,就表示分区结束了。

    比如 http 的 multipart/form-data 的 post 方式,就使用特殊的符号标记数据的开始和结束。

    ------WebKitFormBoundaryZL8FggWNCK9cO6Bi
    Content-Disposition: form-data; name="name"
    
    Adam
    ------WebKitFormBoundaryZL8FggWNCK9cO6Bi
    Content-Disposition: form-data; name="password"
    
    HelloWorld
    ------WebKitFormBoundaryZL8FggWNCK9cO6Bi
    

    代码的坑

    上面已经讨论了二进制设计中的常见问题,这些讨论只针对普遍情况,更详细具体的二进制格式需要根据用途来设计。二进制读写的代码通常会使用 C 或 C++ 来编写,最后稍微提两个常见的坑。

    坑 1

    读写二进制时,应该先将读写字节的代码封装起来,在 C++ 中可以封装成一个类。有些人使用模板来设计读写接口:

    class ByteWriter
    {
    public:
        template <typename T>
        void write(T val);
    };
    
    class ByteReader
    {
    public:
        template <typename T>
        T read();
    };
    

    之后就使用:

    writer.write(header.majorVersion);
    writer.write(header.minorVersion);
    

    这种模板接口看似很统一,其实是不好的。二进制格式的读写需要严格控制字节数目,应该从接口当中就看出读写了多少个字节,不然很容易出问题。比如将 majorVersion 的类型从 uint16_t 修改成 uint8_t,就会出问题了。这种读写接口,宁愿笨一点,麻烦一点。写成:

    class ByteWriter
    {
    public:
        void writeUI16(uint16_t val);
        void writeUI32(uint32_t val);
        void writeBytes(void* bytes, size_t val);
    };
    
    class ByteReader
    {
    public:
        uint16_t readUInt16();
        uint32_t readUInt32();
        void readBytes(void* dest, size_t len);
    };
    
    writer.writeUI16(header.majorVersion);
    writer.writeUI16(header.minorVersion);
    

    ByteWriter 和 ByteReader 内部实现应该考虑到字节顺序。

    坑 2

    读写二进制数据,千万不要为了方便而将一个结构整体写入,比如:

    struct Point
    {
        int16_t x;
        int16_t y;
    };
    
    Point pt;
    xxxxx
    writer.writeBytes(&pt, sizeof(pt));
    

    就算不考虑字节顺序,这种代码也是很不好的。一方面 Point 中修改了字节的顺序,或者添加了字节,甚至源码完全不变仅仅是换了机器编译(比如从 32 位机器换到 64 位机器),这样的代码都有可能会出问题。二进制的读写应该拆分成一个个字段分别读写。比如:

    inline void writePoint(ByteWriter& writer, const Point& pt)
    {
        writer.writeI16(pt.x);
        writer.writeI16(pt.y);
    }
    展开全文
  • 文本文件和二进制文件的判别

    千次阅读 2019-06-26 15:17:03
    作为程序员,从第一次接触编程开始,就知道了文件的两大类别:文本文件和二进制文件。但是,如何定义和判别这两类文件,在我的知识体系了,却一直是模糊的。直觉上,用文本方式读写的文件一定是文本文件,用二进制...

    作为程序员,从第一次接触编程开始,就知道了文件的两大类别:文本文件和二进制文件。但是,如何定义和判别这两类文件,在我的知识体系里,却一直是模糊的。直觉上,用文本方式读写的文件一定是文本文件,用二进制方式读写的文件一定是二进制文件,然而,用 notepad++ 照样可以打开甚至编辑一个 .jpg 文件或者一个 .xls 文件。

    事实上,不管是文本文件还是二进制文件,在物理存储上都是二进制的。二者之间的区别不是物理层面的,而是逻辑层面的。文本文件是基于字符编码的文件,常见的编码有 ASCII 编码,UTF 编码等等。二进制文件是基于值编码的文件,你可以根据具体应用,指定某个值是什么意思(可以看作是自定义编码)。

    在很多文档处理的应用中,我们需要判别某个文件是文本文件还是二进制文件。如果单从文件的扩展名来判别,自然是最简单的,但结果未必是正确的。我们重点讨论通过文件内容来判别。

    适用于 py2 的方法:

    import string
    
    def isText(content):
    	"""判断文件是文本还是二进制"""
    	
    	if "\0" in content:
    	    return False
    	
    	text_characters = ''.join(map(chr, range(32, 127)) + list("\n\r\t\b"))
    	_null_trans = string.maketrans("", "")
    	
    	t = content.translate(_null_trans, text_characters)
    	if float(len(t))/float(len(content)) > 0.30:
    	    return False
    	else:
    	    return True
    

    适用于 py3 的方法:

    def isText(content):
        """判断文件是文本还是二进制"""
        
        if b'\0' in content:
            return False
        
        text_characters = ''.join(list(map(chr, range(32, 127))) + list('\n\r\t\b'))
        _null_trans = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
        
        t = content.translate(_null_trans, text_characters.encode())
        if float(len(t))/float(len(content)) > 0.30:
            return False
        else:
            return True
    
    展开全文
  • 二进制文件和非二进制文件

    千次阅读 2012-09-17 14:34:53
    二进制文件和非二进制文件   1. 流式文件:文件中的数据是一串字符,没有结构。 2. 文本文件是一种典型的顺序文件,其文件的逻辑结构又属于流式文件。特别的是,文本文件是指以ASCII码方式(也称文本方式)存储...

    二进制文件和非二进制文件

     

    1. 流式文件:文件中的数据是一串字符,没有结构。

    2. 文本文件是一种典型的顺序文件,其文件的逻辑结构又属于流式文件。特别的是,文本文件是指以ASCII码方式(也称文本方式)存储的文件,更确切地说,英文、数字等字符存储的是ASCII码,而汉字存储的是机内码。文本文件中除了存储文件有效字符信息(包括能用ASCII码字符表示的回车、换行等信息)外,不能存储其他任何信息,因此文本文件不能存储声音、动画、图像、视频等信息。

    设某个文件的内容是下面一行文字: 中华人民共和国 CHINA 1949如果以文本方式存储,机器中存储的是下面的代码(以十六进制表示,机器内部仍以二进制方式存储) D6 D0 BB AA C8 CB C3 F1 B9 B2 BA CD B9 FA 20 43 48 49 4E 41 20 31 39 34 39 A1 A3 其中,D6D0BBAAC8CBC3F1B9B2BACDB9FA分别是中华人民共和国ABCD”七个汉字的机内码,20是空格的ASCII码,4348494E41分别是五个英文字母“CHINA”ASCII码,31393439别是数字字符“1949”ASCII编码,A1A3是标点的机内码。从上面可以看出,文本文件中信息是按单个字符编码存储的,如1949分别存储“1”“9” “4”“9”这四个字符的ASCII编码,如果将1949存储为079D(对应二进制为0000 0111 1001 1101,即十进制1949的等值数),则该文件一定不是文本文件。

    文本文件是包含用户可读信息的文件。这些文件以ASCII码方式存储,可显示和打印。文本文件的行不能包括空字符(即码中的NULL),行的最大长度(包括换行符在内)也不能超过(LINE_MAX)所定义的字节数。不过文本文件中并不限制使用除空字符以外的控制字符或其它不可打印字符。(二进制文件[此处指非文本文件]包含计算机可读信息的文件。二进制文件可以是可执行的文件,使系统根据其中的指令完成某项工作。命令和程序都是以可执行的而进制文件方式存储。二进制文件没有行的长度限制,也可包含空字符。)

    3. 如果将存储的信息采用字符串方式来保存,那么称此类文件为文本文件(可以按字符显示)。将文件看作是由一个一个字节(byte)组成的,那么文本文件中的每个字节的最高位都是0,也就是说文本文件使用了一个字节中的七位来表示所有的信息,而二进制文件则是将字节中的所有位都用上了。

    如果将存储的信息严格按其在内存中的存储形式来保存,则称此类文件为二进制文件。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。因此也把这种文件称作流式文件。文本或字符文件代表慢速设备,而二进制文件代表可以大块数据操作的快速外设,二进制文件内容基本无意义,系统对它不加解释地传给调用者,解释由调用者负责.而对字符文件,系统把他理解为单字节的ASCII或多字节的UNICODE字符串,并且对其中的特殊字符(如回车等)加以特殊处理.所以同一个文件,可以使用不同类型的系统调用.

    回车(CR)和换行(LF)符都是用来表示“下一行”的。而标准没有规定要使用哪一个。于是产生了三种不同的用法:

    (1) Doswindows采用回车+换行(CR+LF)表示下一行

    (2) UNIX采用换行符(LF)表示下一行

    (3) MAC机采用回车符(CR)表示下一行。

    当在不同的系统间传递文件,就要涉及格式的转换。

    文本方式和二进制方式的最大区别在于文本方式对于'\n'换行符的理解不同

    (1)DOS平台下,该字符会被展开成<CR>< LF>两个控制字符(相当于"\r\n"),在ASCII字符集下是 0DH,0AH

    (2)UNIX平台下,仅仅是<LF>,不会展开。

    (3)在二进制方式下,不管是什么平台,'\n'都是精确的<LF>

    linux/unix 系统上,只有一种文件类型的系统,带b字母的模式和对应的不带b字母的模式是相同的。

    关于EOF   EOF可以作为文本文件的结束标志,但不能作为二进制文件的结束符.feof函数既可以判断二进制文件,又可以判断文本文件. EOFWindows下是ctrl+zlinux下是ctrl+D.

    第二个问题就是文件按照文本方式或者二进制方式打开,两者会有什么不同呢? 其实不管是二进制文件也好,还是文本文件也好,都是一连串的01,但是打开方式不同,对于这些01的处理也就不同。如果按照文本方式打开,在打开的时候会进行translate,将每个字节转换成ASCII码,而以按照二进制方式打开的话,则不会进行任何的translate

    最后就是文本文件和二进制文件在编辑的时候,使用的方式也是不同的。譬如,你在记事本中进行文本编辑的时候,你进行编辑的最小单位是字节(byte);而对二进制文件进行编辑的话,最小单位则是位(bit),当然我们都不会直接通过手工的方式对二进制文件进行编辑了。

    4. 输入码、区位码、国标码与机内码 (都是汉字的编码形式)

    键盘是当前微机的主要输入设备, 输入码就是使用英文键盘输入汉字时的编码。

    计算机只识别由01组成的代码,ASCII码是英文信息处理的标准编码,汉字信息处理也必须有一个统一的标准编码。我国国家标准局于19815月颁布了《信息交换用汉字编码字符集——基本集》,代号为GB2312-80,共对6763个汉字和682个图形字符进行了编码,其编码原则为:汉字用两个字节表示,每个字节用七位码(高位为0,国家标准将汉字和图形符号排列在一个9494列的二维代码表中,;每两个字节分别用两位十进制编码,前字节的编码称为区码,后字节的编码称为位码,此即区位码,如字在二维代码表中处于17区第3位,区位码即为“1703 ”

    国标码并不等于区位码,它是由区位码稍作转换得到,其转换方法为:先将十进制区码和位码转换为十六进制的区码和位码,这样就得了一个与国标码有一个相对位置差的代码,再将这个代码的第一个字节和第二个字节分别加上20H,就得到国标码。如:字的国标码为3123H,它是经过下面的转换得到的:1703D>1103H->+20H>3123H

    国标码是汉字信息交换的标准编码,但因其前后字节的最高位为0,与ASCII码发生冲突,如字,国标码为31H23H,而西文字符“1” “#”SCII也为31H23H,现假如内存中有两个字节为31H23H,这到底是一个汉字,还是两个西文字符“1”;“#”?于是就出现了二义性,显然,国标码是不可能在计算机内部直接采用的,于是,;汉字的机内码采用变形国标码,其变换方法为:将国标码的每个字节都加上128,即将两个字节的最高位由01,其余7位不变,如:由上面我们知道,字的国标码为3123H,前字节为00110001B,后字节为00100011B,高位110110001B10100011B即为B1A3H,因此,保字的机内码就是B1A3H;

    汉字信息处理过程众所周知,计算机并不能识别汉字,因此必须要把每个字符转换成计算机能唯一识别的由01组成的代码,这个代码称为机内码

    汉字机内码的每个字节都大于128,这就解决了与西文字符的ASCII码冲突的问题。

    5. 测试

    FILE *fp;

                    int i = 12;

                    int j = 12;

                   

                    fp = fopen("01.txt","wb");

                    fprintf(fp,"%d",i);

                    fputc('\n',fp);

                    fwrite(&j,sizeof(int),1,fp);

                   

                    fclose(fp);

    即使是用二进制打开,但如果你用fputc,fputsfprintf这些函数,其实还是和用文本文件打开一样。只有用到fwrite/fread函数,才会看到一个整型占4个字节。

    按二进制写文件指的是直接按照数据在内存中的表现形式写入文件。例如,如果int型数据在内存中用 4 个字节表示,则写这个int数据的时候直接把对应的内存中 4 个字节的内容写入文件。在此过程中数据不需要做任何转换,所以效率较高。

    据有字符型和非字符型(数)两种。按文本方式写文件指的是将数据转换为对应的字符型数据之后再写入文件。对于字符型数据,由于其本身就是ASCII码字符,一般不必转换,直接写入文件。但是,由于不同的系统对于换行符('\n')有不同的处理(转换)方式,在有的系统(如Windows)下也会对 '\ n'作适当的转换。

    对于非字符型数据,都要进行转换处理。例如:int m = 12; 以及 double f = 2.3;,分别按照 "%d""%lf"方式将 m f 写入文件的时候,写入的分别是 '1''2'两个字符以及 '2''.' '3'等三个字符的ASCII码值。显然,如果按照二进制方式写的话,在文件中一般 m要占 4个字节、f 要占 8 个字节

     

     

    转载地址  http://liufabin66688.blog.163.com/blog/static/1396854820081027103133373/


    展开全文
  • 二进制文件文本文件和二进制数据

    千次阅读 2015-05-27 17:48:45
    二进制文件二进制数据文本文件个人简单理解

    我们知道计算机是用二进制来做运算处理的,所以所有存储在计算机里面的东西都是二进制的。

    我也知道这句话,但为什么总是听到别人说”二进制“文件和”文本“文件呢? 按照上面那句话来说计算机里面存放的东西都应该是二进制的。

    也就是0101的组合。

    其实计算机底层存储的都是二进制的”数据“,而不是二进制的文件。

    列举一个二进制文件如下:
    00000000h:0F 01 00 00 0F 03 00 00 12 53 21 45 58 62 35 34; .........S!EXb54
    00000010h:41 42 43 44 45 46 47 48 49 47 4B 4C 4D 4E 4F 50; ABCDEFGHIGKLMNOP
    以前我看到这个二进制文件的时候非常犯傻老钻牛角尖,我理解的二进制不应该是下面的样子吗!

    00000000 00000001 10001010 11011101


    其实硬盘里面存放的都属于”数据“,而存在计算机里面的数据(无论什么数据)都是以二进制的形式趴在”物理“磁盘上的。我们平时说的二进制文件或者是文本文件是站在操作系统层面上讲的,这里面的二进制文件存在硬盘里面是0101,但我们在操作系统上面看到该文件的内容就不一定是0101这样,我一直搞混这个概念。

    下面我们再解释一下二进制文件和文本文件的区别:

    将文件看作是由一个一个字节(byte)组成的,那么文本文件中的每个字节的最高位都是0,也就是说文本文件使用了一个字节中的七位来表示所有的信息,而二进制文件则是将字节中的所有位都用上了。这就是两者的区别。文件按照文本方式或者二进制方式打开,两者会有什么不同呢?其实不管是二进制文件也好,还是文本文件也好,都是一连串的0和1,但是打开方式不同,对于这些0和1的处理也就不同。如果按照文本方式打开,在打开的时候会进行translate,将每个字节转换成ASCII码,而以按照二进制方式打开的话,则不会进行任何的translate;最后就是文本文件和二进制文件在编辑的时候,使用的方式也是不同的。譬如,你在记事本中进行文本编辑的时候,你进行编辑的最小单位是字节(byte);而对二进制文件进行编辑的话,最小单位则是位(bit),当然我们都不会直接通过手工的方式对二进制文件进行编辑了。

    通过以上的描述我们应该知道二进制文件是什么意思了吧!


    以上内容均是个人理解总结,如有不对的地方请指出。

    展开全文
  • 文本文件转二进制文件 二进制文件转文本文件
  • FBX二进制文件格式规范

    千次阅读 2019-02-16 12:31:18
    FBX是一种流行的3D文件格式,最初由Kaydara为MotionBuilder开发,于2006年被Autodesk公司...FBX一个基于文本(ascii)和二进制版本。 由于没有关于二进制FBX格式的公开文档,Blender Foundation要求亚历山大写下...
  • 二进制文件,是无格式有数据类型的。比如 10 11 12 三个数。但二进制文件没有行的概念。我们要紧凑地储存他们。二进制整数存储范围为 0-255. 使用二进制文件的好处 为什么要使用二进制文件。原因大概三个:  第一...
  • <p>my go env <pre><code>GOARCH="amd64" GOBIN="" GOEXE="" GOHOSTARCH="amd64" GOHOSTOS="linux" ...GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" ...GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0" ...
  • C++读写二进制格式文件

    千次阅读 2017-08-18 20:28:13
    在实际应用中,为了节约存储空间,文件多会以二进制格式保存,所以必要了解如何在C++中读取二进制文件中的数据。
  • DXF文件格式——二进制 DXF 文件

    千次阅读 2018-02-06 01:42:41
    ASCII DXF 文件格式是 ASCII 文字格式的 AutoCAD 图形的完整表示,这种格式易于被其他程序处理。此外,AutoCAD 可以生成或读取二进制格式的完整 DXF 文件,并接收其他二进制文件格式的有限输入。
  • Java Android 二进制文件读写

    千次阅读 2019-03-08 16:53:28
    Java Android 二进制文件读写 ...Android studio工程目录中有二进制文件abcd.raw 。 二进制文件所放目录 app/src/main/assets/abcd.raw 1.1一次性读取二进制文件 private byte[] readLocalFile() throws IOExce...
  • 文本文件和二进制文件的差异和区别

    万次阅读 多人点赞 2018-05-16 11:11:20
    广义上的二进制文件包括文本文件,这里讨论的是狭义上的二进制文件与文本文件的比较: 能存储的数据类型不同 文本文件只能存储char型字符变量。二进制文件可以存储char/int/short/long/float/……各种变量值。 每...
  • 二进制文件、文本文件

    万次阅读 多人点赞 2018-08-11 14:19:21
    在windows上,用记事本就可以打开文本文件了,但要打开二进制文件需要对应的二进制文件解码器,因此,文本文件是更为大家所熟知的文件形式,而二进制文件的优点在于它的输入输出都省去了转换的过程,而且更省空间。...
  • 二进制文件与文本文件的区别

    千次阅读 2017-05-03 16:27:48
    文本文件是基于字符编码的文件,常见的ASCII、Unicode等,二进制文件是基于值编码的文件,可以看成是变长编码,你可以根据自己的需要,决定多少个比特代表一个值。 文本文件和二进制文件的存
  • C语言读写二进制文件

    千次阅读 2017-07-20 14:35:04
    可以这么说,除了文本文件以外的所有文件都是二进制文件二进制文件相对于文本文件更容易修改。因为文本文件的修改,需要修改以后写入内存,然后再清空原文件,再从内存中读取出修改以后的内容到本文件中。二进制...
  • import numpy as np import cv2 src=cv2.imread("../samples/emma_input.jpg",1) ...src2=np.fromfile("emma.bin",dtype=np.uint8) #从二进制文件恢复图片,注意dtype格式要与src一致 src2=np....
  • 使用Python读取二进制文件

    万次阅读 多人点赞 2018-03-13 09:00:25
    目标:目标文件为一个float32型存储的二进制文件,按列优先方式存储。本文使用Python读取该二进制文件并使用matplotlib.pyplot相关工具画出图像 工具:Python3, matplotlib,os,struct,numpy 1. 读取二进制文件...
  • [C语言] 文件操作,合并两个二进制文件为单独一个二进制文件; #define BUFFER_SIZE 1024//合并文件mergeFile (infile1, infile2, filenmae) void mergeFile(FILE *fp1,FILE *fp2,char *name){ FILE *...
  • //作用域内先把要写的二进制文件加锁,{}后自动解锁 std :: lock_guard < std :: mutex > lock ( database_mutex ) ; std :: ofstream out_database_file ( database_path , std :: ios :: binary | std :...
  • 文本文件与二进制文件

    千次阅读 2013-10-20 11:17:13
    文本文件与二进制文件  一、文本文件与二进制文件的定义  大家都知道计算机的存储在物理上是二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。这两者只是在编码层次上差异。  简单来...
  • 二进制文件转化成文本文件

    万次阅读 2019-06-20 11:13:46
    文本文件和二进制文件的区别在于打开这个文件的程序在对文件内容的解释上。 如果是文本文件,使用任何一种文本编辑器打开可以展现出人类可读信息字符,编码都符合某种编码方式,如ASCII、UTF8、GB2312等 如果是...
  • 文本文件与二进制文件的读写

    万次阅读 2017-09-30 19:31:00
    文本文件与二进制文件的读写 【https://wenku.baidu.com/view/d7dfd688680203d8ce2f243c.html】 以什么模式打开文件根本不重要,只要关心这个文件里的数据内容本身是二进制格式还是文本格式就好了!如果内容是文本...
  • Python二进制文件的读取与写入

    万次阅读 2019-06-20 19:12:59
    Python二进制文件的读取与写入 当读取音视频、图片等二进制文件时,需要采用二进制的读取方法。 一、读取 with open(file,mode) as f: pass file:文件路径 mode:rb 读取二进制文件 strb = f.read() 功能:一次性读取...
  • 文本文件和二进制文件学习

    千次阅读 2016-07-03 20:23:46
     二进制文件与我们通常使用的文本文件储存方式根本的不同。这样的不同很难用言语表达,自己亲自看一看,理解起来会容易得多。因此,我推荐学习二进制文件读写的朋友安装一款十六进制编辑器。这样的编辑器很多,...
  • pandas读写二进制文件

    千次阅读 2016-10-26 10:59:32
    1、 python内置了以pickle序列化的方式存取二进制文件。pandas也提供了方便的方法通过pickle方式加载和存储二进制文件。 原先版本的save和load方法在新版本中都被取代,现在可以通过to_pickle和read_pickle方法将...
  • python处理二进制文件(.bin)

    万次阅读 多人点赞 2019-02-11 16:30:33
    这里写自定义目录标题什么是二进制文件二进制文件读写bytes类型转化欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的...
  • java中关于二进制文件的读写[二进制文件读写]
  • 二进制文件转换为文本文件的工具

    千次下载 热门讨论 2012-01-12 10:31:53
    一个很有用的工具,可以将二进制文件转换为文本文件
  • C/C++读写文本文件、二进制文件

    万次阅读 多人点赞 2017-12-13 10:44:11
    C语言文本文件读写方式;C语言二进制文件读写方式;CPP文本文件读写方式;CPP二进制文件读写方式;

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,039,395
精华内容 415,758
关键字:

二进制文件有哪些格式