在CSV中查找字符串的一部分并用新条目替换整个单元格?

时间:2017-01-08 19:47:26

标签: python bash csv awk sed

我有一个CSV文件,其中包含一个我希望筛选的列。我想使用模式文件来查找模式存在的所有条目,即使在列的值的一部分中,并使用此“模式”替换整个单元格值。 我列出了一些我想用作“模式”银行的关键词; 因此,如果此列中的单元格(此情况仅为第二个)将此“模式”作为其字符串的一部分,那么我想用此“模式”替换整个单元格。

所以例如:

我的目标文件:

id1,Taxidermy Equipment & Supplies,moreinfo1
id2,Taxis & Private Hire,moreinfo2
id3,Tax Services,moreinfo3
id4,Tools & Hardware,moreinfo4
id5,Tool Sharpening,moreinfo5
id6,Tool Shops,moreinfo6
id7,Video Conferencing,moreinfo7
id8,Video & DVD Shops,moreinfo8
id9,Woodworking Equipment & Supplies,moreinfo9

我的“模式”文件:

Taxidermy Equipment & Supplies
Taxis
Tax Services
Tool
Video
Wood

输出文件:

id1,Taxidermy Equipment & Supplies,moreinfo1
id2,Taxis,moreinfo2
id3,Tax Services,moreinfo3
id4,Tool,moreinfo4
id5,Tool,moreinfo5
id6,Tool,moreinfo6
id7,Video,moreinfo7
id8,Video,moreinfo8
id9,Wood,moreinfo9

我想出了通常的“查找和替换”sed:

sed -i 's/PATTERN/REPLACE/g' file.csv

但我想让它在特定的列上运行,所以我提出了:

awk 'BEGIN{OFS=FS="|"}$2==PATTERN{$2=REPLACE}{print}' file.csv

但它不适用于“字符串的一部分”([视频]:“视频和DVD商店” - >“视频”),我似乎无法得到它如何将awk作为文件输入对于“模式”块。

这是否有awk脚本?或者我必须写一些东西(例如在python中使用内置的csv套装?)

4 个答案:

答案 0 :(得分:2)

在awk中,使用index。它只会在更换时打印记录,但即使没有匹配也很容易修改打印(例如将print $1,i,$3}替换为$0=$1 OFS i OFS $3} 1):

$ awk -F, -v OFS=, '
NR==FNR { a[$1]; next }          # store "patterns" to a arr
        { for(i in a)            # go thru whole a for each record
              if(index($2,i))    # if "pattern" matches $2
                  print $1,i,$3  # print with replacement
        }
' pattern_file target_file
id1,Taxidermy Equipment & Supplies,moreinfo1
id2,Taxis,moreinfo2
id3,Tax Services,moreinfo3
id4,Tool,moreinfo4
id5,Tool,moreinfo5
id6,Tool,moreinfo6
id7,Video,moreinfo7
id8,Video,moreinfo8
id9,Wood,moreinfo9

答案 1 :(得分:1)

Perl解决方案,使用Text::CSV_XS

#!/usr/bin/perl
use warnings;
use strict;

use Text::CSV_XS qw{ csv };

my ($input_file, $pattern_file) = @ARGV;

open my $pfh, '<', $pattern_file or die $!;
chomp( my @patterns = <$pfh> );

my $aoa = csv(in => $input_file);
for my $line (@$aoa) {
    for my $pattern (@patterns) {
        if (-1 != index $line->[1], $pattern) {
            $line->[1] = $pattern;
            last
        }
    }
}

csv(in => $aoa, quote_space => 0, eol => "\n", out => \*STDOUT);

答案 2 :(得分:1)

这是(主要)awk解决方案:

#/bin/bash

patterns_regex=`cat patterns_file  | tr '\n' '|'`
cat target_file | awk -F"," -v patterns="$patterns_regex" '
BEGIN {
    OFS=",";
    split(patterns, patterns_split, "|");
}

{
    for (pattern_num in patterns_split) {
        pattern=patterns_split[pattern_num];
        if (pattern != "" && $2 ~ pattern) {
            print $1,pattern,$3
        }
    }
}'

答案 3 :(得分:1)

如果要使用sed解决此问题,则需要执行一些步骤 对于每个模式,您将需要一个像

这样的命令
sed 's/^\([^,]*\),\(.*Tool.*\),/\1,Tool,/' inputfile

您需要两次每个模式,您可以使用

翻译模式文件
sed 's/.*/"&" "&"/' patternfile
# Change the / into #, thats easier for the final command
sed 's#.*#"&" "&"#' patternfile

当您指示sed读取命令文件时,您需要使用sed开始每一行。命令文件看起来像

sed 's#.*#s/^\\([^,]*\\),\\(.*&.*\\),/\\1,&,/#;s#&#\\&#g'  patternfile

您可以存储这是一个文件并使用该文件,但通过进程替换,您可以执行

之类的操作
cat <(echo "Now this line from echo is handled as a file")

尼斯。让我们测试解决方案

sed -f <(sed 's#.*#s/^\\([^,]*\\),\\(.*&.*\\),/\\1,&,/#'  patternfile) inputfile

几乎就在那里!只有第一个输出线很奇怪。发生了什么事? 第一个模式有&,具有特殊含义 我们可以通过在模式中添加反斜杠来修补命令:

sed -f <(sed 's#.*#s/^\\([^,]*\\),\\(.*&.*\\),/\\1,&,/#;s#&#\\&#g' patternfile) inputfile