草庐IT

mysql - 如何转换存储的误编码数据?

coder 2023-10-05 原文

我的Perl应用程序和MySQL数据库现在可以正确处理传入的UTF-8数据,但是我必须转换先前存在的数据。某些数据似乎已经编码为CP-1252,在编码为UTF-8并存储在MySQL中之前并未如此解码。我已经阅读了O'Reilly的文章Turning MySQL data in latin1 to utf8 utf-8,但是尽管它经常被引用,但这并不是一个确定的解决方案。

我已经看过Encode::DoubleEncodedUTF8Encoding::FixLatin,但是都没有处理我的数据。

到目前为止,这是我所做的:

#Return the $bytes from the DB using BINARY()
my $characters = decode('utf-8', $bytes);
my $good = decode('utf-8', encode('cp-1252', $characters));

这可以解决大多数情况,但是如果针对正确编码的记录运行,则会对其进行处理。我尝试使用Encode::GuessEncode::Detect,但是它们无法区分正确编码的记录和错误编码的记录。因此,如果在转换后找到了\x{FFFD} character,我就撤消转换。

但是,某些记录仅被部分转换。这是一个示例,其中左大括号被正确转换,但右大括号被弄乱了。
perl -CO -MEncode -e 'print decode("utf-8", encode("cp-1252", decode("utf-8", "\xC3\xA2\xE2\x82\xAC\xC5\x93four score\xC3\xA2\xE2\x82\xAC\xC2\x9D")))'

这是一个示例,其中正确的单引号没有转换:
perl -CO -MEncode -e 'print decode("utf-8", encode("cp-1252", decode("utf-8", "bob\xC3\xAF\xC2\xBF\xC2\xBDs")))'

我在这里还要处理双重编码的数据吗?我还必须做些什么来转换这些记录?

最佳答案

对于“四分”示例,几乎可以肯定是双重编码的数据。它看起来像:

通过cp1252至utf8处理两次的

  • cp1252数据,或
  • 通过cp1252运行到utf8进程的
  • utf8数据

    (自然地,两种情况看起来都相同)

    现在,这就是您所期望的,所以为什么您的代码不起作用?

    首先,我想向您介绍this table,该文件显示了从cp1252到unicode的转换。我想让您注意的重要一点是,有些字节(例如0x9D)在cp1252中无效。

    因此,当我想将cp1252写入utf8转换器时,我需要对cp1252中没有的那些字节进行处理。我能想到的唯一明智的事情是将未知字节转换为相同值的unicode字符。实际上,这似乎是发生了什么。让我们一次将“四个分数”的示例往后退一步。

    首先,由于它是有效的utf-8,因此我们使用以下代码进行解码:
    $ perl -CO -MEncode -e '$a=decode("utf-8", 
      "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
      "four score" .
      "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
      for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt
    

    这将产生以下序列的unicode代码点:
    e2 20ac 153 66 6f 75 72 20 73 63 6f 72 65 e2 20ac 9d
    

    (“fmt”是用于重新格式化文本格式的unix命令,这样我们就可以很好地处理长数据的换行符)

    现在,让我们将它们分别表示为cp1252中的一个字节,但是当Unicode字符无法在cp1252中表示时,我们只需将其替换为具有相同数值的字节即可。 (而不是默认的,而是用问号代替它)。如果我们对数据发生了正确的判断,我们应该有一个有效的utf8字节流。
    $ perl -CO -MEncode -e '$a=decode("utf-8",
      "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
      "four score" .
      "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
      $a=encode("cp-1252", $a, sub { chr($_[0]) } );
      for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt
    

    编码的第三个参数(当它是一个子变量时)告诉该如何处理无法代表的字符。

    这将产生:
    e2 80 9c 66 6f 75 72 20 73 63 6f 72 65 e2 80 9d
    

    现在,这是一个有效的utf8字节流。不能通过检查得知吗?好吧,让我们让Perl将这个字节流解码为utf8:
    $ perl -CO -MEncode -e '$a=decode("utf-8",
      "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
      "four score" .
      "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
      $a=encode("cp-1252", $a, sub { chr($_[0]) } );
      $a=decode("utf-8", $a, 1);
      for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt
    

    传递“1”作为第三个参数进行解码可确保如果字节流无效,我们的代码将发出嘶哑的声音。这将产生:
    201c 66 6f 75 72 20 73 63 6f 72 65 201d
    

    或打印:
    $ perl -CO -MEncode -e '$a=decode("utf-8",
      "\xC3\xA2\xE2\x82\xAC\xC5\x93" .
      "four score" .
      "\xC3\xA2\xE2\x82\xAC\xC2\x9D");
      $a=encode("cp-1252", $a, sub { chr($_[0]) } );
      $a=decode("utf-8", $a, 1);
      print "$a\n"'
    “four score”
    

    所以我认为完整的算法应该是这样的:
  • 从mysql获取字节流。将此分配给$ bytestream。
  • 虽然$ bytestream是有效的utf8字节流:
  • 将$ bytestream的当前值分配给$ good
  • 如果$ bytestream为全ASCII码(即每个字节小于0x80),请退出此“while ...有效的utf8”循环。
  • 将$ bytestream设置为“demangle($ bytestream)”的结果,其中demangle在下面给出。此例程撤消了我们认为该数据受到影响的cp1252-to-utf8转换器。
  • 如果不是undef,请将$ good放回数据库中。如果从未分配$ good,则假定$ bytestream是一个cp1252字节流,并将其转换为utf8。 (当然,如果步骤2中的循环未做任何更改,请优化并且不要执行此操作,等等。)


  • sub demangle {
      my($a) = shift;
      eval { # the non-string form of eval just traps exceptions
             # so that we return undef on exception
        local $SIG{__WARN__} = sub {}; # No warning messages
        $a = decode("utf-8", $a, 1);
        encode("cp-1252", $a, sub {$_[0] <= 255 or die $_[0]; chr($_[0])});
      }
    }
    

    这是基于这样的假设,即不是全ASCII的字符串实际上是utf-8的情况,实际上很少是有效的utf-8字节流。也就是说,这不是偶然发生的事情。

    编辑添加:

    请注意,不幸的是,此技术对您的“鲍勃”示例没有太大帮助。我认为该字符串也经历了两轮cp1252-to-utf8转换,但不幸的是还存在一些损坏。使用与之前相同的技术,我们首先将字节序列读取为utf8,然后查看获得的unicode字符引用的序列:
    $ perl -CO -MEncode -e '$a=decode("utf-8",
      "bob\xC3\xAF\xC2\xBF\xC2\xBDs");
      for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt
    

    这将产生:
    62 6f 62 ef bf bd 73
    

    现在,恰好对于ef bf bd这三个字节,unicode和cp1252一致。因此,在cp1252中表示此unicode代码点序列仅是:
    62 6f 62 ef bf bd 73
    

    也就是说,相同的数字序列。现在,这实际上是一个有效的utf-8字节流,但是它解码后的内容可能会让您感到惊讶:
    $ perl -CO -MEncode -e '$a=decode("utf-8",
      "bob\xC3\xAF\xC2\xBF\xC2\xBDs");
      $a=encode("cp-1252", $a, sub { chr(shift) } );
      $a=decode("utf-8", $a, 1);
      for $c (split(//,$a)) {printf "%x ",ord($c);}' | fmt
    
    62 6f 62 fffd 73
    

    也就是说,utf-8字节流,尽管是合法的utf-8字节流,仍对字符0xFFFD进行了编码,该字符通常用于“不可翻译字符”。我怀疑这里发生的事情是第一个* -to-utf8转换看到了一个无法识别的字符,并将其替换为“untranslateable”。然后无法以编程方式恢复原始字符。

    结果是,仅通过解码然后寻找0xFFFD,就无法检测字节流是否为有效的utf8(我上面给出的算法所需)。相反,您应该使用如下所示的内容:
    sub is_valid_utf8 {
      defined(eval { decode("utf-8", $_[0], 1) })
    }
    

    关于mysql - 如何转换存储的误编码数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/844432/

    有关mysql - 如何转换存储的误编码数据?的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

      我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

    4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    7. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

      我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

    8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

      我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

    9. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    10. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    随机推荐