最近我在看一些文件,这些文件包含了一堆以字节为单位的大小,大小差异非常大,类似这样。
file 10361909248
percpu 315360
inactive_file 8666644480
active_file 1695264768
slab_reclaimable 194324760
slab 194324760
[...]
(这是来自于Linux的cgroup内存核算)。
我发现看着这些数字,很难对它们的绝对值或相对值有任何感觉,尤其是当我不想花很多时间去思考的时候。如果把这些数字人性化,变成 "9.7G"、"308.0K "和 "185.3M "这样的数字,我就更容易阅读了。为了使这些文件更具可读性,我写了一个Python过滤程序,用它们的人性化版本替换这些原始字节数。
我使用Python做这个过滤器的原因之一是,它是我对Unix文本处理的默认选择,它需要的不仅仅是sed ,或者是awk 。另一个原因是,我知道Python的re模块有一个特点,使这个过滤器非常容易,那就是re.sub()可以使用一个函数作为替换,而不是一个字符串。
使用替换函数意味着我可以写一个简单的函数,取一个保证全是小数的匹配对象,并把它变成一个人性化的数字(以字符串形式)。然后主要看的就是。
rxp = re.compile("\d+")
def process():
for line in sys.stdin:
l = line.strip()
l = rxp.sub(humanize_number, l)
print(l)
正则表达式替换完成了所有拆分行和事后重新组装的工作。我只需要把线送进去,之后再把它们倒出来。
(我这里的正则表达式有点低效;例如,我可以让它跳过所有的一、二、三位数的数字)。这也会使它无法匹配标识符中的数字,例如,如果一个文件有 "fred1 100000 "这样的行。对于我的目的来说,我现在不需要更精确,但是生产版本可能需要更小心)。
Python的正则表达式函数替换是一种方便而强大的方式,可以用一种低难度的方式进行某些非常通用的文本替换。对它的一个警告是,你可能不想在对性能敏感的情况下使用它,因为它确实需要一个Python函数调用,以及为每个替换提供各种其他东西。我上次看的时候,如果你能使用纯文本替换,运行速度要快得多。在这里,不仅情况对性能不敏感,而且没有办法摆脱运行Python代码的一种方式,因为我们不能只用文本替换来完成工作(至少如果我们想要两个人性化数字的幂,就像我一样)。
旁白:人性化的功能
我一开始写的是明显的基于蛮力的if ,然后意识到我可以通过更巧妙的方式得到更简单的代码。最终的结果是。
KB = 1024
MB = KB * 1024
GB = MB * 1024
TB = GB * 1024
seq = ((TB, 'T'), (GB, 'G'), (MB, 'M'), (KB, 'K'))
def humanize_number(mtch):
n = int(mtch.group())
for sz, ch in seq:
if n >= sz:
return '%.1f%s' % (n / sz, ch)
return str(n)
seq 元组需要从最大的单位到最小的单位排序,因为我们取的是输入等于或大于的第一个单位。