区分换行符

crlf2.jpg

区分换行符

换行符的定义

不考虑远古时代打印机,换行符在不同操作系统下定义不同:

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_linelfcrlf,分别表示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

  1. AutoCRLF
#提交时转换为LF,检出时转换为CRLF
git config --global core.autocrlf true
#提交时转换为LF,检出时不转换
git config --global core.autocrlf input   
#提交检出均不转换
git config --global core.autocrlf false

(Windows下默认值为true)

  1. 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的换行符,并且原本输出的两行之间多了一个空行”。

你可能感兴趣的:(区分换行符)