![从文本文件中提取数字](https://linux22.com/image/1011939/%E4%BB%8E%E6%96%87%E6%9C%AC%E6%96%87%E4%BB%B6%E4%B8%AD%E6%8F%90%E5%8F%96%E6%95%B0%E5%AD%97.png)
我有一些文本文件,我想从中提取某些数据。我想从中提取一些特定的数字。特别是,我想在文件中搜索第一次出现的string1
并获取其后的数字。也就是说,我想获取所有数字、点或减号,并在到达另一个字符时停止。然后我想将这些数字写入单独的文件。
最好我可以同时对多个字符串执行此操作(因此也查找string2
,在那里执行相同操作并以某种列出的格式写出结果,比如说{numbers1,numbers2}
。但最后一部分不太重要。
我该如何实现这个目标?
我没有包含具体数据,因为我希望我提出的问题有一个通用的解决方案。这样的工具在很多场合都很有用。(我试图从各种问题中拼凑出一个关于如何从特定字符串中提取数字的通用解决方案,但失败了。)
数据看起来就像
bla bla bla label1_5234_blablab_some_other_text_and_numbers_23343_blabla_more_text_and_numbers_maybe_label1_again_but_now_I_no_longer_care_about_what_comes_after blabla_label2_34343_this_is_some_other_number_want_to_be_able_to_extract_if_I_look_for_label2_instead_of_label1
label3 = -0.34343
and_more_text_and_so_on_and_so_forth
那么要寻找的模式将是label1_
,label2_
或label3 =
。(当然,无论 label1 的具体形式如何,它都应该有效。但由于这显然不是完全清楚,让我再举一个例子。
height_2.3 blabla_bla_length_3.4
,应该给出2.3
,3.4
或 ,{2.3,3.4}
具体取决于我们要求的是高度、长度还是两者。)
如果给定一个要寻找的模式,那么输出将是label1_
5234
或者寻找label3 =
-0.34343
此外,如果它可以同时搜索两个事物并将它们分组,那就太好了。例如,给出上述两种模式,输出
{5234,-0.34343}
最后,如果输入多个文件,它可以将结果分组为多个文件,那就太好了:
{out1a,out1b}
{out2a,out2b}
答案1
如果您希望将单个文件的所有结果组合在一起,那么最简单的方法可能是将每个文件的全部内容放入内存并将其作为一个块进行处理。您可以通过perl
取消设置行分隔符来实现这一点 - 在 perl 单行程序中执行此操作的传统方法是-0777
。
label[123]_
接下来,您需要一个正则表达式来匹配以或开头的十进制数字、小数点分隔符等序列label[123] =
把它放在一起:
perl -0777nE 'say "{", (join ",", /label[123](?:_| = )\K[0-9.+-]+/g), "}"' file1 file2 [...]
注:我还没有尝试解决 maybe_label1_again_but_now_I_no_longer_care_about_what_comes_after
答案2
sed
解决方案
保留$p
标签正则表达式,例如p='label[13](_\| = )'
:
sed ':a;N;$!ba;s/\n/ /g;s/'"$p"'[-.0-9]\+/&\n/g' | \
sed '/.*'"$p"'[-.0-9]\+/!d;s/.*'"$p"'\([-.0-9]\+\)/\2/' | \
sed ':a;N;$!ba;s/\n/,/g;s/.*/{&}/'
第一个命令删除换行符并在每次匹配后添加一个新的换行符,第二个命令删除没有匹配的行并提取数字,第三个命令使它们用逗号分隔并将它们括在花括号中。
$p
必须包含一个有效的正则表达式和一个组(或者您需要调整第三个替换表达式的 RHS 部分),例如:
p='label1\(_\)'
p='label3\( = \)'
p='label[13]\(_\| = \)'
p='\(label1_\|label3 = \)'
p='\(height\|length\)_'
组中的多个不同字符串应该用 分隔\|
。
例子
$ <input cat
bla bla bla label1_5234_blablab_some_other_text_and_numbers_23343_blabla_more_text_and_numbers_maybe_label1_again_but_now_I_no_longer_care_about_what_comes_after blabla_label2_34343_this_is_some_other_number_want_to_be_able_to_extract_if_I_look_for_label2_instead_of_label1
label3 = -0.34343
and_more_text_and_so_on_and_so_forth
$ p='label1\(_\)'
$ <input sed ':a;N;$!ba;s/\n/ /g;s/'"$p"'[-.0-9]\+/&\n/g' | sed '/.*'"$p"'[-.0-9]\+/!d;s/.*'"$p"'\([-.0-9]\+\)/\2/' | sed ':a;N;$!ba;s/\n/,/g;s/.*/{&}/'
5234
$ p='label3\( = \)'
$ <input sed ':a;N;$!ba;s/\n/ /g;s/'"$p"'[-.0-9]\+/&\n/g' | sed '/.*'"$p"'[-.0-9]\+/!d;s/.*'"$p"'\([-.0-9]\+\)/\2/' | sed ':a;N;$!ba;s/\n/,/g;s/.*/{&}/'
-0.34343
$ p='label[13]\(_\| = \)'
$ <input sed ':a;N;$!ba;s/\n/ /g;s/'"$p"'[-.0-9]\+/&\n/g' | sed '/.*'"$p"'[-.0-9]\+/!d;s/.*'"$p"'\([-.0-9]\+\)/\2/' | sed ':a;N;$!ba;s/\n/,/g;s/.*/{&}/'
{5234,-0.34343}
$ echo "height_2.3 blabla_bla_length_3.4" >>input
$ p='\(height\)_'
$ <input2 sed ':a;N;$!ba;s/\n/ /g;s/'"$p"'[-.0-9]\+/&\n/g' | sed '/.*'"$p"'[-.0-9]\+/!d;s/.*'"$p"'\([-.0-9]\+\)/\2/' | sed ':a;N;$!ba;s/\n/,/g;s/.*/{&}/'
2.3
$ p='\(height\|length\)_'
$ <input2 sed ':a;N;$!ba;s/\n/ /g;s/'"$p"'[-.0-9]\+/&\n/g' | sed '/.*'"$p"'[-.0-9]\+/!d;s/.*'"$p"'\([-.0-9]\+\)/\2/' | sed ':a;N;$!ba;s/\n/,/g;s/.*/{&}/'
{2.3,3.4}
答案3
对于单个文件
grep -oP "(?<=label1_)[0-9.+-]+[^_ ]+" ./file | head -n 1 >> ./tmpfile
grep -oP "(?<=label3 = )[0-9.+-]+[^_ ]+" ./file | head -n 1 >> ./tmpfile
paste -sd, ./tmpfile | awk '{ print "{"$0"}" }' >> ./newfile
rm ./tmpfile
对于多个文件在文件夹中。cd
到该文件夹并运行:
for file in *; do
if [ "$file" == "newfile" ] ; then continue; fi
grep -oP "(?<=label1_)[0-9.+-]+[^_ ]+" $file | head -n 1 >> ./tmpfile
grep -oP "(?<=label3 = )[0-9.+-]+[^_ ]+" $file | head -n 1 >> ./tmpfile
paste -sd, ./tmpfile | awk '{ print "{"$0"}" }' >> ./newfile
rm ./tmpfile
done