数字,括号和sed

时间:2015-01-12 10:32:35

标签: regex sed

我需要清理一些文字,并试图在括号中出现时删除数字。如果还有更多,那么应该保留。

示例:

Foo 12 (bar, 13) -> Foo 12 (bar)
Foo 12 (13, bar, 14) -> Foo 12 (bar) 
Foo (14, 13) -> Foo

我想我会先打破字符串并删除数字,如果它们出现在括号之间但似乎我错过了一些东西。

echo "Foo 12 (bar, 12)" | sed 's/\(.*\)\((\)\([^0-9,].*\)\([, ].*\)\([0-9].*\)\()\)/\1\2\3\6/g'

结果为Foo 12 (bar,)

我猜我的做法太原子了。我该怎么办?

4 个答案:

答案 0 :(得分:1)

如果您对Perl没有任何问题,可以试试这个。

$ perl -pe 's/\s*,?\s*\b\d+\b\s*,?\s*(?=[^()]*\))//g;s/\h*\(\)$//' file
Foo 12 (bar)
Foo 12 (bar)
Foo

OR

$ perl -pe 's/(?:(?<=\()\d+,\h*|,?\h*\d+\b)(?=[^()]*\))//g;s/\h*\(\)$//' file
Foo 12 (bar)
Foo 12 (bar)
Foo

DEMO

答案 1 :(得分:1)

以下是针对此类问题的一般方法,您希望隔离特定令牌并对其进行处理,以适应您的问题:

#!/bin/sed -f

:loop                       # while the line has a matching token
/([^)]*[0-9]\+[^)])/ {      
  s//\n&\n/                 # mark it -- \n is good as a marker because it is
                            # nowhere else in the line
  h                         # hold the line!
  s/.*\n\(.*\)\n.*/\1/      # isolate the token

  s/[0-9]\+,\s*//g          # work on the token. Here this removes all numbers
  s/,\s*[0-9]\+//g          # with or without commas in front or behind
  s/\s*[0-9]\+\s*//g
  s/\s*()//                 # and also empty parens if they exist after all that.

  G                         # get the line back
                            # and replace the marked token with the result of the
                            # transformation
  s/\(.*\)\n\(.*\)\n.*\n\(.*\)/\2\1\3/

  b loop                    # then loop to get all such tokens.
}

对于那些认为这超出了我应该合理地用sed做的范围的人我说:是的,但是......好吧,是的。但是,如果你看到的只是指甲,这是一种让sed成为大锤的方法。

这当然可以内联编写(虽然这对可读性没有帮助):

echo 'Foo 12 (bar, 12)' | sed ':loop;/([^)]*[0-9]\+[^)])/{;s//\n&\n/;h;s/.*\n\(.*\)\n.*/\1/;s/[0-9]\+,\s*//g;s/,\s*[0-9]\+//g;s/\s*[0-9]\+\s*//g;s/\s*()//;G;s/\(.*\)\n\(.*\)\n.*\n\(.*\)/\2\1\3/;b loop}'

但我的建议是将其放入文件并运行echo 'Foo 12 (bar, 12)' | sed -f foo.sed。或者,与上面的shebang一样,chmod +x foo.sedecho 'Foo 12 (bar, 12)' | ./foo.sed

顺便说一句,我没有对此进行基准测试。我想这不是处理大量数据的最有效方法。

编辑:回应评论:我不确定在这种情况下OP想要什么,但为了完成,基本模式可以适应其他行为,如下:

#!/bin/sed -f

:loop
/(\s*[0-9]\+\s*)\|(\s*[0-9]\+\s*,[^)]*)\|([^)]*,\s*[0-9]\+\s*)\|([^)]*,\s*[0-9]\+\s*,[^)]*)/ {
  s//\n&\n/
  h
  s/.*\n\(.*\)\n.*/\1/

  s/,\s*[0-9]\+\s*,/,/g
  s/(\s*[0-9]\+\s*,\s*/(/
  s/\s*,\s*[0-9]\+\s*)/)/
  s/\s*(\s*[0-9]*\s*)//

  G
  s/\(.*\)\n\(.*\)\n.*\n\(.*\)/\2\1\3/

  b loop
}

顶部的正则表达式现在看起来更加可怕。它应该有助于知道它由四个子模式组成

(\s*[0-9]\+\s*)
(\s*[0-9]\+\s*,[^)]*)
([^)]*,\s*[0-9]\+\s*)
([^)]*,\s*[0-9]\+\s*,[^)]*)

\|一起订购。这应该涵盖所有情况,并且不匹配括号中的foo1212barfoo12bar等内容(除非它们中还包含独立的数字)。

答案 2 :(得分:1)

以下是awk版本:

awk -F' *\\(|\\)' '{for (i=2;i<=NF;i+=2) {n=split($i,a," *, *");f="";for (j=1;j<=n;j++) f=f (a[j]!~/[[:digit:]]/?a[j]",":""); $i=f?"("f")":"";sub(/,)/,")",$i)}}1' file
Foo 12 (bar)
Foo 12 (bar)
Foo

cat file

Foo 12 (bar, 13, more)
Foo 12 (13, bar, 14) (434, tar ,56)
Foo (14, 13)

awk -F' *\\(|\\)' '{for (i=2;i<=NF;i+=2) {n=split($i,a," *, *");f="";for (j=1;j<=n;j++) f=f (a[j]!~/[[:digit:]]/?a[j]",":""); $i=f?"("f")":"";sub(/,)/,")",$i)}}1' file
Foo 12 (bar,more)
Foo 12 (bar)  (tar)
Foo

更具可读性:

awk -F' *\\(|\\)' '
    {
    for (i=2;i<=NF;i+=2) {
        n=split($i,a," *, *")
        f=""
        for (j=1;j<=n;j++) 
            f=f (a[j]!~/[[:digit:]]/?a[j]",":"")
            $i=f?"("f")":""
            sub(/,)/,")",$i)
        }
    }
1' file

答案 3 :(得分:1)

sed ':retry

# remove "( number )"
s/( *[0-9]* *)//

# remove first ", number" (not at first place)
s/^\(\([^(]*([^(]*)\)*[^(]*([^)]*\), *[0-9]\{1,\} *\([,)]\)/\1\3/
    t retry

# remove " number" (first place)
s/^\(\([^(]*([^(]*)\)*[^(]*(\) *[0-9]\{1,\}\(,\{0,1\}\)\()\{0,1\}\)]*/\1\4/

# case needed where only "( number)" or "()" are the result at this moment
t retry
' YourFile
  • (posix version so --POSIX on GNU sed)