AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / computer / 问题 / 1688170
Accepted
Displayname71
Displayname71
Asked: 2021-11-19 00:46:12 +0800 CST2021-11-19 00:46:12 +0800 CST 2021-11-19 00:46:12 +0800 CST

如何在不更改任何已经是有效 UTF-8 的字符的情况下执行 ISO-8859-1 到 UTF-8 文本文件的转换

  • 772

考虑一个看起来像这样的文本文件:

This’s ISO-8859-1
This’s UTF-8

幕后,’第一行中的花括号字符被编码为 ISO-8859-1,’第二行中的相同字符被编码为 UTF-8

该文件如下所示cat -v(-v选项显示不可打印的字符):

$ cat -v testing.txt
ThisM-4s ISO-8859-1
ThisM-bM-^@M-^Ys UTF-8

目标是将文件标准化为 UTF-8,这意味着第一行需要更改,第二行不得更改。但是,如果您尝试使用 等将 ISO-8859-1 转换为 UTF-8 iconv,recode则会通过将 UTF-8’转换为乱码字符来破坏文件的第二行

这是一个使用iconv证明第二行被破坏的示例:

$ cat testing.txt | iconv -f iso-8859-1 -t utf-8
This´s ISO-8859-1
This’s UTF-8

recode行为类似,修改第二行:

$ recode iso-8859-1..utf-8 testing.txt
$ cat testing.txt
This´s ISO-8859-1
This’s UTF-8

我想做的是跳过 UTF-8´字符的转换(但仍将其传递给输出,不要将其剥离),因为它已经是 UTF-8,因此无需转换它

但我还没有找到任何方法来做到这一点

这个简化的文本文件仅用作示例——需要一个也适用于更大文件的解决方案

例如,文件可能在第 30、40、100’行包含 UTF-8 字符;以及第’50、60 和 200 行的 ISO-8859-1 字符。文件可能不包含 ISO-8859-1’字符的任何实例(在这种情况下,不需要更改文件)。可以安全地假设该文件不会在同一行上同时包含 ISO-8859-1’字符和 UTF-8’字符,如果这会使问题范围更容易。

我看了这个问题: 如何有条件地重新编码为 UTF-8?

但是它似乎没有考虑文件包含混合 ISO-8859-1 和 UTF-8 的情况

是的,我知道在同一个文件中混合编码不是一个好主意

但它已经在几年前发生了,目标是把它全部清理干净,这样就不会再有问题了

linux conversion
  • 2 2 个回答
  • 3970 Views

2 个回答

  • Voted
  1. Best Answer
    user1686
    2021-11-19T01:40:18+08:002021-11-19T01:40:18+08:00

    Python 的 UTF-8 解码器可以将非 UTF-8 字符作为特殊代码点 U+DC00 – U+DCFF 传递(在 UTF-8 中通常是非法的)。之后可以找到它们并将其重新解码为其他内容:

    #!/usr/bin/env python3
    import argparse
    import re
    import sys
    
    parser = argparse.ArgumentParser()
    parser.add_argument("input")
    args = parser.parse_args()
    
    with open(args.input, "rb") as fh:
        buf = fh.read()
        buf = buf.decode("utf-8", errors="surrogateescape")
        buf = re.sub(r"[\udc00-\udcff]+",
                     lambda m: (m.group(0)
                                 .encode("utf-8", errors="surrogateescape")
                                 .decode("iso8859-1")),
                     buf)
        sys.stdout.write(buf)
    

    您也可以手动操作:

    #!/usr/bin/env python3
    import argparse
    import sys
    
    parser = argparse.ArgumentParser()
    parser.add_argument("input")
    args = parser.parse_args()
    
    def decipher_runes(fh):
        curr = None
        more = 0
    
        while buf := fh.read(1):
            ch = buf[0]
            if more == 0:
                # Expect a UTF-8 leading byte
                curr = bytearray([ch])
                if   ch & 0b10000000 == 0b00000000: more = 0
                elif ch & 0b11100000 == 0b11000000: more = 1
                elif ch & 0b11110000 == 0b11100000: more = 2
                elif ch & 0b11111000 == 0b11110000: more = 3
                elif ch & 0b11111100 == 0b11111000: more = 4
                elif ch & 0b11111110 == 0b11111100: more = 5
                else:                               more = -1
            else:
                # Expect a continuation byte
                curr.append(ch)
                if ch & 0b11000000 == 0b10000000: more -= 1
                else:                             more = -1
    
            if more < 0:
                more = 0
                yield curr.decode("iso8859-1")
            elif more == 0:
                yield curr.decode("utf-8")
    
        if more:
            yield curr.decode("iso8859-1")
    
    with open(args.input, "rb") as fh:
        for ch in decipher_runes(fh):
            sys.stdout.write(ch)
    
    • 1
  2. phuclv
    2022-04-09T10:04:53+08:002022-04-09T10:04:53+08:00

    .NET 允许您在默认选项旁边为无效字符创建自定义编码器/解码器(对无效字符抛出异常或将其替换为用户指定的字符串),因此您可以使用任何基于 .NET 的语言并编写自己的解码器进行转换ISO-8859-1 字符转换为 UTF-8。为此,我编写了一个简单的 PowerShell 脚本。如果没有,请将 PowerShell 安装到 Linux并将以下脚本另存为convert.ps1

    class Decoder88591FallbackBuffer : System.Text.DecoderFallbackBuffer {
        [char]$c; [int]$idx # Internal decoder state
        
        Decoder88591FallbackBuffer() { $this.Reset() }
        
        [bool] Fallback([byte[]]$bytesUnknown, [int]$index) {
            $this.idx = 1; $this.c = [char]::ConvertFromUtf32($bytesUnknown[0])
            return $true
        }
    
        [char] GetNextChar() {
            if ($this.idx -eq 1) {
                $this.idx = 2; return $this.c
            }
            return 0
        }
    
        [bool] MovePrevious() {
            if ($this.idx -eq 2) { $this.idx = 1; return $true }
            return $false
        }
    
        [int] get_Remaining() {
            if ($this.idx -eq 0) {
                if ($this.c -eq 0) { return 0 } else {return 1 }
            }
            return 0
        }
    
        [void] Reset() { $this.c = 0; $this.idx = 0 }
    }
    
    class Decoder88591Fallback : System.Text.DecoderFallback {
        Decoder88591Fallback() {}
        
        [Text.DecoderFallbackBuffer] CreateFallbackBuffer() {
            return [Decoder88591FallbackBuffer]::new();
        }
    
        [int] get_MaxCharCount() { return 1; }
    }
    
    $enc = [Text.Encoding]::GetEncoding(65001, `
        [Text.EncoderReplacementFallback]::new(), [Decoder88591Fallback]::new())
    if ($PSVersionTable.PSVersion -ge [version]"6.0") {
        $content = Get-Content -AsByteStream -Raw $args[0]
    } else {
        $content = Get-Content -Encoding Byte -Raw $args[0]
    }
    Set-Content -Path $args[1] -Encoding UTF8 -Value ($enc.GetString($content))
    

    然后运行命令为

    ./convert.ps1 testing.txt testing_out.txt
    

    如果您想让它适用于 Windows-1252,那么只需更改[char]::ConvertFromUtf32($bytesUnknown[0])为[Text.Encoding]::GetEncoding(1252).GetString($bytesUnknown)[0]

    样本输出:

    $ cat -v testing2.txt
    ThisM-4s ISO-8859-1
    
    Bx M-0 M-1 M-2 M-3 M-4 M-5 M-6 M-7 M-8 M-9 M-:M-;毫米-?
    Cx M-@ MA MB MC MD ME MF MG MH MI MJ MK ML MM MN MO
    Dx MP MQ MR MS MT MU MV MW MX MY MZ M-[ M-\ M-] M-^ M-_
    Ex M-` Ma Mb Mc Md Me Mf Mg Mh Mi Mj Mk Ml Mm Mn Mo
    Fx Mp Mq Mr Ms Mt Mu Mv Mw Mx My Mz M-{ M-| M-} M-~ M-^?
    
    ThisM-bM-^@M-^Ys UTF-8
    
    Bx M-BM-0 M-BM-1 M-BM-2 M-BM-3 M-BM-4 M-BM-5 M-BM-6 M-BM-7 M-BM-8 M-BM- 9 M-BM-:M-BM-;M-BM- M-BM-?
    Cx M-CM-^@ M-CM-^A M-CM-^B M-CM-^C M-CM-^D M-CM-^E M-CM-^F M-CM-^G M -CM-^H M-CM-^I M-CM-^J M-CM-^K M-CM-^L M-CM-^M M-CM-^N M-CM-^O
    Dx M-CM-^P M-CM-^Q M-CM-^R M-CM-^S M-CM-^T M-CM-^U M-CM-^V M-CM-^W M -CM-^X M-CM-^Y M-CM-^Z M-CM-^[ M-CM-^\ M-CM-^] M-CM-^^ M-CM-^_
    前 M-CM- M-CM-!M-CM-" M-CM-# M-CM-$ M-CM-% M-CM-& M-CM-' M-CM-( M-CM-) M-CM-* M-CM-+ M-CM-, M-CM-- M-CM-. M-CM-/
    Fx M-CM-0 M-CM-1 M-CM-2 M-CM-3 M-CM-4 M-CM-5 M-CM-6 M-CM-7 M-CM-8 M-CM- 9 M-CM-:M-CM-;M-CM- M-CM-?
    
    $ cat testing2_out.txt
    这是 ISO-8859-1
    
    Bx ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿
    Cx À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï
    Dx Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß
    Ex à á â ã ä å æ ç è é ê ë ì í î ï
    Fx ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ
    
    这是 UTF-8
    
    Bx ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿
    Cx À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï
    Dx Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß
    Ex à á â ã ä å æ ç è é ê ë ì í î ï
    Fx ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ
    

    请注意,od -cor hd(默认情况下包含在大多数 Linux 发行版中)会比cat -v它们更容易检查字节值要好得多

    $高清测试.txt
    00000000 54 68 69 73 b4 73 20 49 53 4f 2d 38 38 35 39 2d |This.s ISO-8859-|
    00000010 31 0a 54 68 69 73 e2 80 99 73 20 55 54 46 2d 38 |1.这...s UTF-8|
    00000020 0a 0a |..|
    00000022
    
    $ od -c 测试.txt
    0000000 T his 264 s ISO - 8 8 5 9 -
    0000020 1 \n 342 200 231 s UTF - 8
    0000040 \n \n
    0000042
    

    欲了解更多信息,请阅读

    • 有没有像“用户定义的编码后备”这样的东西
    • 实施自定义后备策略
    • 1

相关问题

  • 如何让我的 Linux 机器看起来像是在运行 Windows?

  • 对于 cp 或 mv,是否有等同于 cd - 的东西?

  • 以 root 身份运行 docker 容器

  • 如何在域和 Linux 活动目录中启用指纹传感器

  • 如何在CentOS 7 中将Ctrl+C 永久更改为Ctrl+K?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何减少“vmmem”进程的消耗?

    • 11 个回答
  • Marko Smith

    从 Microsoft Stream 下载视频

    • 4 个回答
  • Marko Smith

    Google Chrome DevTools 无法解析 SourceMap:chrome-extension

    • 6 个回答
  • Marko Smith

    Windows 照片查看器因为内存不足而无法运行?

    • 5 个回答
  • Marko Smith

    支持结束后如何激活 WindowsXP?

    • 6 个回答
  • Marko Smith

    远程桌面间歇性冻结

    • 7 个回答
  • Marko Smith

    子网掩码 /32 是什么意思?

    • 6 个回答
  • Marko Smith

    鼠标指针在 Windows 中按下的箭头键上移动?

    • 1 个回答
  • Marko Smith

    VirtualBox 无法以 VERR_NEM_VM_CREATE_FAILED 启动

    • 8 个回答
  • Marko Smith

    应用程序不会出现在 MacBook 的摄像头和麦克风隐私设置中

    • 5 个回答
  • Martin Hope
    Saaru Lindestøkke 为什么使用 Python 的 tar 库时 tar.xz 文件比 macOS tar 小 15 倍? 2021-03-14 09:37:48 +0800 CST
  • Martin Hope
    CiaranWelsh 如何减少“vmmem”进程的消耗? 2020-06-10 02:06:58 +0800 CST
  • Martin Hope
    Jim Windows 10 搜索未加载,显示空白窗口 2020-02-06 03:28:26 +0800 CST
  • Martin Hope
    v15 为什么通过电缆(同轴电缆)的千兆位/秒 Internet 连接不能像光纤一样提供对称速度? 2020-01-25 08:53:31 +0800 CST
  • Martin Hope
    andre_ss6 远程桌面间歇性冻结 2019-09-11 12:56:40 +0800 CST
  • Martin Hope
    Riley Carney 为什么在 URL 后面加一个点会删除登录信息? 2019-08-06 10:59:24 +0800 CST
  • Martin Hope
    zdimension 鼠标指针在 Windows 中按下的箭头键上移动? 2019-08-04 06:39:57 +0800 CST
  • Martin Hope
    jonsca 我所有的 Firefox 附加组件突然被禁用了,我该如何重新启用它们? 2019-05-04 17:58:52 +0800 CST
  • Martin Hope
    MCK 是否可以使用文本创建二维码? 2019-04-02 06:32:14 +0800 CST
  • Martin Hope
    SoniEx2 更改 git init 默认分支名称 2019-04-01 06:16:56 +0800 CST

热门标签

windows-10 linux windows microsoft-excel networking ubuntu worksheet-function bash command-line hard-drive

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve