简 述: 纪录一个 Microsoft 自带的 notepad 所导致的问题,关于对 回车 和 换行 的解析。
测试小姐姐,报一个 bug 让康康先,说软件的日志在 Win10 x64 上,输出显示会多一行空格,而 Win7 x86 下则不会多此空行。我听后???同一个 .dll 还能跑出不同效果来????
[TOC]
本文初发于 “偕臧的小站“,同步转载于此。
背景
本想去敏后截图,但想了下还是直接写了例子演示,大概如图的 bug;💻为 win10 21H2
📎 win7 sp1
。
验证一番,确实可以复现,反而更加迷惑了。难道是对行尾结束标志,发生了历史演变???而恰前几天有写一篇如何修改 Visual Studio
行尾标志的文章 『Visual Studio实际仅增删数行,却显示文件所有行均发生变更』。
赶紧去查查 “回车”、“换行” wiki 历史的区别。再调试一下代码输出,看看是不是历史的演变原因?
回车换行CRLF
前辈们创建了文字,书写了一行文字;想要换行继续写,就创建了一个『换行的 标记』,自然就需要使用某个符号来表示此换行的标记。
打印机时代
此时还电脑还没有出现,打印机完成一个『换行的 操作』操作,需要执行 回车
+ 换行
两个动作。
- 『回车』:将游标移动到当前行的行首
- 『换行』:将游标移动到当前行的下一行
用个视频感受一下: How to Use a Typewriter
计算机时代
计算机出现了,照搬一些打印机的习惯,则两个动作被保留下来了。
回车符(CR)和换行符(LF)是文本文件用于标记换行的控制字符(control characters)或字节码(bytecode)。
- CR = Carriage Return,回车符号(
\r
,十六进制 ascii 码为0x0D
,十进制 ascii 码为13),用于将鼠标移动到行首,并不前进至下一行。 - LF = Line Feed,换行符号(
\n
, 十六进制 ascii 码为0x0A
,十进制 ascii 码为10)。
紧邻的 CR 和 LF(组成 CRLF,\r\n
,或十六进制 0x0D0A
)将鼠标移动到下一行行首。(Windows 操作系统默认的文本换行符为 CRLF;Linux 以及 macOS 系统默认使用 LF,早期的 mac os 系统使用 CR 换行。)
需要用一个字符来表示『换行的 标记』。至于历史原因和想法的争议,Windows 使用了两个字符表示,Linux 和 MacOS 均是一个字符表示,其现在的规则如下:
windows:
CR+LF
表示此标记。两个标记按照此顺序且同时出现才被认为是Linux / MacOS:
LF
表示此标记。早期的 MacOS :
CR
表示此标记。 MacOS 9 版本及之前。
感兴趣更详细,可参考 mozilla.org 和 notepad++ 的这两篇文档:
分析
有了上述知识储备,调试一下源码,发现打印每个文件路径时,会拼接字符串,每次都会添加 \r\n
字符串来表示换行。
写一个小例子验证下区别,分别拿到 win10 和 win 7 下去跑一下,使用 notepad++ 打开,两次效果都是一样的。
QFile file("D:/ExCRLF.txt");
file.open(QIODevice::WriteOnly | QIODevice::Text);
file.write("(abcdefg1234hijk)\r[FOXES]");
file.write("(abcdefg1234hijk)\n[FOXES]");
file.write("(abcdefg1234hijk)\r\n[FOXES]");
file.write("(abcdefg1234hijk)\n\r[FOXES]");
file.close();
在 Win10 21H2 中运行效果
结论: 在 win 系统中,一个"\n"
的 C++ 字符会被标记为一个 "CRLF"
存储到文本文件中;一个"\r"
的 C++ 字符会被标记为一个 "CR"
存储到文本文件中。而在 notepad++ 中,无论是 "LR"
、"CR"
、"CRLF"
都会被解析为换行操作后显示出来;但是早期的 win 下的 notepad 记事本不是,只能够解析 "CRLF"
;而这个 bug 一直到 2018.05 在 win10 中才被修复; 这是在微软维护 notepad 那个老哥写的 blog: Introducing extended line endings support in Notepad 。
最终就是展示到眼睛看到的效果。
"\r"
↔"CR"
被 notepad++ 解析为换行;早期 notepad 不做换行解析"\n"
↔"CRLF"
被 notepad++ 解析为换行;早期 notepad 不做换行解析"\r\n"
↔"CR CRLF"
被 notepad++ 解析为换行;早期 notepad 做换行解析
真相图,同一个 txt 在 win7 下的早期记事本中解析错误,而 notepad++ 解析正确。
解决
上面分析可知,修改此 bug 的最好方式就是不作为修改,谁让软件生成的日志 .txt,使用默认使用 windows 打开,且系统保持向上兼容到 win7、和 win xp。也算是一种妥协!
若是想要解决跨平台,解析来自不同的系统的文件,判定收取的文件的换行是哪一个?可参考 QString::split() and “\r”, “\n” and “\r\n” convention
系列地址
QtExamples 【ExCRLF】,测试例子和打包二进制见此文件夹,自行查看体验
欢迎 star
⭐ 和 fork
🍴 这个系列的 C++ / QT / DTK
学习,附学习由浅入深的目录,这里你可以学到如何亲自编写这类软件的经验,这是一系列完整的教程,并且永久免费!”