目录
- 背景
- 第一部分 回车和换行
- 第二部分 兼容性问题解决
- 参考文献及资料
背景
或许你遇到过这样的坑。当你信心满满将自己编写的程序文件或配置文件上传到生产环境(linux
),却发现无法运行或者生效。但是明明在本地(Windows
)测试运行没有问题。那么很大几率遇到文本行末结束符的坑。
Unix和Windows中文本行末结束符是不同的。本文将详细讲解这个坑的背景、产生原因和解决办法。
第一部分 回车和换行
1.1 历史背景
关于回车(Carriage Return)和换行(Line Feed)的来历,需要从英文打字机讲起。在机械英文打字机中有个叫“字车”的部件,每打印一个英文字符,“字车”前进一位。但是纸张每行是有限制的,打满一行后,“字车”需要重置回到起始位置。从机械角度看,打印机有两个响应动作:(1)“字车”归位;(2)纸张的滚筒上卷一行,开始新的一行。这里“字车”归位就是“回车”,而滚筒上卷一行就是“换行”。
到了电传打印机时候,需要使用控制字符来通知打印机执行非打印操作的指令(回车(CR)和换行(NL))。而在ASCII码中分别使用\r
(值13)和\n
(值10)表示。
再后来计算机发明后,两个概念也就被搬到计算机中(计算机通常需要和打印机交互,保持兼容性)。考虑到当时存储资源的昂贵,就有人提出来文本中使用两个操作符表示行末结束较为浪费,于是分歧就产生了。
1.2 分歧
目前主流操作系统(Unix、Windows、Mac)中分歧如下:
操作系统 | 系统行末结束符 | 备注 |
---|---|---|
UNIX | \n | |
window | \n\r | |
MAC OS | \n | v9 之前 Mac OS 用 ‘\r’ |
注:从2001年3月发布的Mac OS 10.0开始,系统行末结束符采用”\n”。
这种分歧就导致不同操作系统之间兼容问题。
第二部分 兼容性问题解决
这种兼容问题通常发生在:不同操作系统之间传输纯文本文件。
- Unix/Mac系统创建的文件在Windows里打开,文字会变成一行。因为没有
\r
。 - Windows里的文件在Unix/Mac下打开的话,在每行会多出一个
^M
符号。多了\r
。
2.1 Windows文件上传Linux问题
我们经常遇到的问题是:Windows下编写的Shell脚本或者Python脚本,放到Linux下执行会出错。通常上传文件前,使用UE
(Ultraedit
)或者Nodepad++
来转换。
UE
中,执行“File->conversions->Dos to Unix”,将文件中\n\r
转换成\n
。Nodepad++
中,执行“编辑->档案格式转换->转换为UNIX格式”。
上传到Linux后还可以使用下面的命令查看是否准确:
1 | cat -v test.txt |
如果每行换行处都有^M
,这说明仍然是Windows下的文本文件。
注:cat -A
命令:显示不可见字符。如换行符显示为“$”,TAB 显示为
^I等。在这种模式下,回车(
\r)字符将显示为
^M
可以使用下面的命令进行统一替换:
1 | sed -i 's/^M$//g' test.txt |
另外还有专用命令dos2unix
(如果操作系统没有改命令需要安装一下)。
1 | dos2unix test.txt |
最后还可以使用vi
打开文件,输入:set fileformat=unix
,回车。最后保存退出即可。
2.2 Linux文件上传Windows问题
在Windows中使用Ultraedit
或者Nodepad++
文本编辑器查看Linux文件,而不是系统自带的记事本。
2.3 FTP中传输问题
FTP软件在传输文件的时候,通常有两种模式:文本模式(ASCII模式)和二进制模式(BINARY模式)。两种模式的区别就是行末结束符的处理,BINARY模式不会对数据进行任何处理。而ASCII模式将行末结束符转换为本机操作系统的行末结束符。例如Windows系统将文本文件上传至Linux,就会将\r\n
替换成\n
。
在使用过程中需要注意两种模式的差别。特别的上实际生产环境上线投产过程中建议统一使用二进制模式,避免FTP对文件进行转换。
2.4 编程语言中
- Python 使用 “Universal Newline“ 处理这个问题。文本使用 open() 方法打开时,会对行末结束符进行识别并一致处理成 ‘\n’,在文件写入的时候,使用 write(‘\n’) 即可,Python 会根据当前程序执行的操作系统自动处理。
- Java中行末结束符使用下面的函数方法统一处理:
1 | System.getProperty("line.separator") |
参考文献及资料
1、Line_feed,链接:https://nl.wikipedia.org/wiki/Line_feed