【问题标题】:Skip/remove non-ascii character with sed使用 sed 跳过/删除非 ASCII 字符
【发布时间】:2012-01-24 04:32:53
【问题描述】:

Chip,Dirkland,DrobæSphere Inc,cdirkland@hotmail.com,美国

我一直在尝试使用 sed 来修改 .csv 中的电子邮件地址,但上面的行一直让我感到困惑,使用以下命令:

sed -i 's/[\d128-\d255]//' FILENAME

from this stackoverflow question

似乎不起作用,因为我收到“无效的排序字符”错误。

理想情况下,我根本不想更改组合的 AE 字符,我宁愿 sed 直接跳过它,因为我不是试图操纵该文本,而是操纵电子邮件地址。只要该 AE 存在,尽管它会导致我的 sed 替换在一行之后失败,删除该字符并处理整个文件即可。

有什么想法吗?

【问题讨论】:

    标签: sed


    【解决方案1】:

    为此使用awk 怎么样。我们将字段分隔符设置为空。然后循环遍历每个字符。使用if loop 来检查它是否与我们的character class 匹配。如果是,我们打印它,否则我们忽略它。

    awk -v FS="" '{for(i=1;i<=NF;i++) if($i ~ /[A-Za-z,.@ ]/) printf $i}'
    

    测试:

    [jaypal:~/Temp] echo "Chip,Dirkland,DrobæSphere Inc,cdirkland@hotmail.com,usa" | 
    awk -v FS="" '{for(i=1;i<=NF;i++) if($i ~ /[A-Za-z,.@ ]/) printf $i}'
    Chip,Dirkland,DrobSphere Inc,cdirkland@hotmail.com,usa
    

    更新:

    awk -v FS="" '{for(i=1;i<=NF;i++) if($i ~ /[A-Za-z,.@ ]/) printf $i; printf "\n"}' < datafile.csv > asciidata.csv
    

    我在循环之后添加了 printf "\n" 以保持行分开。

    【讨论】:

    • 感谢 Jaypal,如果您想处理 datafile.csv 并输出 asciidata.csv,该如何修改?
    • 如果您只想从输入文件中提取电子邮件地址,那么awk 可以轻而易举地做到这一点,而无需任何复杂的regex。让我知道结果如何。
    【解决方案2】:

    这可能对你有用(GNU sed):

    echo "Chip,Dirkland,DrobæSphere Inc,cdirkland@hotmail.com,usa" |
    sed 's/\o346/a+e/g'
    Chip,Dirkland,Droba+eSphere Inc,cdirkland@hotmail.com,usa
    

    然后做你必须做的事情,然后再做:

    echo "Chip,Dirkland,Droba+eSphere Inc,cdirkland@hotmail.com,usa" | 
    sed 's/a+e/\o346/g'
    Chip,Dirkland,DrobæSphere Inc,cdirkland@hotmail.com,usa
    

    如果您在字符串中有棘手的字符并想了解sed 如何看到它们,请使用l0 命令(请参阅here)。对于调试困难的正则表达式也非常有用。

    echo "Chip,Dirkland,DrobæSphere Inc,cdirkland@hotmail.com,usa" | 
    sed -n 'l0'
    Chip,Dirkland,Drob\346Sphere Inc,cdirkland@hotmail.com,usa$
    

    【讨论】:

    • +1 表示l0。还有另一个sedsed.py 脚本,可用here。检查patternhold 空格很有用。在这种情况下可能无济于事,但仍然是一个有用的调试工具。 :)
    • 这个 sed -n 'l0' 命令很有趣,它为公司打印的内容是:Drob\357\277\275Sphere Inc
    • 我仍然无法使用上面的示例,也许这个字符(在 Windows LibreOffice 中显示为 AE,但在其他地方没有)实际上是一个特殊字符,表示它不能用unicode表示? fileformat.info/info/unicode/char/fffd/index.htm
    • 我从来没有在这个页面上得到任何完美工作的答案,但 potong 的解决方案让我最接近,该命令提供了一些更准确的问题细节
    • 无助于删除所有非 ASCII 字符。仅有助于删除示例中给出的特定项。
    【解决方案3】:
    sed -i 's/[^[:print:]]//' FILENAME
    

    另外,这就像 dos2unix

    【讨论】:

    • 不起作用。 [:print:] 与 ASCII 不同,例如ü 是可打印的,但不是 ASCII。
    【解决方案4】:

    在这种情况下,有一种方法可以跳过非 ASCII 字符,而不用担心删除。

    LANG=C sed /someemailpattern/
    

    请参阅 https://bugzilla.redhat.com/show_bug.cgi?id=440419Will sed (and others) corrupt non-ASCII files?

    【讨论】:

      【解决方案5】:

      我来到这里尝试这个 sed 命令s/[\x00-\x1F]/ /g;,它给了我同样的错误信息。

      在这种情况下,只需从排序规则中删除 \x00 即可,生成 s/[\x01-\x1F]/ /g;

      不幸的是,上面的所有字符(包括 \x7F 和其他一些字符)似乎都被禁止使用,从这个简短的脚本可以看出:

      for (( i=0; i<=255; i++ )); do 
          printf "== $i - \x$(echo "ibase=10;obase=16;$i" | bc) =="
          echo '' | sed -E "s/[\d$i-\d$((i+1))]]//g"
      done
      

      请注意,问题仅在于使用这些字符来指定范围。您仍然可以手动或按脚本列出它们。例如。回到你的例子:

      sed -i 's/[\d128-\d255]//' FILENAME
      

      会变成

      c=; for (( i=128; i<255; i++ )); do c="$c\d$i"; done
      sed -i 's/['"$c"']//' FILENAME
      

      这将转化为:

      sed -i 's/[\d128\d129\d130\d131\d132\d133\d134\d135\d136\d137\d138\d139\d140\d141\d142\d143\d144\d145\d146\d147\d148\d149\d150\d151\d152\d153\d154\d155\d156\d157\d158\d159\d160\d161\d162\d163\d164\d165\d166\d167\d168\d169\d170\d171\d172\d173\d174\d175\d176\d177\d178\d179\d180\d181\d182\d183\d184\d185\d186\d187\d188\d189\d190\d191\d192\d193\d194\d195\d196\d197\d198\d199\d200\d201\d202\d203\d204\d205\d206\d207\d208\d209\d210\d211\d212\d213\d214\d215\d216\d217\d218\d219\d220\d221\d222\d223\d224\d225\d226\d227\d228\d229\d230\d231\d232\d233\d234\d235\d236\d237\d238\d239\d240\d241\d242\d243\d244\d245\d246\d247\d248\d249\d250\d251\d252\d253\d254\d255]//' FILENAME
      

      【讨论】:

      • "不幸的是,似乎上面的所有字符,包括 \x7F 和其他一些字符都是不允许的"。谢谢!这解释了为什么我会收到 Invalid collation character 错误。
      • 非常有助于确定 \u0000 也不能用作范围的一部分。
      【解决方案6】:

      您遇到的问题是本地问题。

      如果您想使用这样的排序规则范围,则需要更改字符类型和排序规则类型。

      这会失败,因为 \x80 -> \xff 在 utf-8 字符串中无效。 注意 \u0080 != \x80 表示 utf8。

      无论如何要让它工作就行了

      LC_ALL=C sed -i 's/[\d128-\d255]//' FILENAME
      

      这将为一个命令覆盖 LC_CTYPE 和 LC_COLLATE 并执行您想要的操作。

      【讨论】:

        猜你喜欢
        • 2023-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-29
        • 2017-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多