`
tomhibolu
  • 浏览: 1407482 次
文章分类
社区版块
存档分类
最新评论

小小换行符乱谈(文本文件vs二进制文件)

 
阅读更多

  • 使用 C 语言的 fopen 打开文件时,可以指定的 mode 有 12 个,其中 6 个包含 "b"

  • 使用 C++ 的 fstream 打开文件时,可用的模式组合有 24 个(?),其中 12 个包含 "binary"

  • 使用 python 的 open 打开文件,除了可以使用 C 中的 12 个模式外,还可以使用 "U" 或 "rU"

  • 使用 Qt 库的 QFile 打开文件时,可以指定 QIODevice::Text 或不指定

  • ...

如此种种,看起来是如此的复杂,难怪很多刚接触编程的网友都不相信(或者不想相信):

  • 这一切仅仅是为了一个小小的换行符!

是啊,一个小小的换行符值得如此大动干戈么?

  • 当使用 windows 下弱智的记事本时,会不会遇到:本该换行的地方,它显示一个黑色方块?
  • 当使用高级点的编辑器时,是不是都提供设置换行符的功能?
  • 当使用跨平台的工具 (比如windows下git) ,是不是需要特别注意换行符设置?
  • ...

文本 vs 二进制

哎,等等...

你前面提的C中的"b",C++中的"fstream::binary",Qt的"QFile::Text",我都知道啊:不是区分文本和二进制操作的么?和换行符有什么关系?!

那么我们有必须要看看:

什么是文本文件(Text File)?

  • 所有的文件都是二进制文件(Binary File)

  • 如果一个二进制文件的内容全是可打印的字符和空白字符(空格、Tab、回车、换行等)组成,可称其为文本文件。

换句话说:本来就不存在 文本文件 这个独立类别,文本文件属于二进制文件。

如果这样,为何C、C++等等打开文件是都提供文本和二进制两种模式么?(暂不解释^_^)

考虑一个例子:打开文件(不管后缀名等等),分别写入:

"/x10/x11/x12/x13/x14"

不可见字符

"/x30/x31/x32/x33/x34"

"01234"

而后者由于全部是可打印字符,你可能就会称其为文本文件。

文件 vs 模式

注意区分两个概念:当我们提C、C++打开文件的方式时,我们一直在说 文本模式 和二进制模式,而不是说打开 文件文件 和二进制文件。这中间有很微妙的区别。

任何一个文件,你都可以用文本或二进制模式打开。但是对于 *.png 等这些东西,你用文本模式打开读进来的往往不是你期望的结果。

考虑这样一个文件 hello.txt,其内容:

line1/r/nline2/r/n

如果在windows下:你用文本模式打开,读进来多少个字符?用二进制模式打开,又是多少个字符?为何同一个文件,读进来的不一样?

换个角度考虑考虑

我们前面提到(C、C++、Python、还有不该和语言并列Qt)的文件操作,都是需要通过系统调用对文件进行操作的。具体一点:

  • 在Windows下,不管通过哪种方式,最终都需要使用
HANDLE WINAPI CreateFile(
  __in      LPCTSTR lpFileName,
  __in      DWORD dwDesiredAccess,
  __in      DWORD dwShareMode,
  __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __in      DWORD dwCreationDisposition,
  __in      DWORD dwFlagsAndAttributes,
  __in_opt  HANDLE hTemplateFile
);

参数很多,每一个参数又有很多标记位组成(具体看MSDN)。但是你可以发现:对它来说,不存在文本文件和二进制文件的区别,你也无法设置text或binary等标记位!!

  • 在posix 系统下,文件操作需要
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);

同样,这儿可以设置flags和mode,可以设置的标记很多。但是就是没有提供text和binary相关的东西!!

是不是很有意思?

  • 系统的文件操作接口压根就没有二进制和文本的区别!
  • 使用这些接口的C、C++、Python 却提供了二进制和文本两种模式

换行符

是时候谈 换行符 了:

  • newline
  • line break
  • EOL (end-of-line)

想象一下,一个文本编辑器打开一个"文本文件",遇到哪个字符开始换行呢?

  • 想想Windows下的记事本,遇到遇到"/r/n"它处理成换行,遇到'/n'它就只会显示黑方框。

应用程序和操作系统通常用1到2个字符代表换行:

CR+LF

Windows、DOS、Symbian、Palm ...

LF

GNU/Linux、Mac OS X、FreeBSD ...

CR

Mac OS 9(之前)...

LF+CR

Acom BBC

RS

QNX 在posix之前

NEL

z/OS、i5/OS ...

...

...

这些之中,其实我们也只对 CR+LF 与 LF 这两种换行符感兴趣。

有什么问题么?

本来一切很正常的:

在Windows下:

  • 调用 CreateFile 打开文件

    HANDLE hFile = CreateFile (TEXT("twoline.txt"), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,   NULL);
  • 调用 WriteFile 写入两行

    DWORD dwBytesWritten;
    WriteFile (hFile, "line1/r/nline2/r/n", 14,
                     &dwBytesWritten, NULL);
  • 调用CloseHandle关闭文件

    CloseHandle(hFile);

在Posix系统下

  • 调用open打开文件
    int fd = open("twolines.txt", O_WRONLY|O_CREAT);
  • 调用write写入两行
    write(fd, "line1/nline2/n", 12);
  • 调用close关闭文件
    close(fd);

各个平台相安无事,windows下你想换行就用'/r/n',posix下想换行就用'/n'

如何就出问题了呢?

各个平台的换行符不一致,一旦涉及跨平台问题就出来了。

考虑一下,如果使用C语言的binary模式的话,我们想生成一个像前面一样包含两行代码的文件,该怎么办?

  • 根据平台不同,用#if #else 进行预处理?
     #ifdef _WIN
     fwrite("line1/r/nline2/r/n");
     #else
     fwrite("line1/nline2/n");
     #endif
  • 还是采用某种方式,同一行代码:在不同平台下生成不同的东西
      fwrite("line1/nline2/n");

应该就是为了这个吧,引入了一个"文本模式"

  • 写入时,遇到'/n'就转换成平台相关的换行符(对与windows就是"/r/n");
  • 读入时,遇到平台相关的换行符(比如windows下的"/r/n"),转换成'/n'
  • 注意:对与posix系统,'/n'就是系统换行符,不存在转换
    • 所以我们经常听说:linux下文本文件和二进制文件没有区别。

正是为了这个换行符,所以C、C++、Python等语言提供的文件操作函数才都有了Text、Binary两种模式:

C、C++、Qt

C语言的文件操作

#include <stdio.h>
FILE *fopen(const char * restrict filename, const char * restrict mode);

除了文件名之外,还要传递一个 mode 的字符串作为标记。而这些标记分为带b和不带b两类:

文本

二进制

r

rb

只读或只写

文件必须存在

w

wb

文件存在则清空、不存在则创建

a

ab

追加;文件不存在则创建

r+

r+b rb+

读写

同r和rb

w+

w+b或 wb+

同w和wb

a+

a+b或 ab+

同a和ab

C++的文件操作时

explicit fstream ( const char * filename,ios_base::openmode mode = ios_base::in | ios_base::out ); 

除了文件名之外,我们需要传递一个 mode:

app

(app end) 每次写操作前找到文件尾

ate

(at e nd) 打开文件后立即将文件定位到文件尾

binary

(binary ) 以二进制模式进行IO操作

in

(in put) 允许读操作

out

(out put) 允许写操作

trunc

(trunc ate) 打开文件时清空文件流

这样看似乎没神马意思哈?一般都是组合使用的:

  • in、out、app、trunc的有效组合如下

out

只写

清空文件内容

out|app

追加

out|trunc

等同out

in

只读

in|out

读写

in|out|trunc

清空文件内容

  • 6个标记这儿只提了4个,其他两个和这儿的可以随意组合,不受限制(我对此不太确定,dbzhang800 2011.5.18)
  • 也就是:带binary和不带binary的组合数目一样多的

Qt的文件操作

bool QFile::open ( OpenMode mode ) 

这儿是mode又是什么东西?

QIODevice::NotOpen

QIODevice::ReadOnly

QIODevice::WriteOnly

QIODevice::ReadWrite

QIODevice::Append

QIODevice::Truncate

QIODevice::Text

QIODevice::Unbuffered

其他

现在国内用linux的似乎越来越多了,很多人有这个问题:

linux下创建了一个包含中文的文件,拷贝到windows下面。
 用记事本打开看 ==> 汉字正确,换行的地方出现了黑方块
 用写字板打开看 ==> 换行正确,汉字乱码

很有意思?可是如何解决?

  • 找个支持utf8编码和'/n'换行的编辑器即可解决问题。
  • 在linux采用"/r/n"换行和gb18030编码保存文件,也可以解决问题

如果就用windows系统自带的记事本写字板 怎么办?看好了:

  • 先用写字板打开文件,不用管乱码问题,直接保存。
  • 再用记事本打开。(恩,此时一切正常)

参考

分享到:
评论

相关推荐

    文本文件与二进制文件

    在不同的操作系统中,文本文件的存储转换时间也不同,如 Windows 操作系统需要对回车换行符进行转换,而 Linux 操作系统不需要。 文本文件和二进制文件是计算机存储中的两种基本文件类型,它们的区别在于逻辑上的...

    文本文件和二进制文件区别读写.rar

    在计算机编程中,文本文件和二进制文件是两种常见的数据存储形式,它们在读写方式上有着显著的差异。本文将深入探讨这两种文件类型及其在C语言中的处理方法。 首先,文本文件是以人类可读的形式存储数据的,通常...

    判断文件是文本文件还是二进制文件

    在计算机领域,文件可以分为两种主要类型:文本文件和二进制文件。了解如何区分这两种文件类型对于数据处理和程序开发至关重要。文本文件通常由可读的字符组成,如ASCII编码,而二进制文件则包含机器可理解的原始位...

    3维数据stl文件从文本格式转换到二进制格式

    STL文件有两种主要格式:ASCII(文本)格式和二进制(binary)格式。 文本格式的STL文件使用ASCII码存储每个三角面片的数据,包括正常向量和顶点坐标,这种格式易于阅读和编辑,但文件大小较大,传输和处理速度较慢...

    二进制文件与文本的区别

    编辑器在创建文件时,也会在行尾添加换行符,如C语言中的'\n'。 相比之下,二进制文件则直接复制内存中的数据到文件中,不经过任何字符编码过程。这意味着,二进制文件能更有效地利用存储空间,因为它不会增加额外...

    转换文本数字为二进制并保存

    4. **保存二进制数据**:转换后的二进制数据可以以文本文件或二进制文件的形式保存。文本文件通常是以ASCII码或UTF-8编码的二进制表示,而二进制文件则直接保存原始的比特序列。在C++中,可以使用`std::ofstream`...

    C++中文件以二进制形式和以文本形式打开的区别

    在文本文件中,每行结尾通常包含换行符,如Windows系统的`&lt;CR&gt;&lt;LF&gt;`(回车加换行),或Unix/Linux系统的`&lt;LF&gt;`(仅换行)。文本文件易于人类阅读和编辑,适用于存储源代码、配置文件、日志记录等。 #### 二进制文件...

    C++读写文本文件和二进制文件(源代码)

    ### C++中的文本文件与二进制文件读写详解 #### 一、概述 本文将详细介绍如何使用C++中的`fstream`库来进行文本文件和二进制文件的读写操作。我们将通过具体的代码示例来解释如何利用这些功能,以及在实际应用中...

    将源文件读入matlab后二进制输出到文本文件

    在MATLAB中,将源文件读入并以二进制格式输出到文本文件是一个常见的数据处理操作,尤其在处理大量数据或需要与不同编程语言交换数据时。这个过程包括两个主要步骤:首先读取源文件,然后以二进制格式写入到新的文本...

    巧用AWK处理二进制数据文件

    2. 使用sed工具对文件进行文本替换,去掉制表符和恢复正确的记录间的换行符。 3. 使用AWK工具对文本文件进行处理和格式化。 在AWK脚本中,我们可以使用BEGIN语句来定义初始值,使用for循环来处理每一行的数据,并...

    c++操作二进制文件详解

    - 写入二进制文件时,使用`endl`会写入一个换行符,但二进制模式下,`endl`不会执行平台相关的换行操作,因此可能导致字节数的差异。 4. **二进制文件的常见用途** - 存储结构化数据,如数组、结构体或自定义对象...

    C语言二进制文件处理

    - **文本模式**:在此模式下,C语言会自动转换文件中的换行符(\n)为特定的操作系统换行序列。例如,在Windows系统中,换行符会被转换成`"\r\n"`。 - **二进制模式**:该模式不对文件内容进行任何特殊处理,适用...

    老生常谈文本文件和二进制文件的区别

    文本文件和二进制文件是计算机中常见的两种文件类型,它们在存储和处理方式上有显著差异。理解这两种文件的区别对于编程和数据存储至关重要。 首先,文本文件,也称为ASCII文件,是以ASCII编码来存储信息的。ASCII...

    fortran处理二进制文件

    在Fortran语言中处理二进制文件是与处理文本文件非常不同的操作,因为二进制文件的数据以字节形式直接储存,而不通过文本编码来表示。二进制文件通常用于需要精确控制数据的存储方式和节省存储空间的场景。由于二...

    c++文件的操作他是用二进制试图打开一个文件

    ### C++ 文件操作:二进制与文本模式详解 在C++编程中,对文件进行操作是一项非常重要的技能。本文将详细介绍如何使用C++来处理文件,并特别关注文本模式和二进制模式的区别。 #### 一、文件操作基础 在C++中,`...

    C语言采用文本方式和二进制方式打开文件的区别分析

    - 文本方式:在Windows系统下,文本文件中的换行符(LF)在写入时会被转换为回车换行(CR-LF),而在读取时,连续的CR-LF会被转换回LF。这种转换可能导致文本方式读取的文件内容长度不同于原始二进制内容。 - 二...

    图片格式文件转化为二进制数数组程序

    4. **显示数据**:在RichtextBox控件中显示二进制数组,通常会使用换行符分隔每个字节的二进制表示,以便于阅读。RichtextBox允许插入格式化的文本,所以可以调整字体、颜色等,使得二进制数组更易读。 对于标签...

    java判断一个文件是否为二进制文件的方法

    如果字节值小于32,并且不是常见的文本分隔符(制表符、换行符和回车符),我们认为这是一个非打印字符,可能是二进制文件的一部分。 ```java if (t != 9 && t != 10 && t != 13) { isBinary = true; break; } ``...

    读取文件内容并以二进制(16进制)显示.读取文件内容以16进制编码显示

    在编程领域,尤其是在C语言中,读取文件内容并以二进制(16进制)方式显示是一项基本操作。这种技术广泛应用于数据处理、内存分析、文件格式研究以及调试等场景。本教程将深入探讨如何使用C语言来实现这个功能。 ...

    excel 文件格式 二进制

    本文主要探讨的是基于二进制的 BIFF8(Binary Interchange File Format)文件格式,它是 Excel 2003 及更早版本所采用的标准。 #### 二、BIFF8 概览 BIFF8 是 Microsoft Excel 文件格式的一种实现,它支持 Excel ...

Global site tag (gtag.js) - Google Analytics