【问题标题】:Using strings from several input files as search criteria for select columns in a CSV file using AWK使用 AWK 将多个输入文件中的字符串用作 CSV 文件中选择列的搜索条件
【发布时间】:2015-09-24 08:30:41
【问题描述】:

问题的性质:

我有一个包含 10 列的 CSV 文件,其中 4 列指定疾病代码。假设这些是第 1 - 4 列。我有 2 个包含“包含”和“排除”代码的文本文件。

包含文件如下:一个带有n输入字符串的文件,每个都在换行符

例子:

123
12300
12301
124
12400
12401
1250

排除文件如下:一个带有m输入字符串的文件,每个字符串都在换行符上。

例子:

456
457
458
459

CSV 文件的截断版本如下所示:

D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
890,001,456,0009,A2,B2,C2,D2,E2,F2
12301,456,00,145,A3,B3,C3,D3,E3,F3
567,1250,010,321,A4,B4,C4,D4,E4,F4

使用 AWK,我如何获取两个名为 inclusionexclusion 的文件以及返回以下内容的 CSV 文件:

D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4

CSV 文件可以有数百万行,而inclusionexclusion 文件可以有几十行。这不是家庭作业,感谢您的帮助。

【问题讨论】:

  • 如果一行既有匹配inclusion的字段又有匹配exclusion的字段会怎样?如果两者都没有会发生什么?到目前为止,您尝试过什么?
  • 排除优先。这就是为什么第三行被省略的原因。抱歉没有说清楚。
  • 非常好。还有我的另外两个问题?
  • 如果不匹配,则排除该行。到目前为止,我一直通过将特定字符串硬编码到 awk 行中来做到这一点。

标签: bash shell csv awk


【解决方案1】:

使用 grep

$ head -n1 <file; grep -E "(^|,)($(tr '\n' '|' <inclusion))(,|$)" file | grep -Ev "(^|,)($(tr '\n' '|' <exclusion))(,|$)"
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4

使用 awk

$ awk -v inc="(^|,)($(tr '\n' '|' <inclusion))(,|$)" -v exc="(^|,)($(tr '\n' '|' <exclusion))(,|$)" 'NR==1 || ($0 ~ inc && ! ($0 ~ exc))' file
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4

工作原理

对于 grep 和 awk 解决方案,关键步骤是创建匹配包含或排除文件的正则表达式。因为比较短,我们以exclusion为例。我们可以为它创建一个正则表达式,如下所示:

$ echo "(^|,)($(tr '\n' '|' <exclusion))(,|$)"
(^|,)(456|457|458|459|)(,|$)

inclusion 的正则表达式的工作方式类似。一旦创建了包含和排除正则表达式,我们就可以将它们与 grep 或 awk 一起使用。如果使用 awk,我们使用条件:

NR==1 || ($0 ~ inc && ! ($0 ~ exc))

如果此条件为真,则 awk 执行其默认操作,即打印该行。如果 (1) 我们在第一行 NR==1 或 (2) 该行在正则表达式中匹配以包含 inc,并且与正则表达式不匹配以排除 exc,则条件为真。

替代 awk 解决方案

$ gawk -F, -v inc="$(<inclusion)" -v exc="$(<exclusion)" 'BEGIN{n=split(inc,x,"\n"); for (j=1;j<=n;j++)incl[x[j]]=1; n=split(exc,x,"\n"); for (j=1;j<=n;j++)excl[x[j]]=1;} NR==1{print;next} {p=0;for (j=1;j<=NF;j++) if ($j in incl)p=1; for (j=1;j<=NF;j++) if ($j in excl) p=0;} p' file
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4

多行写出的相同代码如下所示:

gawk -F, -v inc="$(<inclusion)" -v exc="$(<exclusion)" '
BEGIN{
    n=split(inc,x,"\n")
    for (j=1;j<=n;j++)incl[x[j]]=1
    n=split(exc,x,"\n")
    for (j=1;j<=n;j++)excl[x[j]]=1
}
NR==1{
    print
    next
} 

{
    p=0
    for (j=1;j<=NF;j++) if ($j in incl) p=1
    for (j=1;j<=NF;j++) if ($j in excl) p=0
}
p
' file

上面使用inclusionexclusion 数据创建数组inclexcl。在incl 中包含字段的任何行都标记为打印p=1。但是,如果该行包含 excl 中的字段,则 p 设置为 false,p=0

【讨论】:

  • 谢谢 John1024。该代码是查看包含/排除标准的所有列,还是查看特定的几列?
  • 当我在 awk(OSX 版本 20070501)上运行此代码时,它失败了。当我在 gawk (v4.1.3) 上运行它时,它省略了上面给出的测试文件中的第二行。当我在 mawk 1.3.4 上运行它时,它也省略了第二行。不同版本的 awk 解释正则表达式是否存在已知问题?
  • @oort 它旨在查看从头到尾的所有列。而且,是的,BSD (OSX) 和 GNU awk 有许多微妙而烦人的不兼容性。但是,您的 gawk 版本与我的(4.1.1)几乎相同,我不明白为什么您会从我这里得到不同的结果。为了验证,我只是将此处答案中的命令复制并粘贴到我的终端,并得到了正确的结果,包括第二行。
  • @oort 我刚刚添加了一个新的非常 awk 的解决方案。它根本不使用正则表达式。让我知道它是否适合您。
猜你喜欢
  • 1970-01-01
  • 2012-05-11
  • 1970-01-01
  • 1970-01-01
  • 2022-01-07
  • 2022-01-16
  • 2018-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多