我正在使用sort
命令对 rockyou.txt 单词列表进行排序,这是我从这里下载的地点:
% sort rockyou.txt > rockyou_sorted.txt
但是,当我检查两个文件的文件大小时,它们不同,这 排序后的文件较小:
% du -shk rockyou_sorted.txt rockyou.txt
147520 rockyou_sorted.txt
148304 rockyou.txt
有趣的是,当我使用从以下位置下载的干净版本的 rockyou.txt 单词列表重复这些相同的步骤时这里,我得到相反的结果,即排序后的文件更大:
% sort rockyou_cleaned.txt > rockyou_cleaned_sorted.txt
% du -shk rockyou_cleaned_sorted.txt rockyou_cleaned.txt
114752 rockyou_cleaned_sorted.txt
102104 rockyou_cleaned.txt
我想知道这是为什么?有人可以帮我解释一下吗?难道我做错了什么?我认为排序后的文件和原始文件的大小应该相同,不是吗?
更新1,按照下面 Francesco Lucianò 的评论:使用sort
带有 -o 参数的命令
% sort rockyou.txt -o rockyou_sorted_sO.txt
% du -shk rockyou_sorted_sO.txt rockyou.txt
147996 /Users/Martin/Downloads/rockyou_sorted_sO.txt
148304 /Users/Martin/Downloads/rockyou.txt
排序后的文件仍然比原始文件小,但不像我使用sort
上面的命令版本时那么小。
所有文件中的行数都相同:
% wc -l rockyou_sorted_sO.txt rockyou_sorted.txt rockyou.txt
14344391 rockyou_sorted_sO.txt
14344391 rockyou_sorted.txt
14344391 rockyou.txt
43033173 total
更新2,根据下面 bey0nd 的评论:
set | grep LANG
根本不输出任何内容:
% set | grep LANG
%
% chardet rockyou*
zsh: command not found: chardet
% uchardet rockyou*
rockyou.txt: UTF-8
rockyou_sorted.txt: UTF-8
rockyou_sorted_duplicut.txt: UTF-8
rockyou_sorted_sO.txt: UTF-8
更新3,正如下面 Steeldriver 的评论:
% system_profiler SPSoftwareDataType
Software:
System Software Overview:
System Version: macOS 10.15.4 (19E287)
Kernel Version: Darwin 19.4.0
Boot Volume: Macintosh HD
Boot Mode: Normal
Computer Name: *REDACTED* MacBook Pro
User Name: *REDACTED*
Secure Virtual Memory: Enabled
System Integrity Protection: Enabled
Time since boot: 6 days 4:57
文件系统是APFS。
更新4,根据 roaima 的评论如下:
% ls -l rockyou*
-rw-r--r--@ 1 **REDACTED** staff 139921497 May 16 12:24 rockyou.txt
-rw-r--r-- 1 **REDACTED** staff 139921847 May 16 12:25 rockyou_sorted.txt
-rw-r--r-- 1 **REDACTED** staff 139919642 May 16 12:29 rockyou_sorted_duplicut.txt
-rw-r--r-- 1 **REDACTED** staff 139921847 May 16 13:19 rockyou_sorted_sO.txt
% stat -f .
.
更新5,根据艾萨克的评论如下:
% head -n3 rockyou.txt | od -An -tcx1
1 2 3 4 5 6 \n 1 2 3 4 5 \n 1 2 3
31 32 33 34 35 36 0a 31 32 33 34 35 0a 31 32 33
4 5 6 7 8 9 \n
34 35 36 37 38 39 0a
% LC_ALL=C sort rockyou.txt >rockyou_sorted_with_LC.txt
% du -shk rockyou_sorted_with_LC.txt rockyou.txt
147520 rockyou_sorted_with_LC.txt
140476 rockyou.txt
% wc -l rockyou_sorted_with_LC.txt rockyou.txt
14344391 rockyou_sorted_with_LC.txt
14344391 rockyou.txt
28688782 total
更新6,正如下面 fra-san 的评论:
% sort --version
2.3-Apple (101.40.1)
% locale
LANG=""
LC_COLLATE="en_US.UTF-8"
LC_CTYPE="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
答案1
有二这里发生的事情,而且它们的作用有些相反。
- 输入文件的编码不一致,并被转换
sort
为有效的 UTF-8。这使得文件大。这会影响ls -l
报告的大小。 - 文件系统存储会发生一些奇怪的事情,它会根据文件的创建方式来改变相同大小的文件使用的块的数量。这会影响
du -shk
- 报告的大小,并且主要使其较小(但可以选择任何一种方式)。
我可以对第一点给出比第二点更准确的解释,尽管简短的答案是这du
不是测量单个文件大小的正确工具,尤其是在 APFS 上。
接下来的两节将深入探讨这两个因素。
对于因素 (1),文件包含非 UTF-8 编码的行,UTF-8 是 macOS 上的默认区域设置编码。该sort
命令在输出时修改这些编码错误的行,使文件变得更大。我们将在下面进一步调查,但这只是简短的答案,如果足够了,您可以跳到下一部分。
如果我们在 C 语言环境中对提供的文件进行排序,然后再次在 en_US.UTF-8 语言环境中排序,则这两个文件具有不同的实际大小:
139921497 rockyou.txt
139921497 rockyou_c.txt
139921847 rockyou_sorted.txt
C 排序文件和 UTF-8 排序文件之间的第一个区别是
299c299
< �R3CKL3$$�
---
> R3CKL3$$
第一行在密码的开头和结尾处包含字节 0x93 和 0x94,它们不是 UTF-8 中的有效独立字节(它们只能显示为多字节字符的连续字节)。第二个包含 Unicode 代码点 U+0093 和 U+0094,分别编码为两个字节的 UTF-8,C2 93
以及C2 94
.
结果是原来的十字节行被写成十二字节行。在整个文件中,这些更改在排序后的文件中比原始文件增加了 350 个额外字节。
我相信这里发生的是:
- 原来的线路是
“R3CKL3$$”
(包括引号),编码在Windows-1252 代码页(cp1252)。该编码中的成对引号是 0x93 和 0x94。 - 它作为原始字节添加到这个集合中,没有考虑任何编码,就像许多其他条目一样。
- 在真正的 Latin-1 ISO 8859-1 中,字节范围 0x80-0x9f 未被占用,但在 ISO-8859-1 中(注意额外的连字符),它由C1控制字符。
- 所有 ISO-8859-1 都被编码为块“Latin-1 补充"在Unicode 中,代码点对应于原始字节值
C2 80
并由C2 BF
多字节编码C3 80
表示C3 BF
UTF-8。 - macOS 排序将单独的连续字节解释为尝试的 ISO-8859-1,并以这种方式在内部将它们转换为 Unicode 字符串以进行排序:字节 0x93 在排序的字符串中变为 U+0093。
- 这些翻译后的字符串以 UTF-8 格式输出,而不是文件中的原始行。
网站上的其他问题讨论了如何修复错误编码的 cp1252 文件事后,如果这是你需要的东西。
POSIX 指出如果行包含在语言环境中不形成有效字符的字节序列,则实用程序的行为未定义,因此这是标准严格允许的并且不是一致性错误。这至少仍然是出乎意料的,并且可以说是一个行为错误。我尝试过的其他实现不会以这种方式运行。
这个因素促使文件变得稍微更大当你对它进行排序时,并且确实更大 - 如果你从文件中读取,你将获得更多字节。
因素(2)总体上推动文件变得“更小”,但这有点幻觉。读取文件不一定会产生更多或更少的字节,只是因为du
它们的大小不同。
du -shk
一般来说,这不是检查文件大小的合适方法,因为
du 实用程序显示每个文件参数的文件系统块使用情况
这意味着它报告有关文件占用了多少物理空间的信息,而不是其逻辑大小。根据文件系统和相关文件的具体参数,块计数可能与您的预期有很大差异。那里是块计数很有用的情况,例如当您将文件压缩到完整设备上时,但通常情况下并不如此。
如今区块计数不再那么有用的原因之一是现代文件系统并不总是完全按照给定的方式写入数据:例如,他们可能会在将其存储到更大或更小范围之前默默地对其进行压缩,从而需要更少的块,或者在块内留下空白空间,以便使用更多的块来使将来的插入更容易。稀疏文件省略了零块,但重复数据删除的作用甚至不止于此。
就 APFS 而言,它支持多种算法的压缩、一些重复数据删除和增量编码、加密和高级元数据。其中一些或全部可能都在发挥作用,透明压缩最可能的变化文件何时写入,取决于应用程序实现和系统负载。
如果我们只cat
检查文件几次,我们就可以看到差异。如果我已经下载rockyou.txt
了curl -O
:
cat rockyou.txt > rockyou2.txt
创建一个具有相同字节数(139921497)但块数不同的文件(curl
创建的文件为 147504,创建的文件为 147460cat
)- 捕获新文件又给了我另一个块计数(147520),就像
cp
(150512)一样。 - 再次运行两条线给了我不同的结果比第一次尝试。
我不知道具体原因,也不确定是否有合理的方法来说明。我怀疑它有时比其他时候更努力地压缩数据。在所有情况下,文件的大小实际上相同,并且从任何版本读取都会返回相同的字节。我们只是没有从 APFS 或其他现代高性能文件系统上报告的块计数中获得太多有用的信息。如果您将文件压缩到设备上,尝试几次以获得最小版本可能会有所帮助,但否则就不值得考虑。
总的来说,我们有编码问题导致文件确实稍大一些, 抵消了稍微修改报告的文件系统行为块数那个较大的文件,使其在测试中更小。真实大小测量显示排序后一致增加 350 字节。根据您如何看待它,这可能是某种错误,也可能是使用通过给它一个坏文件来排序。