【问题标题】:OEM non printable characters in Python stringsPython 字符串中的 OEM 不可打印字符
【发布时间】:2019-09-05 09:56:08
【问题描述】:

我正在尝试移植一些将数据发送到 Universe 数据库的 Delphi 代码。为了使 DB 可以阅读文本,我们需要在 OEM 中对其进行编码。

在 Delphi 中是这样完成的:

    procedure TForm1.GenerarTablasNLS;
    var
      i: integer;
    begin
      for i := 0 to 255 do
      begin
        TablaUV_NLS[i] := AnsiChar(i);
        TablaNLS_UV[i] := AnsiChar(i);   
      end;
      // Nulo final
      TablaUV_NLS[256] := #0;
      TablaNLS_UV[256] := #0;

      OemToCharA(@TablaUV_NLS[1], @TablaUV_NLS[1]);
      CharToOemA(@TablaNLS_UV[1], @TablaNLS_UV[1]);

然后我们就这样翻译我们的文本

    function StringToUniverse(const Value: string): AnsiString;
    var
      p: PChar;
      q: PAnsiChar;
    begin
      SetLength(Result, Length(Value));
      if Value = '' then Exit;

      p := Pointer(Value);
      q := Pointer(Result);
      while p^ <> #0 do
      begin
        q^ := TablaNLS_UV[Ord(AnsiChar(p^))];
        Inc(p);
        Inc(q);
      end;
    end;

我使用存储每个字符翻译的字典在 Python 中遵循相同的逻辑


class StringUniverseDict(dict):
    def __missing__(self, key):
        return key

TablaString2UV = StringUniverseDict()

def rellenar_tablas_codificacion():
    TablaString2UV['á'] = ' '       # chr(225) = chr(160)
    TablaString2UV['é'] = '‚'       # chr(233) = chr(130)
    TablaString2UV['í'] = '¡'       # chr(237) = chr(161)
    TablaString2UV['ó'] = '¢'       # chr(243) = chr(162)
    TablaString2UV['ú'] = '£'       # chr(250) = chr(163)
    TablaString2UV['ñ'] = '¤'       # chr(241) = chr(164)
    TablaString2UV['ç'] = '‡'       # chr(231) = chr(135)
    TablaString2UV['Á'] = 'µ'       # chr(193) = chr(181)
    TablaString2UV['É'] = chr(144)  # chr(201) = chr(144)     
    TablaString2UV['Í'] = 'Ö'       # chr(205) = chr(214)
    TablaString2UV['Ó'] = 'à'       # chr(211) = chr(224)
    TablaString2UV['Ñ'] = '¥'       # chr(209) = chr(165)
    TablaString2UV['Ç'] = '€'       # chr(199) = chr(128)
    TablaString2UV['ü'] = chr(129)  # chr(252) = chr(129)     

    TablaString2UV[chr(129)] = '_'  # chr(129) = chr(095)     
    TablaString2UV[chr(141)] = '_'  # chr(141) = chr(095)  
    TablaString2UV['•'] = chr(007)  # chr(149) = chr(007)  
    TablaString2UV['Å'] = chr(143)  # chr(197) = chr(143)     
    TablaString2UV['Ø'] = chr(157)  # chr(216) = chr(157)     
    TablaString2UV['ì'] = chr(141)  # chr(236) = chr(141)    

只要我使用可打印字符进行翻译,就可以“正常”工作。例如,字符串

"á é í ó ú ñ ç Á Í Ó Ú Ñ Ç"

在 Delphi 中被翻译成以下字节:

0xa0 0x20 0x82 0x20 0xa1 0x20 0xa2 0x20 0xa3 0x20 0xa4 0x20 0x87 0x20 0xb5 0x20 0xd6 0x20 0xe0 0x20 0xe9 0x20 0xa5 0x20 0x80 0xfe 0x73 0x64 0x73

(á 转换为 ' ',即 chr(160) 或十六进制的 0xA0。é 是 '‚' 或 chr(130),十六进制的 0x82,í 是 '¡',char(161) 或十六进制的 0xA1等等)

在 Python 中,当我尝试将其编码为 OEM 时,我会执行以下操作:

def convertir_string_a_universe(cadena_python):
    resultado = ''
    for letra in cadena_python:
        resultado += TablaString2UV[letra]
    return resultado

然后,获取字节

txt_registro = convertir_string_a_universe(txt_orig)
datos = bytes(txt_registro, 'cp1252')

这样我得到以下字节:

b'\xa0 \x82 \xa1 \xa2 \xa3 \xa4 \x87 \xb5 \xd6 \xe0 \xe9 \xa5 \x80 \x9a'

我的问题是这种 OEM 编码使用了不可打印的字符,例如 'É' = chr(144)(0x90 in hexa)。如果我尝试使用一个数组调用 bytes(txt_registro, 'cp1252') ,我将 'É' 翻译成 chr(0x90) 我会收到此错误:

caracteres_mal = 'Éü'
txt_registro = convertir_string_a_universe(txt_orig)
datos = bytes(txt_registro, 'cp1252')

  File "C:\Users\Hector\PyCharmProjects\pyuniverse\pyuniverse\UniverseRegister.py", line 138, in reconstruir_registro_universe
    datos = bytes(txt_registro, 'cp1252')
  File "C:\Users\Hector\AppData\Local\Programs\Python\Python36-32\lib\encodings\cp1252.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character '\x90' in position 0: character maps to <undefined>

如何在不引发此 UnicodeEncodeError 的情况下进行此 OEM 编码?

【问题讨论】:

  • This 建议您可能应该从 cp437 或类似的解码?
  • 我建议您编辑您的问题并删除 Delphi 标签,因为您的问题与 Delphi 无关。你不需要知道 Delphi 的人:OemToChar 和 CharToOem 不是 Delphi 函数,它们是 Windows API 函数。

标签: python delphi encoding character-encoding delphi-7


【解决方案1】:

这是因为cp1252 不知道chr(0x90)。如果您尝试改用utf-8,它会起作用。

>>> chr(0x90).encode("utf8")
b'\xc2\x90'

我不明白您为什么要尝试转换为cp1252:您已应用自定义转换映射,然后使用bytes(txt_registro, 'cp1252'),您再次将结果转换为cp1552

我认为你正在寻找的是这样的:

datos = bytes(txt_orig, 'uv')

uv 是您的自定义编解码器。

所以你必须为它编写一个编码器和一个解码器(这基本上是你已经完成的)。看看https://docs.python.org/3/library/codecs.html#codecs.register 注册一个新的编解码器。您将向其注册的函数应返回文档上方描述的CodecInfo 对象。

import codecs

def buscar_a_uv(codec):
    if codec == "uv":
        return codecs.CodecInfo(
            convertir_string_a_universe, convertir_universe_a_string, name="uv")
    else:
        return None

codecs.register(buscar_a_uv)
datos = bytes(txt_orig, 'uv')

编辑

编码器/解码器函数应该返回字节,因此您需要稍微更新convertir_string_a_universe

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多