【问题标题】:Remove duplicate words after stripping punctuation去除标点符号后删除重复的单词
【发布时间】:2013-06-10 20:28:59
【问题描述】:

假设我有一个包含以下内容的文件:

VSDmaMapInfo
VSDmaMapInfo::
VSDmaMapInfo;
VSPortErr
VSPortErr,
VSPortErr::

排序后我希望输出是

VSDmaMapInfo
VSPortErr

有没有办法使用 grep、awk、uniq 或任何其他工具等来做到这一点

非常感谢您的帮助。

【问题讨论】:

  • 去掉标点符号,然后得到唯一的行?哦,Vs 的大写不一致是怎么回事?这相关吗?
  • 是的。这样就可以了。是的,我的错字。总是VS..

标签: regex perl sed awk pattern-matching


【解决方案1】:
$ awk -F'[[:punct:]]' '{print $1}' file | sort -u
VSDmaMapInfo
VSPortErr

【讨论】:

    【解决方案2】:

    使用 GNU sed 排序内容的代码

    sed -r '$!N;/(\w+)\W*\n\1\W*/!{s/(\w+).*/\1/;P};D' file
    

    【讨论】:

      【解决方案3】:

      跳过重复的行,

      perl -nE 's|\W||g; say unless $h{$_}++' file
      

      【讨论】:

        【解决方案4】:

        这可以工作:

        $ tr -d "[[:punct:]]" < file | sort -u
        VSDmaMapInfo
        VSPortErr
        

        说明

        tr -d "[[:punct:]]"            < file         |    sort -u
           remove puntuation chars     read file          get unique
        

        更新

        来自您的评论:

        我刚刚观察到:如果输入包含 VSDmaMapInfo::callMe 它正在删除标点符号但加入下一个单词 VSDmaMapInfocallMe。是否有可能我的输出为 VSDmapMapInfo 仅不附加下一个单词。

        我们可以做到以下几点:

        $ cat file
        VSDmaMapInfo
        VSDmaMapInfo::
        VSDmaMapInfo;
        VSDmaMapInfo;asdfs
        VSPortErr
        VSPortErr,
        VSPortErr::
        
        $ awk -F"[,:;]" '{print $1}' file | sort -u
        VSDmaMapInfo
        VSPortErr
        

        也就是说,让awk 打印任何,:; 之前的第一个单词。然后,使用-u 参数对其进行排序以获得唯一数据。

        【讨论】:

        • 更新了我的答案。以前每个名称都有不同的大小写,所以我添加了一个管道来将所有内容都用小写。
        • 非常感谢.. 它有效。我刚刚观察到:如果输入包含 VSDmaMapInfo::callMe,它会删除标点符号,但会加入下一个单词,例如 VSDmaMapInfocallMe。是否有可能我只将输出作为 VSDmapMapInfo 而没有附加下一个单词。
        【解决方案5】:

        假设重复数据删除不区分大小写,以下 Perl-oneliner 会发出所需的输出:

        perl -ne's/[[:punct:]]+$//;$h{lc $_}++ or print'
        

        测试:

        $ perl -ne's/[[:punct:]]+$//;$h{lc $_}++ or print' <<'END'
        VSDmaMapInfo
        VSDmaMapInfo::
        VsDmaMapInfo;
        VSPortErr
        VsPortErr,
        VsPortErr::
        END
        

        输出:

        VSDmaMapInfo
        VSPortErr
        

        编辑:

        对于区分大小写的匹配,将$h{lc $_}++ 更改为$h{$_}++

        编辑2:

        要删除行中第一个标点字符之后的任何内容,请将替换替换为s/[[:punct:]].*//

        要使用文件调用单行,您可以将输入文件列为命令行参数:

        $ perl -ne'...' the-file.txt
        

        【讨论】:

        • 非常感谢..这真的很有帮助。假设我有一个大文件作为输入,我如何将它传递给你的脚本。
        • @MarcSpencer 您可以将文件作为第一个命令行参数传递,而不是 &lt;&lt;'END' here-doc: perl -ne'...' the-file.txt。您还可以将内容通过管道传输到脚本中,例如 perl -ne'...' &lt; the-file.txt
        【解决方案6】:

        sed 解决方案(基本上是 sed + sort)

        sed 's/[^[:alpha:]]//g' <file> |sort -u
        

        另一个笨拙的 awk 解决方案

        awk '{gsub(/[^[:alpha:]]/,""); a[$0]=1} END{for(var in a) print var}' <file>
        

        另一个很棒的纯 bash 解决方案(我喜欢玩 bash :))

        l=""
        while read r
        do
        r=${r//[^[:alpha:]]/}
         if ! [[ $l =~ $r ]]
         then
         echo $r
         l="$l $r"
         fi
        done < <file>
        

        【讨论】:

          【解决方案7】:

          如果你使用 GNU awk,你可以使用多个字符作为记录分隔符 (RS),所以你可以这样做:

          awk '!a[$0]++' RS='[[:punct:]]*\n' test.txt
          

          解释:

          1. 通过将记录分隔符 (RS) 设置为此正则表达式,我们去掉了尾随标点符号,因此记录,即 $0 是一个单词。
          2. 我们会统计a 中的字数。
            • 如果单词不重复,a[$0] 为 0,!a[$0] 计算结果为真,因此该单词被打印出来。否则,它不会被打印出来。

          【讨论】:

          • 你应该提到这只是 GNU awk。
          • @EdMorton,是哪一部分? [[::punc::]]?
          • 将 RS 设置为多个字符。在 POSIX 和大多数其他非 GNU awks 中,RS 只能是单个字符,并且所有其他字符都会被忽略,因此您在上面指定的内容实际上会将 RS 设置为单个字符 [
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多