【问题标题】:Bash : Replace special character sequenceBash:替换特殊字符序列
【发布时间】:2017-07-05 15:55:51
【问题描述】:

我有一个 UTF-8 数据文件,其中包含一些意外的 UTF-8 字符序列,会中断数据处理。我需要用空格或任何安全字符替换这个序列(数据来自外部来源):

In Hex: E2 80 8B (​)

在 bash 脚本中,如何使用命令行工具(如 sedreplace 命令)替换这些字符?我试过了:

sed 's/​/   /g' file_1 > file_2

但它不起作用(没有替换数据)。是否有一种特殊的语法可以用“hex”字符而不是 ascii 字符来指定正则表达式? 是否可以使用 3 字节序列执行“二进制”替换?

注意:文件是 UTF-8 编码的,所以我认为搜索真实序列有问题,因为不是 utf-8(3 字节 ASCII)

【问题讨论】:

  • 你能解释一下那个字节序列是“坏的UTF-8”吗?并且不使用“ascii”这个词?
  • 这对我不好,我更喜欢使用“意外”。转换过程正在删除字符。
  • 我已经搜索并找到“使用 sed 替换十六进制字符串”stackoverflow.com/questions/7760717/… 并尝试使用序列's/\xE2\x80\x8B/ /g',它似乎有效。

标签: regex bash replace sed utf-8


【解决方案1】:

如果字节确实是0xE20x800x8B,并且它是utf-8 编码的文本。

然后it means:

U+200B  ​   e2 80 8b    ZERO WIDTH SPACE

用于将某些文本中的单词保持在一起。
并且可以在 bash 中打印为:

$ printf 'zero width\U200Bspace \n'
zero widthspace 

$ printf 'zero width\U200Bspace \n' | xxd
00000000: 7a65 726f 2077 6964 7468 e280 8b73 7061  zero width...spa
00000010: 6365 200a 

你可以删除;因为 sed 实际上理解字节(而不是字符);与:

$ printf 'zero width\U200bspace\n' | sed 's/\xE2\x80\x8B/ /g'
zero width space

【讨论】:

  • 感谢您提供此信息,不知道它的存在。我可以使用您的示例中的十六进制搜索来解决替换问题(该空间在应用程序中很重要)。
【解决方案2】:

我有一个用于过滤此类符号的 PHP 函数。这不是您想要的,但它仍然可以在控制台中运行,只需安装 PHP。 将in.txt 替换为输入文件名,将out.txt 替换为输出文件名并运行脚本(php script_file.php)。 希望对你有用。

<?php
const INPUT_FILE_NAME = 'in.txt';
const OUTPUT_FILE_NAME = 'out.txt';

$finput = fopen(INPUT_FILE_NAME, 'r');
$foutput = fopen(OUTPUT_FILE_NAME, 'w');

while( ! feof($finput)) {
    $data = fgets($finput);
    fputs($foutput, strip_bad_utf8($data));
}

fclose($finput);
fclose($foutput);

function strip_bad_utf8($str)
{
    $str = sprintf('  %s  ', $str);
    $ret = '';
    for ($i = 0;$i < strlen($str);) {
        $tmp = $str{$i++};
        $ch = ord($tmp);
        if ($ch > 0x7F) {
            if ($ch < 0xC0) continue;
            elseif ($ch < 0xE0) $di = 1;
            elseif ($ch < 0xF0) $di = 2;
            elseif ($ch < 0xF8) $di = 3;
            elseif ($ch < 0xFC) $di = 4;
            elseif ($ch < 0xFE) $di = 5;
            else continue;

            for ($j = 0;$j < $di;$j++) {
                $tmp .= $ch = $str{$i + $j};
                $ch = ord($ch);
                if ($ch < 0x80 || $ch > 0xBF) continue 2;
            }
            $i += $di;
        }
        $ret .= $tmp;
    }
    return $ret;
}
?>

这里还有两个函数用于剥离 3 和 4 字节的 UTF 符号:

function strip_3bytes_utf($text)
{
    return preg_replace("#([\xE0-\xEF][\x80-\xBF]{2})#i", '', $text);
}
function strip_4bytes_utf($text)
{
    return preg_replace("#([\xF0-\xF7]{1}[\x80-\xBF]{3})#i", '', $text);
}

【讨论】:

    猜你喜欢
    • 2017-10-19
    • 1970-01-01
    • 2015-03-17
    • 1970-01-01
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    相关资源
    最近更新 更多