文档章节

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

jieqiuming
 jieqiuming
发布于 2014/04/10 12:29
字数 2215
阅读 631
收藏 8
  • 使用 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系统自带的记事本 和写字板 怎么办?看好了:

  • 先用写字板打开文件,不用管乱码问题,直接保存。

  • 再用记事本打开。(恩,此时一切正常)

参考


本文转载自:http://blog.csdn.net/dbzhang800/article/details/6430280

共有 人打赏支持
jieqiuming
粉丝 2
博文 10
码字总数 10704
作品 0
合肥
高级程序员
私信 提问
小小换行符乱谈(文本文件vs二进制文件)

使用 C 语言的 fopen 打开文件时,可以指定的 mode 有 12 个,其中 6 个包含 "b" 使用 C++ 的 fstream 打开文件时,可用的模式组合有 24 个(?),其中 12 个包含 "binary" 使用 python 的 op...

晨曦之光
2012/05/08
493
0
fopen参数@windows和linux的区别

作为文件的数据,在计算机中都是以二进制的形式存储的。因此,对于文本文件和二进制文件的区分,不是在物理上的,而是在逻辑上的。 在开发时,遇到一个小问题,代码在linux下运行读写文件正常...

地狱的烈火
2013/07/29
0
0
也谈文本文件与二进制文件

网上关于文本文件与二进制文件的文章很多,但遗憾的是,这些文章讲得都比较 散。下面我将结合所查到的资料,从多个角度谈谈文本文件与二进制文件。 一、文本文件与二进制文件的定义 大家都知...

长平狐
2012/09/03
163
0
飘逸的python - 不要用二进制模式打开文本文件

先看下面代码的“诡异”现象。 假设在windows下,我有个f.txt文件,里面的内容是下面这样的。 hello world 代码一, with open('f.txt', 'r') as f: with open('f.txt', 'rb') as f: 输出 ['...

长平狐
2013/08/21
1K
0
ParisGabriel:Python全栈工程师(0基础到精通)教程 第二十五课(文件操作、)

ParisGabriel 每天坚持手写 一天一篇 决定坚持几年 为了梦想为了信仰                         Python人工智能从入门到精通 最近简直要死了 发烧感冒 喉咙痛。。。。...

ParisGabriel
07/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

day149-2018-11-16-英语流利阅读-待学习

抖音登陆海外市场,歪果仁作何反应? Lala 2018-11-16 1.今日导读 抖音在国内已经火了有一段时间了,但你知道它已经进军海外,而且火了一把吗?中国版抖音的宣传语是“记录美好生活”,但外国...

飞鱼说编程
6分钟前
1
0
Confluence 6 教程:在 Confluence 中导航

当你对 Confluence 有所了解后,你会发现 Confluence 使用起来非常简单。这个教程主要是针对你使用的 Confluence 界面进行一些说明,同时向你展示在那里可以进行一些通用的任务和操作。 空间...

honeymose
今天
2
0
sed, awk 练习

1. sed打印某行到某行之间的内容 2. sed 转换大小写 将单词首字母转化大写 将所有小写转化大写 3. sed 在某一行最后面添加一个数字 4. 删除某行到最后一行 解析: {:a;N;$!ba;d} :a : 是...

Fc丶
今天
2
0
babel6升级到7,jest-babel报错:Requires Babel "^7.0.0-0", but was loaded with "6.26.3".

自从将前端环境更新到babel7,jest-babel之前是基于babel6的,执行时候就会报:Requires Babel "^7.0.0-0", but was loaded with "6.26.3". 很烦,因为连续帮好几台电脑修复这个问题,所以记...

曾建凯
今天
1
0
探索802.11ax

802.11ax承诺在真实条件下改善峰值性能和最差情况。 如何改善今天的Wi-Fi? 在决定如何改进当前版本以外的Wi-Fi时,802.11ac,IEEE和Wi-Fi联盟调查了Wi-Fi部署和行为,以确定更广泛使用的障碍...

linuxprobe16
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部