首页 > 解决方案 > 在一行上匹配两个模式并将它们打印在两列中

问题描述

我有大约几百个 CSV 文件。这些 CSV 文件有不同的定义,我不想手动将所有 CSV 文件合并为一种格式。

我想从文件中得到两个不同的东西——A 和 B,我可以用正则表达式匹配它们。我想同时匹配它们两个 - 所以只会打印包含这两个东西的行。我知道该怎么做,而且我已经看到很多 SO 帖子回答了如何做到这一点。

但我不知道如何在A B没有其余行的情况下打印。我不知道这两件事的顺序或列中,所以我不知道如何(或者我什至可以)使用 awk。

例子:

(匹配 A[0-9]、B[0-9])

A0 B0 C0
B1 C1 D1
E2 C2 A2
C3 F3 F3
B4 F4 A4

结果:

A0 B0
A4 B4

标签: awkfiltergrep

解决方案


一种解决方案:使用match. awk它将按照 OP 显示的示例从字母 A 到 B 的顺序给出输出。

awk '
match($0,/A[0-9]+/){
  val=substr($0,RSTART,RLENGTH)
  if(val && match($0,/B[0-9]+/)){
     print val,substr($0,RSTART,RLENGTH)
  }
}'  Input_file


第二种解决方案:此解决方案不关心字母 A 和 B,因此它们按照哪个顺序排列,它们将按相同的顺序出现。

awk '
{
  for(i=1;i<=NF;i++){
    if($i ~ /A[0-9]+/ || $i ~ /B[0-9]+/){
       val=val?val OFS $i:$i
    }
  }
  if(val ~ /A[0-9]+/ && val ~ /B[0-9]+/){
    print val
  }
  val=""
}
END{
  if(val ~ /A[0-9]+/ && val ~ /B[0-9]+/){
    print val
  }
}'   Input_file


第三种解决方案:考虑到您在输出中按 A 到 B 的顺序需要它们,那么以下可能会有所帮助。

awk '
{
  for(i=1;i<=NF;i++){
     line=$i
     sub(/[0-9]+/,"",line)
     if($i ~ /A[0-9]+/ || $i ~ /B[0-9]+/){
       array[tolower(line)]=$i
     }
  }
  if(array["a"] ~ /A[0-9]+/ && array["b"] ~ /B[0-9]+/){
     print array["a"],array["b"]
  }
  delete array
}
END{
  if(array["a"] ~ /A[0-9]+/ && array["b"] ~ /B[0-9]+/){
     print array["a"],array["b"]
  }
}'   Input_file

注意:man awk文档中添加有关使用函数的信息,例如-->match、、和tolowerRSTARTRLENGTH

match(s, r [, a]) 返回 s 中正则表达式 r 出现的位置,如果 r 不存在,则返回 0,并设置 RSTART 和 RLENGTH 的值。请注意,参数顺序与 ~ 运算符相同:str ~ re。如果提供了数组 a,则清除 a,然后用 s 中与 r 中相应的带括号的子表达式匹配的部分填充元素 1 到 n。a 的第 0 个元素包含 s 与整个正则表达式 r 匹配的部分。下标 a[n, "start"] 和 a[n, "length"] 分别提供每个匹配子字符串在字符串中的起始索引和长度。

RSTART match()匹配的第一个字符的索引;如果不匹配,则为 0。(这意味着字符索引从 1 开始。)

RLENGTH match()匹配的字符串长度;-1 如果不匹配。

tolower(str) 返回字符串 str 的副本,其中 str 中的所有大写字符都转换为对应的小写字符。非字母字符保持不变。


推荐阅读