【问题标题】:Find and Replace Pipe delimiter from field in a pipe delimited file从管道分隔文件中的字段查找和替换管道分隔符
【发布时间】:2017-08-31 11:17:47
【问题描述】:

我之前有过类似的问题,后来我要为这个问题增加更多的范围,但不知道如何编辑它并让它再次生效。这就是为什么我要发布一个新问题。

我的文件是一个竖线分隔的文件。

 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04  | GO|OGLE | EUROPE | EURO   | PARIS
 XYZE | 12  | Y|A|HOO | USA    | DOLLAR | SEATTLE
 LMNO | 17  | |FACE|B|O|O|K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98  | A||M|AZ|ON| | AFRICA | AF DOLLAR | CAPETOWN

我的文件就是这么复杂。我们需要删除“|” WEB 字段中的符号,并将其替换为垃圾值,例如 #、$ 和任何其他值。

输出必须是:

NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN

我尝试了几个过滤器来清理这个混乱。似乎没有什么能找到一个幸福的结局。谢谢! 我要感谢几个回答我上一个问题的人:RomanPerekhrest、Ed Morton、shellter、val rog。

【问题讨论】:

  • 分隔符前后是否总是有空格?
  • 不,不是。他们都挤得很紧。只是为了更好的说明目的,我是这样写的
  • 那你怎么知道在acbd|02|gh|ij|kl|mn|op|qr 这样的字符串上,web 字段的开始和结束位置?
  • 我们确定第二个字段是“NUM”,而 LOCATION 字段是第四个字段,这不会在文件中更改。介于这两个字段之间的任何字段都必须接受审查。

标签: linux shell unix awk sed


【解决方案1】:
$ cat tst.awk
BEGIN { FS=OFS="|" }
NR==1 { outNf=NF; print; next }
{
    end = beg + (NF - outNf) - 1
    for (i=1; i<=NF; i++) {
        sep = (i>=beg && i<=end ? "#" : OFS)
        printf "%s%s", $i, (i<NF ? sep : ORS)
    }
}

$ awk -v beg=3 -f tst.awk file
 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
 XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
 LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN

它是如何工作的:在第一行,要输出的字段数与该行上的字段数相同,因此它将该数字保存为 outNF。从那时起,任何具有多个 outNF 字段的后续行都将组合从 beg 开始的 outNF-NF 字段。因此,在循环内部,它在从 1 到 beg 的字段之间使用 OFS,然后从 beg+1 到 beg+(outNF-NF) 它使用 # between fields 从该范围内的输入字段创建一个合并的输出字段,然后返回在字段之间使用 OFS。

【讨论】:

  • 这是迄今为止最好的解决方案,我有疑问。我们在 .awk 文件中做什么。你能解释一下那个 .awk 文件中发生了什么吗?我试图破译但无法真正理解。
  • 命令如何知道要查找的字段是 3 直到最后。我知道您已将其声明为 beg=3,但它如何知道它必须检查直到文件的最后一个字段才能找到并替换 |。
  • 例如,我们知道问题字段是#3。该命令如何能够从字段 3 跟踪到最后一个字段。我在 .awk 文件中看不到任何比较文件的每个字段的条件。
  • Ed,我的最后一个问题是,我怎样才能像你一样达到你对 linux 脚本和文件处理工具的理解水平。我知道这完全取决于不断的练习,除此之外还有其他成分。开发人员在这里互相帮助的方式给我留下了深刻的印象。
  • 谢谢,你帮了我很多。我真的欠你很多知识。我真的希望你和你所爱的人一切顺利。你救了我男人!! :)
【解决方案2】:

你可以使用这个awk 命令:

awk 'BEGIN{FS=OFS="|"} NR==1{n=NF} NF > n {
s=$3; for (i=4; i<=NF-3; i++) {s = s "#" $i; $i=""} $3=s; gsub(/\|{2,}/, "|")} 1' file

NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN

【讨论】:

  • 请注意,此答案适用于有问题的给定示例数据,并且仅适用于转换第三个字段中的额外字段。
  • Hi Anubhav .. 这里的“gsub(/\|{2,}/, "|")}” 是什么意思。你能介绍一下吗
  • 什么是 gsub(/\|{2,}/, "|")}" 中的“2”以及为什么在 for 循环中设置 i = 4。
  • i=4 因为我们从第 4 个字段开始抓取额外字段。在将$i = "" 中的每个额外列设置为空后调用gsub 函数,并将额外连续的| 替换为单个|
【解决方案3】:

如果您不介意使用 Perl 就很容易

如果有空格;然后我们可以通过以下方式打印它:

stackoverflow ❱ perl -F'\s+|\s+' -a -le  'print $F[5]' file
WEB
GO|OGLE
Y|A|HOO
|FACE|B|O|O|K
A||M|AZ|ON|
stackoverflow ❱  

由于我们可以在Perl中修改@F数组;因此我们可以:

$F[5] =~ s/\|/#/g;  

它只修改此列而不修改其他列。

最终我们可以打印出来:

stackoverflow ❱ perl -F'\s+|\s+' -lae  '$F[5] =~ s/\|/#/g;print "@F"' file
 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04 | GO#OGLE | EUROPE | EURO | PARIS
 XYZE | 12 | Y#A#HOO | USA | DOLLAR | SEATTLE
 LMNO | 17 | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98 | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN
stackoverflow ❱  

如果您的文件没有空间,正如有人评论我的那样;然后你可以传播其他列;只修改那个并将它们全部连接在一起:

stackoverflow ❱ cat file2
NAME|NUM|WEB|LOCATION|CURRENCY|PLACE
ABCD|04|GO|OGLE|EUROPE|EURO|PARIS
XYZE|12|Y|A|HOO|USA|DOLLAR|SEATTLE
LMNO|17||FACE|B|O|O|K|ASIA|ASIANDOLLAR|HONGKONG
EDDE|98|A||M|AZ|ON||AFRICA|AFDOLLAR|CAPETOWN
stackoverflow ❱ perl -F'\|' -le  '$s=$#F;$e="@F[2..$s-3]";$e=~s/ +/#/g;print join "|", @F[0..1],$e,join "|",@F[$s-2,$s-1,$s]' file2
NAME|NUM|WEB|LOCATION|CURRENCY|PLACE
ABCD|04|GO#OGLE|EUROPE|EURO|PARIS
XYZE|12|Y#A#HOO|USA|DOLLAR|SEATTLE
LMNO|17|#FACE#B#O#O#K|ASIA|ASIANDOLLAR|HONGKONG
EDDE|98|A#M#AZ#ON#|AFRICA|AFDOLLAR|CAPETOWN

【讨论】:

    【解决方案4】:

    另一个awk解决方案可以是:-

    awk  -F'[[:space:]][|][[:space:]]' '{gsub(/\|/,"#",$3);print $1,"|",$2,"|",$3,"|",$4,"|",$5,"|",$6}' file.txt
    

    解释:-

    -F - for field separator here it is space|space
    gsub - global substitution in field 3. i.e. every occurance of | will be replaced by #. 
    print - just print all the columns separated by "|"
    

    输出将是:-

    NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
    ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
    XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
    LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
    EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN
    

    【讨论】:

      【解决方案5】:

      一个简单的awk解决方案:

      awk  -F "|" '{printf $1} 
      {for(i=2; i<=NF; i++) { if(i>3 && i<NF-2)printf "#"$i; else printf "|"$i } printf "\n"} ' file
      
      NAME|NUM|WEB|LOCATION|CURRENCY|PLACE
      ABCD|04|GO#OGLE|EUROPE|EURO|PARIS
      XYZE|12|Y#A#HOO|USA|DOLLAR|SEATTLE
      LMNO|17|#FACE#B#O#O#K|ASIA|ASIANDOLLAR|HONGKONG
      EDDE|98|A##M#AZ#ON#|AFRICA|AFDOLLAR|CAPETOWN
      

      if(i&gt;3 &amp;&amp; i&lt;NF-2) :此条件适用于第 3 场之后和 NF-2 场之前的额外不需要的场。如果满足,请在打印这些额外字段之前添加前缀“#”。

      【讨论】:

      • @mac_online:这可以带/不带空格。一个简单的通用解决方案。
      • 嗨,哇,这也不错。我可以知道你为什么在 for 循环中设置 i=2
      • 在 for 循环中,字段以这种方式打印 [operator][value],即以运算符“#”或“|”为前缀的值。我在 for 循环之前打印了第一个字段,因为它不需要前缀,然后使用从第二个字段到最后一个字段的 for 循环来实现我们所需的逻辑。
      【解决方案6】:

      我并没有试图把它放在一行中,而是让它更容易阅读。那些玩perl golf 的人将能够大大减少它。这个想法是锚定前两个字段和后三个字段。

      #!/usr/bin/perl
      
      while(<DATA>) {
        chomp;
        if(($name, $num, $web, $location, $currency, $place) = $_ =~
           /^([^\|]+)\|([^\|]+)\|(.+)\|([^\|]+)\|([^\|]+)\|([^\|]+)$/) {
          $web =~ tr/\|/\_/;
          printf "%s\n", join('|', ($name, $num, $web, $location, $currency, $place));
        }
      }
      __DATA__
       NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
       ABCD | 04  | GO|OGLE | EUROPE | EURO   | PARIS
       XYZE | 12  | Y|A|HOO | USA    | DOLLAR | SEATTLE
       LMNO | 17  | |FACE|B|O|O|K | ASIA | ASIAN DOLLAR | HONGKONG
       EDDE | 98  | A||M|AZ|ON| | AFRICA | AF DOLLAR | CAPETOWN
      

      输出:

       NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
       ABCD | 04  | GO_OGLE | EUROPE | EURO   | PARIS
       XYZE | 12  | Y_A_HOO | USA    | DOLLAR | SEATTLE
       LMNO | 17  | _FACE_B_O_O_K | ASIA | ASIAN DOLLAR | HONGKONG
       EDDE | 98  | A__M_AZ_ON_ | AFRICA | AF DOLLAR | CAPETOWN
      

      【讨论】:

      • 我不是 100% 确定,但我认为这就是 @k-five 所做的。职业高尔夫球手!
      猜你喜欢
      • 2012-02-19
      • 2020-11-30
      • 1970-01-01
      • 2019-08-27
      • 1970-01-01
      • 1970-01-01
      • 2020-07-21
      • 2010-09-17
      • 2016-09-05
      相关资源
      最近更新 更多