数学是神造科学,计算机是人造科学 。
所以在计算机的底层软件中,有着数不清的 tricks,让人很无奈,但又无法避免。譬如 Big-endian 和 Little-endian 的问题,据说 UNIX 曾经移植到另外一台不同 "endian" 的机器上,结果系统启动显示的是 NUXI 而不是 UNIX;再比如 ASCII 和 Unicode,以及各种各样地方编码纠结的关系;连最简单的 hello world,放到不同的平台上,生成的字符串也是不同的,考虑下面的程序:
#include <iostream>
using namespace std;
int main( int argc, char *argv[]) {
cout << "Hello\nWorld";
}
这个程序如果在 *nix 平台下执行,生成的字符串的长度是 11:
lox@lox-pad ~/tmp> clang++ main.cxx -o main
lox@lox-pad ~/tmp> ./main | wc -c
11
lox@lox-pad ~/tmp> ./main | od -c
0000000 H e l l o \n W o r l d
0000013
lox@lox-pad ~/tmp>
但是如果放到 Windows 上执行,那么程序生成的字符串的长度是 12,字符串的值是 "Hello\r\nWorld"
,so,what is '\r'
?
细究起来,我们发现,一个小小的 newline 字符也有如此多的故事,或者说,历史包袱。 Wikipedia 里面给出了完整的解释。简单来讲, '\n'
和 '\r'
的概念来源于早期的打字机, '\n'
代表 "line feed",表示打字机打完一整行后相对纸面向下移动一整行的距离,而 '\r'
则代表 "carriage return",就是控制打字机重新回到一行的行首, "\r\n"
连在一起用才会使得打字机移动到新的一行的行首开始工作。而在后来的 OS 设计中,由于在 TTY 终端并没有实体的打印机控制操作,所以在设计上将 "\r\n"
进行了简化处理,只是不同的 OS 采取的策略不一样。我们仅以 Windows、Linux、Mac这三种最流行的系统来做说明。
- Windows –
"\r\n"
- Linux –
'\n'
- Mac –
'\r'
所以 Linux 和 Mac 上的多行文本文件用 Windows 的 Notepad 打开,常常会出现多行并做一整行的现象,而 Windows 上的文本文件用 Linux 上的 Vim 或者 Emacs 等editor来访问,往往会看到奇怪的 '^M'
字符,事实上这个 '^M'
并不是 '^'
+ 'M'
的组合,它就是 Windows 中的 '\r'
。在 Linux 终端中你可以通过输入 Ctrl+v Ctrl+m
来获得 '\r'
这个输入。
那么,我们怎样去掉这写讨厌的影响心情和版面美感的 '\r'
呢?方法有很多,最简单的,安装 dos2unix 这个小软件,然后直接 dos2unix filename
即可,Arch Linux 可以通过 pacman -S hd2u
来获取 dos2unix,反过来也有 unix2dos 这样的命令。
除了 dos2unix,利用 Linux 本身的 shell 工具,也可以达到目的,比如:
sed 's/\^M//g' filename
sed 's/\r//g' filename
cat filename | tr -d '\r' > newfile
vim: %s/\^M//g
最后需要注意的是,C 语言的标准库针对这种情况做了专门的规定,并针对性的规定了 text mode 和 binary mode,这种小的 trick 平时不需要注意,但是没准某一天就会冒出来拷打一下你的耐心。