如何检测文本文件中无效的 utf8 unicode/二进制

2024-11-12 08:36:00
admin
原创
20
摘要:问题描述:我需要检测包含无效(非 ASCII)utf-8、Unicode 或二进制字符的损坏文本文件。�>t�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿...

问题描述:

我需要检测包含无效(非 ASCII)utf-8、Unicode 或二进制字符的损坏文本文件。

�>t�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½o��������ï¿ï¿½_��������������������o����������������������￿����ß����������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~�ï¿ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½}���������}w��׿��������������������������������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~������������������������������������_������������������������������������������������������������������������������^����ï¿ï¿½s�����������������������������?�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½}����������ï¿ï¿½ï¿½ï¿½ï¿½y����������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½o�������������������������}��

我已尝试过:

iconv -f utf-8 -t utf-8 -c file.csv 

这会将文件从 utf-8 编码转换为 utf-8 编码,并-c跳过无效的 utf-8 字符。但是最后那些非法字符仍然被打印出来。在 Linux 或其他语言上的 bash 中还有其他解决方案吗?


解决方案 1:

假设您将语言环境设置为 UTF-8(参见locale输出),这可以很好地识别无效的 UTF-8 序列:

grep -axv '.*' file.txt

解释(来自grep手册页):

  • -a, --text:将文件视为文本,一旦发现无效的字节序列(不是 utf8),基本可以防止 grep 中止

  • -v, --invert-match:反转显示不匹配的行的输出

  • -x '.*' ( --line-regexp ):表示匹配由任意 utf8 字符组成的完整行。

因此,将会有输出,即包含无效的非 utf8 字节序列的行(因为反转了 -v)

解决方案 2:

我会grep针对非 ASCII 字符。

使用带有 pcre 的 GNU grep(由于-P,并不总是可用。在 FreeBSD 上,您可以使用包 pcre2 中的 pcregrep),您可以执行以下操作:

grep -P "[x80-xFF]" file

参考如何在 UNIX 中 grep 所有非 ASCII 字符。因此,实际上,如果您只想检查文件是否包含非 ASCII 字符,您可以这样说:

if grep -qP "[x80-xFF]" file ; then echo "file contains ascii"; fi
#        ^
#        silent grep

要删除这些字符,您可以使用:

sed -i.bak 's/[d128-d255]//g' file

这将创建一个file.bak文件作为备份,而原始文件file中的非 ASCII 字符将被删除。 参考从csv 中删除非 ASCII 字符。

解决方案 3:

尝试这个,以便从 shell 中查找非 ASCII 字符。

命令:

$ perl -ne 'print "$. $_" if m/[x80-xFF]/'  utf8.txt

输出:

2 Pour être ou ne pas être
4 Byť či nebyť
5 是或不

解决方案 4:

您所看到的内容显然已损坏。显然,您正在显示以 Latin-1 呈现的文件;三个字符 � 代表三个字节值 0xEF 0xBF 0xBD。但这些是 Unicode替换字符 U+FFFD的 UTF-8 编码,这是尝试将未知或未定义编码的字节转换为 UTF-8 的结果,并且将正确显示为 �(如果您使用的是本世纪的浏览器,您应该会看到类似黑色菱形的东西,里面有一个问号;但这也取决于您使用的字体等)。

以上文字的屏幕截图

因此,关于“如何检测”这种特殊现象的问题很简单;Unicode 代码点 U+FFFD 是一个明显的迹象,也是您所暗示的过程中唯一可能的症状。

这些不是“无效的 Unicode”或“无效的 UTF-8”,因为这是一个有效的 UTF-8 序列,它编码了有效的 Unicode 代码点;只是这个特定代码点的语义是“这是一个无法正确表示的字符的替换字符”,即无效输入。

至于如何首先防止这种情况发生,答案非常简单,但也没有什么信息量——您需要确定错误编码发生的时间和方式,并修复产生此无效输出的过程。

要删除 U+FFFD 字符,请尝试类似

perl -CSD -pe 's/x{FFFD}//g' file

但同样,正确的解决方案是首先不要产生这些错误的输出。

要真正回答有关如何仅删除无效代码点的问题,请尝试

iconv -f UTF-8 -t UTF-8//IGNORE broken-utf8.txt >fixed-utf8.txt

(您没有透露示例数据的编码。它可能有额外的损坏。如果您向我们展示的是数据的 UTF-8 呈现的复制/粘贴,则它已被“双重编码”。换句话说,有人拿走了——如上所述已经损坏的——UTF-8 文本并告诉计算机将其从 Latin-1 转换为 UTF-8。撤消它很容易;只需将其“转换回”Latin-1。您获得的应该是多余的错误转换之前的原始 UTF-8 数据。

iconv -f utf-8 -t latin-1 mojibake-utf8.txt >fixed-utf8.txt

另请参阅mojibake。)

解决方案 5:

... 我正在尝试检测文件是否有损坏的字符。我也想删除它们。

我编写的 ugrep 实用程序使用 Unicode 扩展了 grep。使用 ugrep 只需一行:

ugrep -q -e "." -N "p{Unicode}" file.csv && echo "file is corrupted"

这与任何字符匹配,-e "."但不是有效的 Unicode,因为-N "p{Unicode}"它是“负模式”。

为了删除无效的 Unicode 字符,我们只想保留有效的字符:

ugrep "p{Unicode}" --format="%o" file.csv

这将使用自定义的方式写入结果--format="%o"

解决方案 6:

以下 C 程序检测无效的 utf8 字符。它已在 Linux 系统上测试和使用。

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>

void usage( void ) {
    printf( "Usage: test_utf8 file ...
" );

    return;
}
    
int line_number = 1;
int char_number = 1;
char *file_name = NULL;

void inv_char( void ) {
    printf( "%s: line : %d - char %d
", file_name, line_number, char_number );

    return;
}

int main( int argc, char *argv[]) {

    FILE *out = NULL;
    FILE *fh = NULL;

//    printf( "argc: %d
", argc );

    if( argc < 2 ) {
        usage();
        exit( 1 );
    }

//    printf( "File: %s
", argv[1] );

    file_name = argv[1];

    fh = fopen( file_name, "rb" );
    if( ! fh ) {
        printf( "Could not open file '%s'
", file_name );
        exit( 1 );
    }

    int utf8_type = 1;
    int utf8_1 = 0;
    int utf8_2 = 0;
    int utf8_3 = 0;
    int utf8_4 = 0;
    int byte_count = 0;
    int expected_byte_count = 0;

    int cin = fgetc( fh );
    while( ! feof( fh ) ) {
        switch( utf8_type ) {
            case 1:
                if( (cin & 0x80) ) {
                    if( (cin & 0xe0) == 0xc0 ) {
                        utf8_1 = cin;
                        utf8_type = 2;
                        byte_count = 1;
                        expected_byte_count = 2;
                        break;
                    }

                    if( (cin & 0xf0) == 0xe0 ) {
                        utf8_1 = cin;
                        utf8_type = 2;
                        byte_count = 1;
                        expected_byte_count = 3;
                        break;
                    }

                    if( (cin & 0xf8) == 0xf0 ) {
                        utf8_1 = cin;
                        utf8_type = 2;
                        byte_count = 1;
                        expected_byte_count = 4;
                        break;
                    }

                    inv_char();
                    utf8_type = 1;
                    break;
                }

                break;

            case 2:
            case 3:
            case 4:
//                printf( "utf8_type - %d
", utf8_type );
//                printf( "%c - %02x
", cin, cin );
                if( (cin & 0xc0) == 0x80 ) {
                    if( utf8_type == expected_byte_count ) {
                        utf8_type = 1;
                        break;
                    }

                    byte_count = utf8_type;
                    utf8_type++;

                    if( utf8_type == 5 ) {
                        utf8_type = 1;
                    }

                    break;
                }

                inv_char();
                utf8_type = 1;
                break;

            default:
                inv_char();
                utf8_type = 1;
                break;
        }

        if( cin == '
' ) {
            line_number ++;
            char_number = 0;
        }

        if( out != NULL ) {
            fputc( cin, out );
        }

//        printf( "lno: %d
", line_number );

        cin = fgetc( fh );
        char_number++;
    }

    fclose( fh );

    return 0;
}

解决方案 7:

此 Perl 程序应删除所有非 ASCII 字符:

 foreach $file (@ARGV) {
   open(IN, $file);
   open(OUT, "> super-temporary-utf8-replacement-file-which-should-never-be-used-EVER");
   while (<IN>) {
     s/[^[:ascii:]]//g;
     print OUT "$_";
   }
   rename "super-temporary-utf8-replacement-file-which-should-never-be-used-EVER", $file;
}

它的作用是将文件作为命令行的输入,如下所示:
perl fixutf8.pl foo bar baz

然后,对于每一行,它将每个非 ASCII 字符的实例替换为空(删除)。

然后它将修改后的这一行写出到super-temporary-utf8-replacement-file-which-should-never-be-used-EVER(命名以便它不会修改任何其他文件。)

之后,它将临时文件重命名为原始文件。

这接受所有 ASCII 字符(包括 DEL、NUL、CR 等),以防您对它们有特殊用途。如果您只想要可打印字符,只需将其替换为:ascii: 。希望
这对您有所帮助!如果这不是您要找的,请告诉我。:print:`s///`

解决方案 8:

我可能重复了其他人已经说过的话。但我认为您的无效字符仍会被打印出来,因为它们可能是有效的。通用字符集试图引用全球常用的字符,以便能够编写不依赖于特殊字符集的强大软件。

因此,我认为您的问题可能是以下问题之一 - 假设您的总体目标是处理来自 utf 文件的这种(恶意)输入:

  1. 存在无效的utf8 字符(最好称为无效字节序列- 对于这一点,我想参考相应的Wikipedia 文章)。

  2. 您当前的显示字体中缺少等效字符,这些等效字符被特殊符号替换或显示为其二进制 ASCII 等效字符(fe - 因此我想参考以下帖子:UTF-8 特殊字符不会显示)。

因此,我认为有两种方法来解决这个问题:

  1. 将所有字符从 utf8转换为可处理的内容 - fe ASCII - 可以使用 fe 完成iconv -f utf-8 -t ascii -o file_in_ascii.txt file_in_utf8.txt。但要小心,从较宽的字符空间(utf)转移到较小的字符空间可能会导致数据丢失。

  2. 正确处理 utf(8) - 这就是世界编写内容的方式。如果您认为由于任何限制性后处理步骤而可能不得不依赖 ASCII 字符,请停下来重新思考。在大多数情况下,后处理器已经支持 utf,最好找出如何使用它。您正在使您的内容面向未来且防弹。

处理 utf 可能看起来很棘手,以下步骤可以帮助您实现 utf-readyness:

  • 能够正确显示 utf 或确保您的显示堆栈(操作系统、终端等)能够显示足够的 unicode 子集(当然,这应该可以满足您的需求),这在许多情况下可以避免使用十六进制编辑器。不幸的是,utf 太大,无法用一种字体显示,但这篇文章是一个很好的起点:https: //stackoverflow.com/questions/586503/complete-monospaced-unicode-font

  • 能够过滤无效的字节序列。有很多方法可以实现这一点,这篇 ul-post 展示了其中的多种方法:过滤无效的 utf8 - 我想特别指出第 4 个答案,它建议使用uconv它来为无效序列设置回调处理程序。

  • 阅读有关unicode的更多信息。

解决方案 9:

使用 Ubuntu 22.04,我通过使用以下命令获得更正确的答案:

grep -axv -P '.*' file.txt

没有 -P 的原始答案似乎对许多亚洲字符给出了误报,例如:

    <lei:LegalName xml:lang="ko">피씨에이생명보험주식회사</lei:LegalName>
    <lei:LegalName xml:lang="ko">린드먼 부품소재 전문투자조합 1</lei:LegalName>
    <lei:LegalName xml:lang="ko">비엔피파리바 카디프손해보험 주식회사</lei:LegalName>

这些字符确实通过了实用程序的扫描isutf8

解决方案 10:

Python 3 中一个非常肮脏的解决方案

import sys
with open ("cur.txt","r",encoding="utf-8") as f:
    for i in f:
            for c in i:
                 if(ord(c)<128):
                     print(c,end="")
 

输出应为:

>two_o~}}w~_^s?w}yo}
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用