【问题标题】:Using grep / sed replace values from list with semi colons, new lines as seperators使用 grep / sed 用分号替换列表中的值,换行作为分隔符
【发布时间】:2020-10-13 10:27:09
【问题描述】:

我是相当新的 grep、sed 和 awk。我过去曾使用它们来提取行和/或替换确切列表中的内容。

在这种情况下,我对如何去做感到困惑。我有两个 csv 文件。

我的第一个 csv 文件是由空格和分号分隔的名称。

姓名,
弗兰克
坦率;约翰;抢; ,
约翰;缺口; ,

第二个 csv 带有位置和名称

地点名称, 法国,弗兰克, 新泽西州约翰, 尼克,德国, 罗布,日本,

我希望输出将位置添加为名称旁边的列。

姓名、地点、 法兰克,法国, 坦率;约翰;抢; , 法国;新泽西州;日本, 约翰;缺口; , 新泽西州;德国,

如何逐行搜索第二个 csv 文件并将每个名称视为唯一名称以提取其各自的位置?然后输出它,这样它就可以用分号保留每行的信息..

到目前为止我所做的是:

猫文件1.csv |切-f1 | tr';' '\t' > 文件-test.tsv

谢谢。

【问题讨论】:

  • 欢迎来到 SO,在 SO,我们确实鼓励用户添加他们为解决自己的问题所做的努力,所以请编辑您的帖子并在那里添加您的努力。
  • 能否更正您的示例:您的第二个 csv 文件不一致,名称从第一列移到第二列。
  • 大家好,感谢您修复格式和示例。我已经进行了修改,现在应该没问题了。
  • 在第二个文件的第二行,你有France, Frank, 。不应该反过来吗?
  • 逻辑不是很清楚。为什么Frank 不在所需输出的最后一行?

标签: awk sed grep


【解决方案1】:

您的文件格式有些奇怪。逗号整体分隔,各个字段用分号分隔,但有时尾随分号有时不。 此外,在编写此答案时,您的第二个文件仍然具有第一个数据行的“位置、名称”和所有其余部分的“名称、位置”。我假设 actual 文件的每一行都是“位置,名称”。

这是我的处理方式:

  • 通过第二个文件并创建从名称到位置的映射
  • 通过第一个文件并应用映射

这是我的解决方案,只使用 awk:

# use delimiter of zero or more spaces on either side of a comma
awk -F ' *, *' '
# First line of first file processed; set flag variable
FNR == 1 && NR == 1 {mapfile = 1;}

# Lines 2+ in the map file: save the mapping
mapfile && FNR > 1 {map[$2] = $1;}

# First line of second file; print header and reset flag
FNR == 1 && NR > 1 {print "Name, Location,"; mapfile = 0;}

# Process lines 2+ in the name file (i.e. not the map file)
!mapfile && FNR > 1 {
    data = $0;
    sub(/ *, *$/,"",data); # remove trailing comma
    sub(/ *; *$/,"",data); # remove trailing semicolon

    # create "names" array of length "num"
    num = split(data,names,/ *; */);

    locs = ""; # init location string to empty

    for (i = 1; i <= num; i++)
    {
        locs = locs map[names[i]] "; ";
    }

    sub(/; $/,",",locs); # change last semicolon to comma

    # print original line from name file, and append locations
    print $0 " " locs;

}' file2.csv file1.csv

更多解释:

  • NR = 正在处理“行数”。无论awk 处理了多少文件,它都从 1 开始并永远递增
  • FNR = "文件行号"。这从 1 开始,每个文件都在处理中

所以当两者都为 1 时,正在处理映射文件的第一行。 当FNR 为1 但NR 大于1 时,正在处理第二个文件。

还有,

  • awk 可以使用正则表达式作为分隔符,所以我告诉它使用逗号作为分隔符,两边各有零个或多个空格 ( *, *)。
  • $0 = 整行
  • $1$2 等是使用指定分隔符拆分时每行的各个字段。

其余的逻辑从脚本中的代码和cmets应该是不言而喻的。

按此顺序处理文件时

  1. file2.csv = 您的第二个文件,但在所有行中使用“位置、名称”顺序
  2. file1.csv = 您的第一个文件

输出是:

Name, Location,
Frank , France,
Frank; John; Rob;  , France; New Jersey; Japan,
John; Nick;  , New Jersey; Germany,

【讨论】:

    【解决方案2】:

    假设您的第二个文件的行实际上总是以location, name 顺序排列,而不是有时一个,有时另一个,如您问题中的示例所示,这里是如何输出您想要的数据:

    $ cat tst.awk
    BEGIN { FS=" *, *"; OFS=" , " }
    NR == FNR {
        name2loc[$2] = $1
        next
    }
    {
        for (i=1; i<=NF; i++) {
            n = split($i,names,/ *; */)
            for (j=1; j<=n; j++) {
                locs = (j>1 ? locs "; " : "") name2loc[names[j]]
            }
        }
        print $1, locs
    }
    

    .

    $ awk -f tst.awk file2 file1
    Name , Location
    Frank , France
    Frank; John; Rob; , France; New Jersey; Japan;
    John; Nick; , New Jersey; Germany;
    

    调整输出格式以适合您真正希望输出的样子。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 2012-04-09
      • 2021-10-25
      • 1970-01-01
      相关资源
      最近更新 更多