区分换行符
换行符的定义
不考虑远古时代打印机,换行符在不同操作系统下定义不同:
CR符号:16进制为0X0D,ASCII转义符号表示为\r
,意为回车(Carriage Return)
LF符号:16进制为0X0A,ASCII转义符号表示为\n
,意为换行(Line Feed)
Windows系统: 用CR LF两个符号,表示“行与行之间的间隔”。注意最后一行末尾没有换行符。
Linux/MacOSX系统:用LF一个符号,表示“每行末尾符号”。注意最后一行末尾也有换行符。
MacOS在OSX之前的版本:用CR符号。从OSX开始用LF。
EditorConfig里应该配置换行符吗?
.editorconfig文件中,设定end_of_line
为lf
或crlf
,分别表示Linux/MacOSX和Windows下的换行符。
特定平台的文件,例如.sh
,.bat
,个人觉得应该设定end_of_line
。
但对于跨平台的文件,典型如C/C++的.h/.hpp/.c/.cpp/.cc文件,不应该设定end_of_line
。例如Windows下仍然配置为lf
,则一旦有中文注释,会编译报错,或编译通过但调试阶段显示异常,当前调试行和运行行不对应。
P.S. 曾有讨论添加end_of_line=native
的提议,但反对人数略多,官方否决了。
应该在版本控制工具(svn, git等)的全局配置中,配置换行符设定。
用cat命令显示换行符
cat -A file
(TODO)
Vim状态栏显示的行尾换行符,解释
[noeol]
多行文本,相邻行之间用LF(16进制0x0A,转义符\n
)分隔,最后一行末尾没有LF。
通常产生原因:在Windows下创建文件,填写内容保存;同时,git的config中core.autocrlf没有设定,或设定为true;则执行git add和git commit后,存储该文件到index中是用LF替代CRLF的,并且最后一行末尾也不会自动追加LF。
则在Linux下git clone后用Vim打开该文件,显示[noeol]。
(但如果是在Windows下的Vim里查看,显示[noeol][dos])
用Vim打开并保存后,会在最后一行后追加LF,使它变成unix格式。(Win和Linux下的Vim都会这么做)
定义空行:除了
Linux/MacOSX下:一个行,包括它的行尾换行符,则空行定义只有换行符的行。
Windows下:一个行,不包括它的行尾换行符,则空行定义为不含任何字符的行。
[noeol][dos]
多行文本,行与行之间以CR LF分隔,最后一行末尾没有CR LF,也没有LF或CR。
典型出现场景:
Windows下的文件(换行符用CR LF),文件最后一行有内容而非空行;把这个文件拷贝到Linux/MacOSX下,用Vim打开,则显示[noeol][dos]。或者,这个文件在Windows下被Vim打开,也同样显示[noeol][dos]。
用Vim打开后,最后一行新增一行然后保存,重新打开则显示[dos]。也就是Vim给新增的换行符,是根据已有的换行符(CR LF)来增加的。
总结:dos的意思是“CR LF”作为换行符。
[dos]状态
多行文本,行与行之间用CR LF分隔,最后一行末尾有CR LF。或者说,在Windows上编辑该文件时,最后一行是空行。而按照Linux下换行符的定义,这个空行被“吃掉”,整个文本比Windows上少一行。
Vim的set list
命令
这个命令估计主要是区分Tab和换行的,因此每行末尾(包括最后一行)后都显示$
。并不能区分CR LF和LF,也不能区分最后一行末尾是否有换行符。
vim中的^M
符号
在Vim中,^M
是用来表示ASCII的13(\r
,16进制为0x0D
)的,而M
是英文字母表中第13个字母。
因此原因也不难猜到:产生文本文件时用\r\n
作为换行符了(通常是在Windows下产生),并且原封不动拷贝到Linux下(或是在Windows下用vim/gvim查看),Vim把\n
当做换行符处理,而把\r
当做普通字符予以显示,显示的效果就是^M
。
git中的换行符配置
需要明确,一个git repo它的工作区和索引区使用相互独立的换行符规则。
也即:git add & git commit,提交代码,根据autocrlf做转换;git clone/git pull,根据autocrlf
- AutoCRLF
#提交时转换为LF,检出时转换为CRLF
git config --global core.autocrlf true
#提交时转换为LF,检出时不转换
git config --global core.autocrlf input
#提交检出均不转换
git config --global core.autocrlf false
(Windows下默认值为true)
- SafeCRLF
#拒绝提交包含混合换行符的文件
git config --global core.safecrlf true
#允许提交包含混合换行符的文件
git config --global core.safecrlf false
#提交包含混合换行符的文件时给出警告
git config --global core.safecrlf warn
C/C++/Python中写入文件的语句
常见的编程语言大都支持“把字符串写入文件”的功能。以C/C++为例,假设fout
是输出文件指针,则:
fprintf(fout, "hello\n"); //行尾写入\n单个符号。推荐
fprintf(fout, "hello\r\n"); //行尾写入\r\n两个符号。不推荐。
通常来说,编程语言中写入文件时,换行符用\n
表示即可。有人可能担心说Windows下的换行符不是\r\n
吗?以Windows10为例,用系统自带的Notepad软件打开以\r\n
作为行尾的文本文件,状态栏显示的换行符并不是"CRLF",而是"Macintosh(CR)"。测试代码:
#include
int main() {
FILE* fp = fopen("out.txt", "w");
fprintf(fp, "hello\r\n");
fprintf(fp, "123 test\r\n");
fprintf(fp, "oh yeah~~\r\n");
fclose(fp);
return 0;
}
编译运行后用Notepad自行打开"out.txt"查看即可发现,文本区域显示内容也和预期不太一样:
hello
123 test
oh yeah~~
用HxD64.exe查看"out.txt"的16进制表示:
68 65 6C 6C 6F 0D 0D 0A 31 32 33 20 74 65 73 74 0D 0D 0A 6F 68 20 79 65 61 68 7E 7E 0D 0D 0A
可以发现,行尾是"OD OD OA",也就是说:原本我们在C/C++代码中输出的\r\n
,实际上写入到文件的字符是\r\r\n
。什么意思呢?Visual Studio(这里我假定你用Windows时用的C/C++编译器为MSVC,用Cygwin的暂不考虑)的C/C++实现,把fprintf
里的\n
给强行换成\r\n
了;而这也导致了“Notepad认为换行符是\r
,也就是Macintosh的换行符,并且原本输出的两行之间多了一个空行”。