我有一组文本文件需要复制,但希望每个文件的词长度大致相同,同时保持完整的句子。将{.?!}
内的任何标点符号视为句子边界是可以接受的。我可以用Python来做这件事,但我正在尝试学习bash,所以欢迎提供建议。我考虑的方法是将目标词长度超出几个词,然后将结果修剪到最后的句子边界。
我熟悉head
和wc
,但我想不出如何将两者结合使用。head
的man
文件没有说明如何使用词数,而wc
的man
文件没有说明如何分割文件。
背景:我正在进行一个使用机器学习的文本分类任务(记录在案,我使用的是weka
)。我想确保文本长度(在我的数据中变化很大)不会过多地影响结果。为此,我试图在进行特征提取之前规范化我的文本长度。
回答:
让我们考虑这个测试文件:
$ cat fileDo I exist? I program. Therefore, I am!
假设我们想将这个文件截断为完整的句子,每个句子不超过20个字符:
$ awk -v n=20 -v RS='[.?!]' '{if (length(s $0 RT)>n) exit; else s=s $0 RT;} END{print s;}' fileDo I exist?
如果我们想要不超过30个字符:
$ awk -v n=30 -v RS='[.?!]' '{if (length(s $0 RT)>n) exit; else s=s $0 RT;} END{print s;}' fileDo I exist? I program.
工作原理
-
-v n=20
这将awk变量
n
设置为我们想要的最大长度(不包括文件的最后一个换行符)。 -
-v RS='[.?!]'
这将awk的记录分隔符
RS
设置为您提到的三个字符中的任何一个。 -
if (length(s $0 RT)>n) exit; else s=s $0 RT
对于文件中的每个记录(一个记录是一个句子),我们测试将它添加到
s
是否会使输出过长。如果输出过长,我们就退出。如果没有,我们就将它添加到s
中。在awk中,
$0
代表完整的记录,RT
是awk在记录末尾找到的记录分隔符。 -
END{print s;}
在退出之前,这会打印字符串
s
。
替代方案1:基于词数截断
假设我们想基于词数进行截断。例如,如果我们想要6个词:
$ awk -v n=6 -v RS='[[:space:]]+' 'NR>n{exit;} {printf "%s%s",$0,RT;} END{print"";}' fileDo I exist? I program. Therefore,
不同之处在于我们现在使用空白作为记录分隔符。这样,每个记录就是一个词,我们会一直打印词直到达到限制。
替代方案2:完整句子但限制词数
$ awk -v n=6 -v RS='[.?!]' '{c+=NF; if (c>n) exit; else s=s $0 RT;} END{print s;}' fileDo I exist? I program.
Mac OSX
上述设置记录分隔符RS
为正则表达式。这可能需要GNU awk(gawk)。OSX的awk
手册页没有说明是否支持此功能。然而,@***报告说,在从macports安装gawk
后,上述代码可以在OSX上成功运行。