代码之家  ›  专栏  ›  技术社区  ›  G__

用sed剥离十六进制字节-不匹配

  •  7
  • G__  · 技术社区  · 14 年前

    我有一个包含两个非ASCII字节(0xFF和0xFE)的文本文件:

    ??58832520.3,ABC
    348384,DEF
    

    这个文件的十六进制是:

    FF FE 35 38 38 33 32 35 32 30 2E 33 2C 41 42 43 0A 33 34 38 33 38 34 2C 44 45 46
    

    巧合的是,FF和FE恰好是前导字节(它们存在于整个文件中,尽管看起来总是在一行的开头)。

    我正试图用sed去掉这些字节,但我做的似乎与它们不匹配。

    $ sed 's/[^a-zA-Z0-9\,]//g' test.csv 
    ??588325203,ABC
    348384,DEF
    
    $ sed 's/[a-zA-Z0-9\,]//g' test.csv 
    ??.
    

    主要问题:如何去掉这些字节?
    额外的问题:上面的两个regex是直接否定的,所以其中一个在逻辑上必须过滤掉这些字节,对吗?为什么这两个regex都匹配0xff和0xfe字节?

    更新 :直接剥离一个十六进制字节范围的方法(由下面的两个答案建议)似乎会从每行中剥离出第一个“合法”字节,并保留我要删除的字节:

    $sed 's/[\x80-\xff]//' test.csv
    ??8832520.3,ABC
    48384,DEF
    
    FF FE 38 38 33 32 35 32 30 2E 33 2C 41 42 43 0A 34 38 33 38 34 2C 44 45 46 0A
    

    注意每行开头缺少的“5”和“3”,文件末尾添加了新的0A。

    更大更新 :此问题似乎是系统特定的。在OSX上观察到了这个问题,但是这些建议(包括我上面的原始SED声明)和我在NetBSD上期望的一样有效。

    解决方案 :通过Perl,这一任务似乎足够简单:

    $ perl -pe 's/^\xFF\xFE//' test.csv
    58832520.3,ABC
    348384,DEF
    

    但是,我会让这个问题保持开放状态,因为这只是一个解决方法,无法解释SED的问题所在。

    7 回复  |  直到 14 年前
        1
  •  4
  •   deinst    14 年前
    sed 's/[^ -~]//g'
    

    或者正如另一个答案所暗示的那样

    sed 's/[\x80-\xff]//g'
    

    section 3.9 SED信息页的。标题为“逃亡”的章节。

    编辑 对于OSX,本机语言设置为en-u.utf-8

    尝试

    LANG='' sed 's/[^ -~]//g' myfile
    

    这在这里的OSX机器上工作,我不完全确定为什么它在UTF-8中不工作

        2
  •  3
  •   Gary    14 年前

    这将删除以特定字节ff fe开头的所有行。

    sed -e 's/\xff\xfe//g' hexquestion.txt
    

    被否定的regex不工作的原因是[]指定了一个字符类。SED假设一个特定的字符集,可能是ASCII。文件中的这些字符不是7位ASCII字符,因为它们都以F开头。SED不知道如何处理这些字符。上面的解决方案不使用字符类,因此它应该更易于在平台和字符集之间移植。

        3
  •  2
  •   Community Paul Sweatte    7 年前

    这个 FF FE 文件开头的字节称为“字节顺序标记(bom)”。它可以出现在unicode文本流的开头,以指示文本的结尾。 FF FE 以小尾数表示UTF-16

    以下是常见问题解答的摘录:

    问:我应该如何处理BOM?

    A:下面是一些要遵循的准则:

    1. 一个特定的协议(例如 .txt 文件)可能需要在某些Unicode数据流(如文件)上使用BOM。当您需要遵守这样的协议时,使用一个BOM。
    2. 有些协议允许在无标签文本的情况下使用可选的BOM。在那些情况下,
      • 如果已知文本数据流是纯文本,但编码未知,则可以将BOM用作签名。如果没有BOM,编码可以是任何东西。
      • 如果已知文本数据流是纯Unicode文本(但不是哪个endian),那么可以将bom用作签名。如果没有bom,则文本应解释为big endian。
    3. 一些面向字节的协议要求在文件开头使用ASCII字符。如果将UTF-8与这些协议一起使用,则应避免使用BOM作为编码表单签名。
    4. 如果知道数据流的精确类型(例如unicode big endian或unicode little endian),则不应使用BOM。尤其是,当数据流声明为utf-16be、utf-16le、utf-32be或utf-32le时,不能使用BOM。

    工具书类

    也见

    相关问题

        4
  •  1
  •   dawg    14 年前

    在OSX上,字节顺序标记可能是作为单个字读取的。尝试任一 sed 's/^\xfffe//g' sed 's/^\xfeff//g' 取决于endianes。

        5
  •  0
  •   schoetbi    14 年前

    您可以使用\xff\xfe获取十六进制代码,然后将其替换为Nothing。

        6
  •  0
  •   Dennis Williamson    14 年前

    要证明这不是Unicode BOM的问题,而是8位与7位字符的问题,并与区域设置相关联,请尝试以下操作:

    显示所有字节:

    $ printf '123 abc\xff\xfe\x7f\x80' | hexdump -C
    00000000  31 32 33 20 61 62 63 ff  fe 7f 80                 |123 abc....|
    

    sed 删除用户区域设置中不是字母数字的字符。请注意,删除了空格和0x7f:

    $ printf '123 abc\xff\xfe\x7f\x80'|sed 's/[^[:alnum:]]//g' | hexdump -C
    00000000  31 32 33 61 62 63 ff fe  80                       |123abc...|
    

    塞德 删除C语言环境中不是字母数字的字符。请注意,只有“123ABC”保留:

    $ printf '123 abc\xff\xfe\x7f\x80'|LANG=C sed 's/[^[:alnum:]]//g' | hexdump -C
    00000000  31 32 33 61 62 63                                 |123abc|
    
        7
  •  0
  •   bashfu    14 年前

    作为替代方案,您可以使用ed(1):

    printf '%s\n' H $'g/[\xff\xfe]/s///g' ',p' | ed -s test.csv
    
    printf '%s\n' H $'g/[\xff\xfe]/s///g' wq | ed -s test.csv  # in-place edit