我有一个包含以下帐户的文本文件:
输入样本
Paid 100 15/02/2022 3000
recd 50 15/02/2022 nelur trip 3050
PAID 80 25/03/2022 Adjusted towards trip 3130
14 PAID 50 26/03/2022 Given to Nate Cash (padma ac) 3180
我想忽略开头的数字或将它们连接到第一个单词,然后第一个单词用破折号/连字符连接它们,有一个数字放在 ;然后日期放一个;那么话就这么放了;然后将它们加入一个数字,所以输入一个;删除除新行之外的空白
也可以是一个脚本,perl/php、bash、awk、sed 任何我可以在我的 Linux 笔记本电脑上轻松安装和运行的东西。
输出样本
Paid;100;15/02/2022;3000
recd;50;15/02/2022;nelur-trip;3050
PAID;80;25/03/2022;Adjusted-towards-trip;3130
14-PAID;50;26/03/2022;Given-to-Nate-Cash-(padma-ac);3180
我想将其导入电子表格,所以任何其他方式也可以。大约有300行。
我之前尝试过什么?使用电子表格清理从图像和图像到文本转换器获得的数据。
还尝试使用记事本++和列选择。我很快就尝试学习AWK。我了解 Java,但这需要很长时间。得到答案后我学到了一些东西:
- 如果有行号,则保留行号有助于了解使用 awk 时缺少哪些行。 AS 给出的输出没有任何跳过行的警告。可能是绕过它的设置,不确定
- 始终要求提供 csv、电子表格或 PDF 格式的帐户,并且不接受图像!
我通过在我们帐户上的朋友那里获得的图像上运行图像到文本来获得输入
答案1
perl -pe 's/(?:^[0-9]+|[^0-9])\K (?=[^0-9])/-/g;
s/ /;/g
' -- file
-p
逐行读取输入并在处理后打印每一行;- 第一个替换使用环视断言:它说如果空格前面是字符串开头的数字,或者不是数字但后面不是数字,则用破折号替换空格;
- 第二次替换用分号替换剩余的空格。
如果最后一行后面的两个空格50
不是拼写错误,则需要将第二个替换更改为,以s/ +/;/g
将所有相邻空格替换为单个分号。
答案2
既然你已经有了答案...
使用 GNU awk 作为第三个参数match()
:
awk -v OFS=';' '
match($0,/\S\s+([0-9]+)\s+(([0-9]{2}\/){2}[0-9]{4})(\s+(.*\S))? ([0-9]+)$/,a) {
$0 = substr($0,1,RSTART) OFS a[1] OFS a[2] OFS a[5] OFS a[6]
gsub(/\s+/,"-")
print
}
' file
Paid;100;15/02/2022;;3000
recd;50;15/02/2022;nelur-trip;3050
PAID;80;25/03/2022;Adjusted-towards-trip;3130
14-PAID;50;26/03/2022;Given-to-Nate-Cash-(padma-ac);3180
上面假设您的输入格式是:
<anything1> <integer1> <date>[ <anything2>] <integer2>
(.*\S) ([0-9]+) ^ ( (.*\S))? ([0-9]+)
|
(([0-9]{2}\/){2}[0-9]{4})
其中字段之间可以有任何非换行符空格,anything
内部字段<...>
始终以非空格结尾,但可以包含空格或任何其他字符,不能在 中包含前面带有整数的日期字符串anything1
,[ <anything2>]
是可选的,并且您想要假设输出为:
<anything1>;<integer1>;<date>;<anything2>;<integer2>
这意味着Paid;100;15/02/2022;3000
在预期的输出中实际上应该是Paid;100;15/02/2022;;3000
这样,每个输出行都具有与OP所说的他们想要做的相同数量的用于导入电子表格的字段。
\S
我在正则表达式的开头使用单个,然后使用它substr()
来获取头anything
字符串,而不是像我最初预期的那样使用它(.*\S)
来填充数组元素,并且将使用问题中发布的示例输入,因为我意识到这会给定输入失败,例如:
Paid 100 15/02/2022 foo 100 15/02/2022 3000
其中该行中有 2 个日期,并且第二个日期周围的字符串与正则表达式的其余部分匹配,例如,给定此修改后的输入,并在底部有问题的行:
$ cat file
Paid 100 15/02/2022 3000
recd 50 15/02/2022 nelur trip 3050
PAID 80 25/03/2022 Adjusted towards trip 3130
14 PAID 50 26/03/2022 Given to Nate Cash (padma ac) 3180
Paid 100 15/02/2022 foo 100 15/02/2022 3000
(.*\S)
如果我们在正则表达式的开头使用,请注意不合需要的最后一行输出:
$ awk -v OFS=';' '
match($0,/(.*\S)\s+([0-9]+)\s+(([0-9]{2}\/){2}[0-9]{4})(\s+(.*\S))? ([0-9]+)$/,a) {
$0 = a[1] OFS a[2] OFS a[3] OFS a[6] OFS a[7]
gsub(/\s+/,"-")
print
}
' file
Paid;100;15/02/2022;;3000
recd;50;15/02/2022;nelur-trip;3050
PAID;80;25/03/2022;Adjusted-towards-trip;3130
14-PAID;50;26/03/2022;Given-to-Nate-Cash-(padma-ac);3180
Paid-100-15/02/2022-foo;100;15/02/2022;;3000
与使用建议脚本的正确输出:
$ awk -v OFS=';' '
match($0,/\S\s+([0-9]+)\s+(([0-9]{2}\/){2}[0-9]{4})(\s+(.*\S))? ([0-9]+)$/,a) {
$0 = substr($0,1,RSTART) OFS a[1] OFS a[2] OFS a[5] OFS a[6]
gsub(/\s+/,"-")
print
}
' file
Paid;100;15/02/2022;;3000
recd;50;15/02/2022;nelur-trip;3050
PAID;80;25/03/2022;Adjusted-towards-trip;3130
14-PAID;50;26/03/2022;Given-to-Nate-Cash-(padma-ac);3180
Paid;100;15/02/2022;foo-100-15/02/2022;3000
根据评论中的反馈进行编辑 - 记录与正则表达式不匹配的任何行:
awk -v OFS=';' '
match($0,/\S\s+([0-9]+)\s+(([0-9]{2}\/){2}[0-9]{4})(\s+(.*\S))? ([0-9]+)$/,a) {
$0 = substr($0,1,RSTART) OFS a[1] OFS a[2] OFS a[5] OFS a[6]
gsub(/\s+/,"-")
print
next
}
{ print > "/dev/stderr" }
' file 2>unmatched.log
答案3
sed -E 's,([^0-9 ]) +([^0-9 ]),\1-\2,g
s,^([0-9]+) +,\1-,g
s, +,;,g
'
答案4
使用乐(以前称为 Perl_6)
首先看看是否可以获取所有要解析的行:
~$ raku -ne '.match(/ ^ \d* \s? <.alpha>+ \s \d+ \s [ \d**2 \/ \d**2 \/ \d**4 ] \s [[<.graph>+]+ % " " \s]? \d+ $ /).say;' file
以上是 Raku 中的一种方法,Raku 是 Perl 编程语言家族中支持 Unicode 的成员。因为您是从一个复杂的正则表达式问题开始的,所以最好看到所有行从头到尾都正确解析/^ … $/
。 (您可能需要raku -pe 's:g/\s+/ /;'
首先运行诸如标准化空白之类的操作)。上面的代码将返回Nil
未正确解析的行,这是OP的结果空白标准化数据:
输入示例:
Paid 100 15/02/2022 3000
recd 50 15/02/2022 nelur trip 3050
PAID 80 25/03/2022 Adjusted towards trip 3130
14 PAID 50 26/03/2022 Given to Nate Cash (padma ac) 3180
解析行:
「Paid 100 15/02/2022 3000」
「recd 50 15/02/2022 nelur trip 3050」
「PAID 80 25/03/2022 Adjusted towards trip 3130」
「14 PAID 50 26/03/2022 Given to Nate Cash (padma ac) 3180」
现在您看到所有行都从头到尾进行了解析,您可以捕获匹配的元素。用括号表示捕获,注意 Raku 捕获从 开始$0
:
~$ raku -ne '.match(/ ^ (\d* \s? <.alpha>+) \s (\d+) \s ( \d**2 \/ \d**2 \/ \d**4 ) \s [(<.graph>+)+ % " " \s]? (\d+) $ /).say;' file
「Paid 100 15/02/2022 3000」
0 => 「Paid」
1 => 「100」
2 => 「15/02/2022」
4 => 「3000」
「recd 50 15/02/2022 nelur trip 3050」
0 => 「recd」
1 => 「50」
2 => 「15/02/2022」
3 => 「nelur」
3 => 「trip」
4 => 「3050」
「PAID 80 25/03/2022 Adjusted towards trip 3130」
0 => 「PAID」
1 => 「80」
2 => 「25/03/2022」
3 => 「Adjusted」
3 => 「towards」
3 => 「trip」
4 => 「3130」
「14 PAID 50 26/03/2022 Given to Nate Cash (padma ac) 3180」
0 => 「14 PAID」
1 => 「50」
2 => 「26/03/2022」
3 => 「Given」
3 => 「to」
3 => 「Nate」
3 => 「Cash」
3 => 「(padma」
3 => 「ac)」
4 => 「3180」
最终答案——使用 Raku.subst( /…/, {…} )
或 Raku 的s///
替换命令/惯用语。注意:这两个替换习惯用法都在替换中将“可调用”代码放在大括号内,从而大大增加了每个命令的功能。因此,您可以.trans
将剩余的空白字符添加到-
连字符中,然后添加到分号join
中;
:
~$ raku -ne '.subst(/ ^ (\d* \s? <.alpha>+) \s (\d+) \s ( \d**2 \/ \d**2 \/ \d**4 ) \s [(<.graph>+)+ % " " \s]? (\d+) $/, \
{join ";", $0.trans(" " => "-"), $1, $2, $3.trans(" " => "-") // "", $4}).put;' file
#OR:
~$ raku -pe 's/ ^ (\d* \s? <.alpha>+) \s (\d+) \s ( \d**2 \/ \d**2 \/ \d**4 ) \s [(<.graph>+)+ % " " \s]? (\d+) $ \
/{join ";", $0.trans(" " => "-"), $1, $2, $3.trans(" " => "-") // "", $4}/;' file
示例输出(来自上面任一解决方案):
Paid;100;15/02/2022;;3000
recd;50;15/02/2022;nelur-trip;3050
PAID;80;25/03/2022;Adjusted-towards-trip;3130
14-PAID;50;26/03/2022;Given-to-Nate-Cash-(padma-ac);3180
奖励积分:可以正常化情况下使用类似{$0.uc}
将第一列全部转换为PAID
,RECD
等。
简化假设:您可以简化正则表达式以\d
用于数字和\D
非数字,但您可能无法解析具有混合非数字/数字“单词”的行,例如英国邮政编码或航空公司确认代码(尤其是在第 4 列中)。
https://docs.raku.org/routine/subst#Callable
https://docs.raku.org/language/regexes
https://raku.org