CR、LF、CR/LF
不同系统对“换行”的表示,详情可以看这里。
LF(Line Feed): 换行,ascii码是10,对应字符’\n’,*Nix系使用此字符做为换行的标志(包括OSX及其之后的版本)。
CR(Carriage Return): 回车,ascii码是13,对应字符是’\r’,部分系统,类似Apple II那个经典的系统(wiki上有具体的解释)。
现在某度或者google出来的部分权重略大的搜索结果还是说CR是macOS的换行符(可能是一个抄一个,也可能写下的时间较早),注意看上面wiki百科给出的解释,CR部分的是解释是:
Commodore 8-bit machines, Acorn BBC, ZX Spectrum, TRS-80, Apple II family, Oberon), Mac OS up to version 9, MIT Lisp Machine and OS-9.
这里的确提到了macOS,但是注意后面的up to version 9,也就是Mac OS 9之前的系统版本的确是使用CR做为换行符,但是之后的系统版本都是与*Nix系保持一致,都使用LF做为换行符。
CR/LF,同时使用\n\r来表示换行,现在只有Windows系统(大部分问题都出在这里,这里不说网络协议中用CRLF做为分隔的情况)。
目前的情况就是大部分民众常用的系统几乎只有两种情况使用\n做为换行符,使用\n\r做为换行标识。
起因
一个类似这样的场景,一堆android工程需要生成,各个工程都有自己的配置文件—不同工程的key或者id(或者其他各工程间独立且不同的配置),这些都需要在工程生成的时候动态的把配置文件里面提前约定好的字符串替换为其对应的key或者id(类似%needReplaceIdentifier%这样的文本字符串),对应的key或者id在相对固定的路径中存放,生成配置文件时候,把这个固定路径里面的字符串读取出来替换到对应的约定字符串中去。
这个看起来并没什么,其实如果在一个同一个系统中操作也真没什么,问题就出现在跨系统的操作上,有二个机器,一个Windows一个Mac,同时check了一份工程的代码(svn),准备在Mac端生成对应的配置文件,但是某些情况下就直接在Windows端编辑对应的配置文件提交至svn(svn不会像git那样自动转换line ending),之后在Mac直接update下对应的配置文件,开始生成,类似这样:
文件IDConfig.cfg内容:
1101 firstLine
1201 secondLine
…
被替换的文件BeReplacedFile内容:
…
… “%needReplaceID%” …
… “%needReplaceKey%”…
…
之后没有转换过的Windows换行直接被读取出来,字符串切分,替换,完成之后就变成了非预期的样子,出现了一个非预期的换行,如果不用类似vim这种编辑器提前看一下(vim下直接会发现Windows编辑过的文件后面多了一个^M的字样,也就是\r),不容易发现。
替换之后文件:
…
… “1101” …
… “firstLine
“…
…
类似如下shell脚本:
1
2
3
4
5
6
7
8
9
10
11
declare -i count
count=0
cat ~/Desktop/test.txt | while read line
do
tmp=$(echo ${line} | awk '{print $2}')
count=$count+1
echo "needReplaceIdentifier${count}"
sed -i -e "s/needReplaceIdentifier${count}/${tmp}/" ~/Desktop/test.dst
done执行上文shell处理之后就变成标准的macOS换行的格式,替换完成后这时候某些配置多了一个换行就已经开始报错了。
解决
扯了这么多,其实是非常简单的一个问题,解决方式有N种:
双系统开发,只在对应的系统进行提交(和没说一样)
使用dos2unix进行转换,直接brew安装一个dos2unix,还支持指转换,还可以unix2dos。
dos2unix file_to_convert
unix2dos file_to_convert
打开vim,你会看到这种文件有\r的地方都有一个^M标记,直接命令模式:
:%s/\r//g
其他任何可用的替换命令/操作。
End