Regex 壳牌正则表达式:提取价格

Regex 壳牌正则表达式:提取价格,regex,shell,awk,sed,grep,Regex,Shell,Awk,Sed,Grep,给出下面的价格列表,我试图找出如何规范化/仅提取数字 INPUT DESIRED_OUTPUT CA$1399.00 1399.00 &#36;1399.11 1399.11 $1,399.22< 1399.22 Z$1 399.33 1399.33 $1399.44# 1399.44 C$ 1399.55 1399.55 1,399.66 1399.66 1399.77 1399.77 ,

给出下面的价格列表,我试图找出如何规范化/仅提取数字

INPUT          DESIRED_OUTPUT

CA$1399.00     1399.00
&#36;1399.11   1399.11
$1,399.22<     1399.22
Z$1 399.33     1399.33
$1399.44#      1399.44
C$ 1399.55     1399.55
1,399.66       1399.66
1399.77        1399.77
,1399.88       1399.88
25 1399.88     1399.88
399.99          399.99
88.88 99.99      99.99 (if >2 matches on one line, only the last one matters)
.1399.88         DO NOT MATCH (not a price; too many ".")
666.000          DO NOT MATCH (not a price: too many 0's)

编辑:是的,我的输入格式可能会有点奇怪,因为它是拼接刮页的结果

您可以使用以下shell脚本:

#/bin/sh
grep -v '\.\d\+\.' | # get rid of lines with multiple dots within the same number
grep -v '\.\d\d\d\+' | # get rid of lines with more than 2 digits after .
sed -e 's/\(.*\.[0-9][0-9]\).*$/\1/' | # remove anything after last .NN
sed -e 's/^.* \([0-9][0-9][0-9][0-9]\)\./\1./' | # "* NNNN." => "NNNN."
sed -e 's/^.* \([0-9][0-9]\)\./\1./' | # "* NN." => "NN."
sed -e 's/^.* \([0-9]\)\./\1./' | # "* N." => "N."
sed -e 's/^\(.*\)[ ,]\(\([0-9]\)\{3,\}\)\./\1\2./g' | # "*,NNN." or "* NNN." => "*NNN."
sed -e 's/^\(.*\)[ ,]\(\([0-9]\)\{6,\}\)\./\1\2./g' | # "*,NNNNNN." or "* NNNNNN." => "*NNNNNN."
sed -e 's/^\(.*\)[ ,]\(\([0-9]\)\{9,\}\)\./\1\2./g' | # "*,NNNNNNNNN." or "* NNNNNNNNN." => "*NNNNNNNNN."
grep -o '\d\+\.\d\d' # print only the price
如果数字以空格或
分隔,以3位为一组,此解决方案最多可在
之前使用9位数字。如果您需要提取更高的价格,只需添加更多行,将正则表达式中的数字增加3即可

将其放入名为
提取价格
的文件中,使其可执行(
chmod+x提取价格
)并运行它:
/extract价格

使用以下输入在OS X上进行测试:

CA$1399.00
&#36;1399.11
$1,399.22<
Z$1 399.33
Z$12 777 666.34   # <-- additonal monster price
$1399.44#
C$ 1399.55
1,399.66
1399.77
,1399.88
25 1399.88
399.99
88.88 99.99
.1399.88
666.000

一种带有awk的解决方案,它将所有非数字或小数点的字符拆分,并打印与价格匹配的最后一个字段。前导的sed脚本处理异常情况#3,其中我们有一个空格而不是一个逗号来标记数千个点

sed -e 's/  / x /g; :a; s/\(\$[1-9][0-9]*\) /\1/; ta' | awk -F '[^0-9.]' -v p='[0-9]+\\.[0-9][0-9]' '$0 ~ p { gsub(/,/, ""); for (i=NF; i>0; i--) if ($i ~ "^" p "$") { print $i; next } }'
注:

1) sed脚本使用测试进行迭代;因此,它可以处理数百万、数十亿等业务。
2) sedsed脚本还处理多空间条件,使得$1[][]1000.00最终不会变成$11000.00。
3) 逗号被简单地去除/忽略。。。如果存在数字逗号分隔问题,可以通过在awk脚本中去掉gsub并在前面的sed脚本中修复过滤器来解决该问题

下面是一个更复杂的版本,它基于注释3中的思想,仅当空格或逗号位于千位分隔符时,才将逗号和空格作为数字的一部分

sed -e ':a; s/\(\$[1-9][0-9]*\) \([0-9][0-9][0-9][ .]\)/\1\2/; ta; :b; s/\([1-9][0-9]*\),\([0-9][0-9][0-9][,.]\)/\1\2/; tb;' | awk -F '[^0-9.]' -v p='[0-9]+\\.[0-9][0-9]' '$0 ~ p { for (i=NF; i>0; i--) if ($i ~ "^" p "$") { print $i; next } }'
如果每一行的成功率都很高,那么去掉“p”将有助于提高脚本的效率

sed -e ':a; s/\(\$[1-9][0-9]*\) \([0-9][0-9][0-9][ .]\)/\1\2/; ta; :b; s/\([1-9][0-9]*\),\([0-9][0-9][0-9][,.]\)/\1\2/; tb;' | awk -F '[^0-9.]' '{ for (i=NF; i>0; i--) if ($i ~ /^[0-9]+\.[0-9][0-9]$/) { print $i; next } }'
最后,为了安全起见,我们可以检查sed过滤器,以确保在进行替换之前有一个有效的空格或逗号分隔的数字

sed -e ':a; /\$[1-9][0-9]\?[0-9]\?\( [0-9][0-9][0-9]\)\+\.[0-9][0-9]/ s/\(\$[1-9][0-9]*\) \([0-9][0-9][0-9][ .]\)/\1\2/; ta; :b; /[1-9][0-9]\?[0-9]\?\(,[0-9][0-9][0-9]\)\+\.[0-9][0-9]/ s/\([1-9][0-9]*\),\([0-9][0-9][0-9][,.]\)/\1\2/; tb;' | awk -F '[^0-9.]' '{ for (i=NF; i>0; i--) if ($i ~ /^[0-9]+\.[0-9][0-9]$/) { print $i; next } }'
这可能适用于您(GNU-sed):


这适用于提供的数据,但某些规范有点粗略。

请参阅推荐
grep-o
。您的输入格式非常糟糕-
Z$1 399.33
应该与空格前的数字匹配,但是
25 1399.88
不应该与空格前的数字匹配?为什么——根据什么规则可以对这种区别进行编码?程序和数据的其余部分是什么样的?您可以执行一次清理运行还是多次运行?位置
4
8
12
等处的空格/逗号(RTL)。可以接受,因此
1399.88
133399.88
113333399.88
都可以。匹配
Z$1399.33
应该没什么大不了的;当$出现时,数字就结束了(再次读取RTL)。棘手的情况是:
.1399.98
不能匹配,而
1399.98
对应于
1399.98
。那么
.1399.98
呢。空间是否重要,以便匹配,且价格为
399.98
?我认为需要通过提取
.1
作为标记来解决这个问题,其中尾随空格终止分数。下一个数字标记是
399.98
:很好的价格。您使用哪些严格可移植的Unix工具来刮取这些数据?这一点很好。人类的常识告诉我们,
.1399.98
独立存在就是
1399.98
,但在其他情况下,这样一个允许的规则可能会导致误报,这就是为什么我首先拒绝
.1399.88
。我不知道如何解决这个问题,但无论如何,
.1399.98
是不太可能的@卡兹卷曲。我使用curl.而不是删除尾部的
#
True。这一部分并不难。欢迎来到堆栈溢出,顺便说一句:)好的,有解决方案了!哈哈,哈奇,但它是有效的。谢谢!终于想得到一些名声。。。为什么
echo“$1399.22”| extract_prices
会生成不正确的
399.22
,而
echo'$1399.22'| extract_prices
会生成正确的
1399.22
sed -e ':a; s/\(\$[1-9][0-9]*\) \([0-9][0-9][0-9][ .]\)/\1\2/; ta; :b; s/\([1-9][0-9]*\),\([0-9][0-9][0-9][,.]\)/\1\2/; tb;' | awk -F '[^0-9.]' '{ for (i=NF; i>0; i--) if ($i ~ /^[0-9]+\.[0-9][0-9]$/) { print $i; next } }'
sed -e ':a; /\$[1-9][0-9]\?[0-9]\?\( [0-9][0-9][0-9]\)\+\.[0-9][0-9]/ s/\(\$[1-9][0-9]*\) \([0-9][0-9][0-9][ .]\)/\1\2/; ta; :b; /[1-9][0-9]\?[0-9]\?\(,[0-9][0-9][0-9]\)\+\.[0-9][0-9]/ s/\([1-9][0-9]*\),\([0-9][0-9][0-9][,.]\)/\1\2/; tb;' | awk -F '[^0-9.]' '{ for (i=NF; i>0; i--) if ($i ~ /^[0-9]+\.[0-9][0-9]$/) { print $i; next } }'
 sed -r '/\n/!s/([^0-9]*\b(([0-9])[ ,]([0-9]{3})|([0-9]+))(\.[0-9]{2})\b)+/\n\3\4\5\6\n/;/^[0-9]+\.[0-9]{2}\b/P;D' file