为了便于阅读,在几行上格式化字符串常量

时间:2011-12-20 21:21:26

标签: awk gawk

出于学习目的,我正在为电话号码实现一个小的正则表达式匹配器。我的目标是可读性,而不是最短的gawk程序:

# should match
#1234567890
#123-456-7890
#123.456.7890
#(123)456-7890
#(123) 456-7890 

BEGIN{
    regexp="[0-9]{10},[0-9]{3}[-.][0-9]{3}[.-][0-9]{4},\\([0-9]{3}\\) ?[0-9]{3}-[0-9]{4}"
    len=split(regexp,regs,/,/)
}
{for (i=1;i<=len;i++)
    if ($0 ~ regs[i]) print $0
}

为了更好的可读性,我想将行regexp="...分成几行,如:

regexp="[0-9]{10}
       ,[0-9]{3}[-.][0-9]{3}[.-][0-9]{4}
       ,\\([0-9]{3}\\) ?[0-9]{3}-[0-9]{4}"

在awk中有一种简单的方法吗?

5 个答案:

答案 0 :(得分:3)

BEGIN {
    regs[1] = "[0-9]{10}"
    regs[2] = "[0-9]{3}[-.][0-9]{3}[.-][0-9]{4}"
    regs[3] = "\\([0-9]{3}\\) ?[0-9]{3}-[0-9]{4}"
    c = 3
    }
{
  for (i = 1; i <= c; i++)
    if ($0 ~ regs[i]) 
      print $0
  }

如果你的awk实现支持length(数组) - 使用它(参见下面的Jaypal Singh评论):

BEGIN {
    regs[1] = "[0-9]{10}"
    regs[2] = "[0-9]{3}[-.][0-9]{3}[.-][0-9]{4}"
    regs[3] = "\\([0-9]{3}\\) ?[0-9]{3}-[0-9]{4}"
    }
{
  for (i = 1; i <= length(regs); i++)
    if ($0 ~ regs[i]) 
      print $0
  }

还要考虑计算(动态)正则表达式的副作用, 有关详细信息,请参阅GNU awk manual

答案 1 :(得分:2)

以下链接可能包含您要查找的答案:

http://www.gnu.org/software/gawk/manual/html_node/Statements_002fLines.html

它表示在awk脚本文件或某些shell的命令行中,awk命令可以以与makefile命令相同的方式拆分为多行。只需用反斜杠(\)结束该行,awk将在解析时丢弃换行符。将此与字符串的隐式串联(类似于C)相结合,解决方案可以是

BEGIN {
    regexp = "[0-9]{10}," \
             "[0-9]{3}[-.][0-9]{3}[.-][0-9]{4}," \
             "\\([0-9]{3}\\)?[0-9]{3}-[0-9]{4}"
    len = split(regexp, regs, /,/)
}

尽管如此,我赞成将正则表达式直接存储在数组中的解决方案:它更好地反映了语句的意图,并没有强迫程序员完成比需要更多的工作。此外,不需要length函数,因为可以使用foreach语法。需要注意的是,awk中的数组类似于Java中的映射或Python中的字典,因为它们不会将一系列整数索引与值相关联。相反,他们将字符串键映射到值。即使将整数用作键,它们也会隐式转换为字符串。因此,并不总是提供length函数,因为它具有误导性。

BEGIN {
    regs[1] = "[0-9]{10}"
    regs[2] = "[0-9]{3}[-.][0-9]{3}[.-][0-9]{4}"
    regs[3] = "\\([0-9]{3}\\)?[0-9]{3}-[0-9]{4}"
}

{
    for (i in regs) {        # i recieves each key added to the regs array
        if ($0 ~ regs[i]) {
            print            # by default `print' prints the whole record
            break            # we can stop finding a regexp
        }
    }
}

请注意,break命令会过早退出for循环。如果每条记录只打印一次,即使几个正则表达式可以匹配,这也是必要的。

答案 2 :(得分:1)

那么你可以将regexp存储在变量中,然后加入它们,例如:

awk '{
       COUNTRYCODE="WHATEVER_YOUR_CONTRY_CODE_REGEXP"
       CITY="CITY_REGEXP"
       PHONENR="PHONENR_REGEX"
       THE_WHOLE_THING=COUNTRYCODE CITY PHONENR
       if ($0 ~ THE_WHOLE_THING) { print "BINGO" }
     }'

HTH

答案 3 :(得分:1)

共识似乎是没有简单的方法来分割多行字符串而不会打扰awk?感谢其他想法,但让我作为程序员做我不喜欢的电脑工作。所以我提出了这个解决方案,在我看来,它非常接近一种可执行规范。我使用base和here文档并处理redicrection来动态创建awk的文件:

#!/bin/bash

# numbers that should be matched
read -r -d '' VALID <<'valid'
1234567890
123-456-7890
123.456.7890
(123)456-7890
(123) 456-7890 
valid
# regexp patterns that should match
read -r -d '' PATTERNS <<'patterns'
[0-9]{10}
[0-9]{3}\.[0-9]{3}\.[0-9]{4}
[0-9]{3}-[0-9]{3}-[0-9]{4}
\([0-9]{3}\) ?[0-9]{3}-[0-9]{4}
patterns

gawk --re-interval 'NR==FNR{reg[FNR]=$0;next}
  {for (i in reg) 
    if ($0 ~ reg[i]) print $0}' <(echo "$PATTERNS") <(echo "$VALID")

欢迎任何评论。

答案 4 :(得分:0)

我想介绍我最喜欢的问题,因为它还没有被提及。我喜欢使用awk的简单字符串追加操作,这只是两个术语之间的默认运算符,作为典型数学符号中的乘法:

x = x"more stuff"

"more stuff"附加到x并再次将新值设置为x。所以你可以写

regexp = ""
regexp = regexp"[0-9]{10}"
regexp = regexp"[0-9]{3}[-.][0-9]{3}[.-][0-9]{4}"
regexp = regexp"\\([0-9]{3}\\) ?[0-9]{3}-[0-9]{4}"

要控制其他拆分字符,例如我知道的大多数语言和awk的片段之间的换行符,可以使用数组连接和拆分方法从数组生成字符串并将字符串转换回数组,而不会丢失原始数据数组的结构(例如换行符标记):

i = 0
regexp[i++] = "[0-9]{10}"
regexp[i++] = "[0-9]{3}[-.][0-9]{3}[.-][0-9]{4}"
regexp[i++] = "\\([0-9]{3}\\) ?[0-9]{3}-[0-9]{4}"

使用regstr = join(regexp, ",")添加拆分&#34;,&#34;你用过。 当然在awk中没有连接功能,但我想这很简单 实现,知道上面的字符串追加操作。

我的方法似乎看起来更冗长,但有一个优点,即原始数据(此部分中的正则表达式字符串片段)以每个片段的字符串常量为前缀。这意味着代码可以通过一个非常简单的算法(甚至一些编辑器快捷方式)生成。