我有一堆 txt 文件,我想以小写形式输出它们,仅按字母顺序输出,每行一个单词,我可以tr
在管道中使用多个命令来完成此操作,如下所示:
tr -d '[:punct:]' <doyle_sherlock_holmes.txt | tr '[:upper:]' '[:lower:]' | tr ' ' '\n'
是否可以在一次扫描中完成此操作?我可以编写一个 C 程序来执行此操作,但我觉得有一种方法可以使用tr
、sed
或awk
来执行此操作perl
。
答案1
您可以组合多个翻译(涉及重叠的区域设置相关集的复杂情况除外),但不能将删除与翻译组合起来。
<doyle_sherlock_holmes.txt tr -d '[:punct:]' | tr '[:upper:] ' '[:lower:]\n'
两次调用tr
可能比一次调用更复杂的工具更快,但这很大程度上取决于输入大小、不同字符的比例、工具的实现tr
和竞争工具、操作系统、数量核心数量等
答案2
是的。您可以tr
在 ASCII 语言环境中执行此操作(无论如何,对于 GNU 来说tr
,这是它唯一的权限)。您可以使用 POSIX 类,也可以通过八进制数引用每个字符的字节值。您也可以跨范围分割它们的转换。
LC_ALL=C tr '[:upper:]\0-\101\133-140\173-\377' '[:lower:][\n*]' <input
上面的命令会将所有大写字符转换为小写,完全忽略小写字符,并将所有其他字符转换为换行符。当然,最后你会得到大量的空行。在这种情况下,挤压重复开关可能很有用,但如果将其与to转换tr
-s
一起使用,那么最终也会挤压大写字符。这样它仍然需要第二个过滤器,例如......[:upper:]
[:lower:]
LC... tr ... | tr -s \\n
...或者...
LC... tr ... | grep .
...所以它最终比这样做要方便得多...
LC_ALL=C tr -sc '[:alpha:]' \\n <input | tr '[:upper:]' '[:lower:]'
...它将-c
按顺序将字母字符的补码压缩成单个换行符,然后在管道的另一侧进行从上到下的转换。
这并不是说这种性质的范围没有用。像这样的东西:
tr '\0-\377' '[1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][0*]' </dev/random
...可以非常方便,因为它将输入字节转换为其值的扩频范围内的所有数字。不要浪费,不要想要,你知道。
另一种进行转换的方法可能涉及dd
.
tr '\0-\377' '[A*64][B*64][C*64][D*64]' </dev/urandom |
dd bs=32 cbs=8 conv=unblock,lcase count=1
dadbbdbd
ddaaddab
ddbadbaa
bdbdcadd
因为dd
可以同时进行unblock
和lcase
转换,所以甚至可以将大部分工作交给它。但是,只有当您能够准确预测每个字的字节数时,这才真正有用 - 或者至少可以预先用空格填充每个字以达到可预测的字节数,因为unblock
会占用每个块末尾的尾随空格。
答案3
以下是一些方法:
GNU
grep
和tr
:查找所有单词并将其变为小写grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
GNU grep 和 perl:如上所述,但 perl 处理小写转换
grep -Po '\w+' file | perl -lne 'print lc()'
perl:找到所有字母字符并以小写形式打印它们(感谢@steeldriver):
perl -lne 'print lc for /[a-z]+/ig' file
sed:删除所有非字母或空格的字符,用小写版本替换所有字母字符,并用换行符替换所有空格。请注意,这假设所有空白都是空格,没有制表符。
sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file