【问题标题】:Remove non-ASCII characters in a file [duplicate]删除文件中的非 ASCII 字符 [重复]
【发布时间】:2011-03-16 22:44:34
【问题描述】:

如何从文件中删除非ASCII 字符?

【问题讨论】:

  • 这里有一个非常相似的线程,询问如何在 UNIX 中的文件中查找非 ASCII 字符:stackoverflow.com/questions/3001177/…
  • 我认为上面的“重复”链接实际上与这个问题无关。这些是关于非ASCII字符的grepping,它要么显示整行,要么不显示。这个问题是关于删除所有非 ASCII 字符。我认为这个问题更相关:stackoverflow.com/questions/3337936/…

标签: unix


【解决方案1】:

如果你想使用 Perl,请这样做:

perl -pi -e 's/[^[:ascii:]]//g' filename

详细说明

假设读者不熟悉解决方案中的任何内容,以下解释涵盖了上述命令的每个部分...

  • perl

    运行 perl 解释器。 Perl 是一种编程语言,通常在所有类 Unix 系统上都可用。此命令需要在 shell 提示符下运行。

  • -p

    -p 标志告诉 perl 遍历输入文件中的每一行,在每一行上运行指定的命令(稍后描述),然后打印结果。这相当于将您的 perl 程序包装在 while(<>) { /* program... */; } continue { print; } 中。有一个类似的 -n 标志,它的作用相同,但省略了 continue { print; } 块,所以如果你想自己打印,可以使用它。

  • -i

    -i 标志告诉 perl 输入文件将被原地编辑并且输出应该返回到该文件。这对于实际修改文件很重要。省略此标志会将输出写入STDOUT,然后您可以将其重定向到新文件。

    注意,您不能省略 -i 并将 STDOUT 重定向到输入文件,因为这会在输入文件被读取之前破坏它。这就是 shell 的工作方式,与 perl 无关。 -i 标志可以智能地解决这个问题。

    Perl 和 shell 允许您将多个单个字符参数组合为一个,这就是为什么我们可以使用 -pi 而不是 -p -i

    -i 标志有一个参数,如果你想备份原始文件,它是一个文件扩展名,所以如果你使用-i.bak,那么 perl 会将输入文件复制到 @987654339 @在进行更改之前。在此示例中,我省略了创建备份,因为我希望您无论如何都会使用版本控制 :)

  • -e

    -e 标志告诉 perl 下一个参数是封装在字符串中的完整 perl 程序。如果您有一个很长的程序,这并不总是一个好主意,因为它可能会变得不可读,但是使用我们这里的单个命令程序,它的简洁性可以提高可读性。

    注意,我们不能将 -e 标志与 -i 标志结合起来,因为它们都接受一个参数,并且 perl 会假设第二个标志是参数,所以,例如,如果我们使用-ie <program> <filename>,perl 会假设<program><filename> 都是输入文件并尝试创建<program>e<filename>e,假设e 是您要用于备份的扩展名.这将失败,因为 <program> 不是真正的文件。反过来 (-ei) 也将不起作用,因为 perl 会尝试将 i 作为程序执行,这会导致编译失败。

  • s/.../.../

    这是 perl 的基于正则表达式的替换运算符。它接受四个参数。第一个出现在运算符之前,如果未指定,则使用默认值$_。第二个和第三个在/ 符号之间。第四个是在最后的/ 之后,在这种情况下是g

    • $_ 在我们的代码中,第一个参数是$_,这是 perl 中的默认循环变量。如上所述,-p 标志将我们的程序包装在while(<>) 中,这会创建一个while 循环,从输入中一次读取一行(<>)。它隐含地将此行分配给$_,如果没有指定,所有接受单个参数的命令都将使用它(例如:仅调用print; 实际上将转换为print $_;)。因此,在我们的代码中,s/.../.../ 运算符在输入文件的每一行上运行一次。

    • [^[:ascii:]] 第二个参数是要在输入字符串中搜索的模式。此模式是一个正则表达式,因此 [] 中包含的任何内容都是括号表达式。这部分可能是这个例子中最复杂的部分,所以我们将在最后详细讨论。

    • <empty string> 第三个参数是替换字符串,在我们的例子中是空字符串,因为我们要删除所有非 ascii 字符。

    • g 第四个参数是替换运算符的修饰符标志。 g 标志指定替换应该在输入中的所有匹配项中是全局的。如果没有这个标志,只有第一个实例将被替换。其他可能的标志是 i 用于不区分大小写的匹配,sm 仅与多行字符串相关(我们这里有单行字符串),o 指定应该预编译模式(这在这里可能对长文件有用),x 指定模式可以包含空格和 cmets 以使其更具可读性(但如果是这种情况,我们不应该将程序写在一行上)。

  • filename

    这是包含我们要删除的非 ascii 字符的输入文件。

[^[:ascii:]]

现在让我们更详细地讨论[^[:ascii:]]

如上所述,正则表达式中的[] 指定了一个括号表达式,它告诉正则表达式引擎匹配输入中的单个字符,该字符匹配表达式中字符集中的任何一个字符。因此,例如,[abc] 将匹配abc,它只会匹配单个字符。使用^ 作为第一个字符会反转匹配,因此[^abc] 将匹配任何不是abc 的字符。

但是括号表达式中的[:ascii:] 呢?

如果您有基于 unix 的系统可用,请在命令行运行 man 7 re_format 以阅读手册页。如果没有,read the online version

[:ascii:] 是代表整个ascii 字符集的字符类,但这种字符类只能在括号表达式内使用。使用它的正确方法是[[:ascii:]],它可以与上面的abc 情况一样被否定,或者在括号表达式中与其他字符组合,因此,例如,[éç[:ascii:]] 将匹配所有 ascii 字符以及@987654398 @ 和 ç 不是 ascii,[^éç[:ascii:]] 将匹配所有不是 ascii 的字符,也不是 éç

【讨论】:

  • 在看到您的评论之前就这样做了。
  • @bluesmoon 你能分解一下并解释发生了什么吗?
  • @JoshuaRobinson 我已经用详细的解释编辑了答案。
  • 一些人尝试编辑此答案以将^ 移动到内部[] 这是不正确的。 [:ascii:] 令牌是一个完整的令牌,不能以任何方式修改。
【解决方案2】:
tr -dc [:graph:][:cntrl:] < input-file > cleaned-file

假设您要保留“控制”字符和“可打印”字符。根据需要摆弄。

【讨论】:

  • tr -dc '\11\12\15\40-\176' &lt; input-file &gt; cleaned-file 控制字符可以包含很多东西
【解决方案3】:
perl -pe's/[[:^ascii:]]//g' < input.txt > output.txt

【讨论】:

  • 这正是我为解决问题所做的。
【解决方案4】:

你可以这样写一个C程序:

#include <stdio.h>
#include <ctype.h>

int main(int argc, char **argv)
{
   FILE *fin = fopen("source_file", "rb");
   FILE *fout = fopen("target_file", "w");
   int c;
   while ((c = fgetc(fin)) != EOF) {
       if (isprint(c))
          fputc(c, fout);
   }
   fclose(fin);
   fclose(fout);
   return 0;
}

注意:为简单起见,避免了错误检查。

编译:

$ gcc -W source_code.c -o convert

运行它:

$ ./convert

【讨论】:

    【解决方案5】:

    我的two cents:它可能不能解决你的问题,但它可能会给你一些提示。

    file 命令告诉您文件编码,即UTF、ASCII 等,iconv 可以在不同编码之间转换文件。

    【讨论】:

    • iconv 出人意料地在 xml 文件上也删除了一些其他的东西。我做了 iconv -f ascii -t ascii -c
    猜你喜欢
    • 2013-09-02
    • 2013-02-08
    • 2018-02-16
    • 2012-04-13
    • 1970-01-01
    • 2017-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多