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

小小换行符乱谈(文本文件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系统自带的记事本写字板 怎么办?看好了:

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

参考

分享到:
评论

相关推荐

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

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

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

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

    文本文件与二进制文件

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

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

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

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

    本段代码描述了如何使用C++的fstream库来读写文本文件和二进制文件。对于文本文件,我们使用std::ofstream和std::ifstream类,并通过流操作符和&gt;&gt;或std::istreambuf_iterator来写入和读取文本数据。对于二进制文件,...

    二进制文件与文本的区别

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    在文本模式下,系统会识别特定的字符序列,如回车换行符('\n'),并将其转换为跨平台兼容的形式。例如,在Windows系统中,回车换行通常表示为0x0D, 0x0A,而在Linux或Mac系统中,回车换行仅表示为0x0A。如果在读取...

    C++课件_IO流_二进制文件流操作PPT

    - 二进制文件操作不会自动处理换行符,因此在读写时需要特别注意数据的边界和格式。 - 二进制文件不能直接用文本编辑器查看,因为它们可能包含不可见的控制字符或字节序列。 - 在处理二进制文件时,确保读写的数据量...

    visual c++ vc++对二进制文件进行浏览、编辑的实现.zip

    - 在编辑二进制文件时,可能需要动态分配内存来存储整个文件或部分数据块,确保正确使用`new`和`delete`操作符进行分配和释放。 8. **错误处理**: - 文件操作过程中可能出现错误,如打开失败、读写错误等,需要...

    SFTP工具,二进制传输

    如果一个二进制文件被误用ASCII模式传输,可能会导致文件损坏,因为ASCII模式会尝试对非文本数据进行错误的字符转换。因此,对于不确定类型的文件,使用二进制模式通常是更安全的选择。 总结来说,SFTP工具,尤其是...

    Node.js安装包和二进制文件

    标题中的"Node.js安装包和二进制文件"指的是这个压缩包内包含的两个主要内容:一个是Node.js的安装程序,另一个是Node.js的二进制文件。安装程序通常是用于在用户的计算机上设置Node.js环境的,而二进制文件则包含...

    swfile.rar_fread text_fread 二进制_二进制文件_读写文件

    这是因为文本模式下,C编译器会自动进行换行符的转换,这可能导致二进制数据的损坏。 标题中提到的"swfile.rar_fread text_fread 二进制_二进制文件_读写文件"暗示我们可能在处理一个名为"swfile.rar"的压缩文件,...

    去掉换行符

    这款软件的操作流程可能是这样的:首先,用户将包含换行符的文本文件导入到软件中,然后软件会扫描文件中的所有换行符,并将其删除或替换为无换行状态。处理完成后,用户可以选择直接打印优化后的文本,或者保存为新...

    Qt实现GUI的二进制文件读写操作(源码分析+工程打包+测试例子)

    在本文中,我们将深入探讨如何使用Qt库进行GUI界面下的二进制文件读写操作,同时结合源码分析、工程打包以及测试实例来全面理解这一主题。Qt是一个跨平台的应用程序开发框架,广泛用于创建图形用户界面和其他软件。...

Global site tag (gtag.js) - Google Analytics