简 述: 纪录一个 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 学习,附学习由浅入深的目录,这里你可以学到如何亲自编写这类软件的经验,这是一系列完整的教程,并且永久免费!”