使用sed跳过/删除非ASCII字符

14

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

我一直在尝试使用sed修改.csv文件中的电子邮件地址,但上面这行代码让我卡住了,我尝试过以下命令:

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

从这个stackoverflow问题获得的内容:

看起来好像不起作用,因为我收到了一个“无效的排序字符”的错误。

理想情况下,我不想改变组合的AE字符,我希望sed能够跳过它,因为我并不想操作那段文本,而是要处理电子邮件地址。但只要那个AE在里面,它就会导致我的sed替换在一行之后失败,删除该字符后,它就可以正确处理整个文件。

有什么想法吗?

6个回答

6
这可能适用于你(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命令(请参见此处)。对于调试困难的正则表达式也非常有用。

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 的脚本,可以在这里找到。用于检查 patternhold 空间。虽然在这种情况下可能没有帮助,但仍是一种有用的调试工具。 :) - jaypal singh
那个 sed -n 'l0' 命令很有趣,它对于公司的输出是:Drob\357\277\275Sphere Inc。 - xref
我仍然无法使用上面的示例使它正常工作,也许该字符(在Windows LibreOffice中显示为AE,但其他地方都没有)实际上是一个特殊字符,表示它无法用unicode表示?http://www.fileformat.info/info/unicode/char/fffd/index.htm - xref
我从这个页面上没有得到任何完美的答案,但是potong的解决方案让我最接近,并且提供的命令提供了更精确的详细信息,说明出了什么问题。 - xref
只有帮助删除示例中特定的字符,而无法帮助删除所有非ASCII字符。 - Jason C

5
sed -i 's/[^[:print:]]//' FILENAME

此外,它的作用类似于dos2unix。

不起作用。 [:print:]与ASCII不同,例如,ü可打印但不是ASCII。 - Jason C

3
你遇到的问题是本地化问题。
如果要使用类似于该范围的排序规则,您需要更改字符类型和排序规则类型。
这会失败,因为在 utf-8 字符串中 \x80 -\xff 是无效的。 请注意,对于 utf8,\u0080 != \x80。
要使此方法起作用,只需执行以下操作。
LC_ALL=C sed -i 's/[\d128-\d255]//' FILENAME

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


2
我来尝试一下这个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

1
“很不幸,似乎所有包括\x7F及以上的字符和一些其他字符都是不允许的。”谢谢!这解释了为什么我会收到“无效排序字符”错误。 - xpt
非常有帮助的是,可以识别出\u0000不能作为范围的一部分。 - MobileVet

1

0

这个可以使用 awk 来实现。我们将字段分隔符设置为空。然后循环遍历每个字符。使用一个 if 循环 来检查它是否匹配我们的 字符类。如果匹配,我们就打印它,否则忽略它。

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,应该如何修改呢? - xref
如果您只想从输入文件中提取电子邮件地址,那么 awk 可以轻松完成此操作,而无需使用任何复杂的 regex。让我知道它的运行情况如何。 - jaypal singh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接