我怎样从文件中删除非ASCII字符?
如果您想使用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 会在进行更改之前将输入文件复制到 filename.bak
。在这个例子中,我省略了创建备份,因为我期望你会使用版本控制 :)
-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/.../.../
运算符对输入文件的每一行操作一次。[]
中间的内容都是括号表达式。这部分可能是这个示例中最复杂的部分,我们将在最后详细讨论它。g
是替换操作符的第四个参数,它是一个修饰符标志。 g
标志指定替换应在输入中的所有匹配项上全局进行。 如果没有此标志,则仅替换第一个实例。 其他可能的标志是用于不区分大小写的匹配的i
,只与多行字符串有关的s
和m
(我们这里只有单行字符串),指定模式应预编译的o
(对于长文件可能会有用),以及指定模式可以包括空格和注释以使其更易读的x
(但如果情况如此,我们不应该将程序编写为单行)。
filename
这是包含非 ASCII 字符的输入文件,我们希望去除它们。
[^[:ascii:]]
现在让我们更详细地讨论 [^[:ascii:]]
。
如上所述,正则表达式中的 []
指定一个方括号表达式,它告诉正则表达式引擎在输入中匹配一个字符,该字符匹配表达式内字符集合中的任何一个字符。 因此,例如,[abc]
将匹配 a
、b
或 c
中的任何一个,并且只会匹配一个字符。 使用 ^
作为第一个字符可以反转匹配,因此 [^abc]
将匹配任何不是 a
、b
或 c
的单个字符。
那么方括号表达式中的 [:ascii:]
是什么意思呢?
如果您有一个基于 unix 的系统,请在命令行上运行 man 7 re_format
来阅读 man 页面。 如果没有,请阅读在线版本
[:ascii:]
是表示整个 ascii
字符集的字符类,但这种字符类只能在方括号表达式内使用。 使用它的正确方式是 [[:ascii:]]
,并且可以像上面的 abc
案例一样否定它,或者与其他字符组合在方括号表达式中,因此例如,[éç[:ascii:]]
将匹配所有 ascii 字符以及非 ascii 的 é
和 ç
,而 [^éç[:ascii:]]
将匹配所有不是 ascii 且也不是 é
或 ç
的字符。
^
移动到内部的 []
中,这是不正确的。[:ascii:]
标记是一个完整的标记,不能以任何方式修改。 - bluesmoontr -dc [:graph:][:cntrl:] < input-file > cleaned-file
假设你想保留“控制字符”和“可打印字符”,就像你想要的那样。根据需要进行微调。
perl -pe's/[[:^ascii:]]//g' < input.txt > output.txt
您可以编写如下的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