有没有办法在Ruby中解码q编码的字符串?

有没有办法在Ruby中解码q编码的字符串?,ruby,email,decoding,Ruby,Email,Decoding,我在处理邮件,名字和主题有时是q编码的,比如: =?UTF-8?Q?J=2E_Pablo_Fern=C3=A1ndez?= 有没有办法用Ruby解码它们?似乎TMail应该处理它,但它没有这样做。Ruby包含一种解码引用的可打印字符串的方法: puts "Pablo_Fern=C3=A1ndez".unpack "M" # => Pablo_Fernández 但这似乎对整个字符串(包括开头的=?UTF-8?Q?部分)都不起作用。不过,也许你可以从这里开始计算出来。我用它来解析电子邮件

我在处理邮件,名字和主题有时是q编码的,比如:

=?UTF-8?Q?J=2E_Pablo_Fern=C3=A1ndez?=

有没有办法用Ruby解码它们?似乎TMail应该处理它,但它没有这样做。

Ruby包含一种解码引用的可打印字符串的方法:

puts "Pablo_Fern=C3=A1ndez".unpack "M"
# => Pablo_Fernández

但这似乎对整个字符串(包括开头的
=?UTF-8?Q?
部分)都不起作用。不过,也许你可以从这里开始计算出来。

我用它来解析电子邮件主题:

您可以尝试以下方法:

str = "=?UTF-8?Q?J=2E_Pablo_Fern=C3=A1ndez?="
if m = /=\?([A-Za-z0-9\-]+)\?(B|Q)\?([!->@-~]+)\?=/i.match(str)
        case m[2]
        when "B" # Base64 encoded
          decoded = Base64.decode64(m[3])
        when "Q" # Q encoded
          decoded = m[3].unpack("M").first.gsub('_',' ')
        else
          p "Could not find keyword!!!"
        end
        Iconv.conv('utf-8',m[1],decoded) # to convert to utf-8
end

这是一个很老的问题,但是TMail::Unquoter(或者它的新化身Mail::Encodings)也可以解决这个问题

TMail::Unquoter.unquote_and_convert_to(str, 'utf-8' )


每行解码:

line.unpack("M")
将标准输入或文件提供的编码字符串输入转换为解码输出:

if ARGV[0]
  lines = File.read(ARGV[0]).lines
else
  lines = STDIN.each_line.to_a
end

puts lines.map { |c| c.unpack("M") }.join

这可能有助于任何想要测试电子邮件的人。delivery.html\u部分通常是编码的,但可以使用
.decoded
解码为一个直接的html正文

test "email test" do
  UserMailer.confirm_email(user).deliver_now
  assert_equal 1, ActionMailer::Base.deliveries.size
  delivery = ActionMailer::Base.deliveries.last
  assert_equal "Please confirm your email", delivery.subject
  assert delivery.html_part.decoded =~ /Click the link below to confirm your email/ # DECODING HERE
end

最有效和最新的解决方案似乎是使用的
value\u decode
方法


下面是可以剪切和粘贴的Ruby代码,如果愿意的话。如果直接使用Ruby执行,它将运行测试,
Ruby./copy pasted.rb
。正如在代码中所做的那样,我使用这个模块作为对字符串核心类的细化

关于解决方案的几点意见:

  • 其他解决方案对未打包的字符串执行
    .gsub(“”,“”)
    。但是,我不认为这是正确的,并且可能会根据字符集导致错误的解码。表示“
    \uu
    始终表示十六进制
    20
    ”,因此,首先将
    =20
    替换为
    ,然后依赖解包结果似乎是正确的。(这也使实现更加优雅。)中也讨论了这一点

  • 更具指导意义的是,我以自由间距模式编写了正则表达式,以允许注释(我发现这通常对复杂正则表达式很有帮助)。如果调整正则表达式,请注意,自由间距模式会更改空白的匹配,然后必须将其转义或作为字符类执行(就像在代码中一样)。我也做了,所以您可以阅读命名捕获组、惰性量词等的解释,然后自己进行实验

  • 正则表达式将占用空间(
    ;但不会占用
    选项卡或换行符)在单个字符串中的多个Q编码短语之间,如string
    test_4
    所示。这是因为指示多个Q编码短语必须通过线性空格相互分隔。根据您的使用情况,可能不需要吸收空格

  • 名为capture的正则表达式
    code
    允许意外引用的可打印代码(而不是
    [bBqQ]
    ),以便发生匹配,并且代码可能引发错误。这有助于我在处理文本时检测意外值。将名为capture的
    code
    正则表达式更改为
    [bBqQ]
    如果不希望出现此行为。(将不存在匹配项,并返回原始字符串。)

  • 它使用全局
    Regexp.last_match
    作为
    gsub
    块中的一种便利。如果在多线程代码中使用此选项,您可能需要小心,我没有考虑到这一点

  • 其他参考资料和阅读:

    要求“小型测试/自动运行”
    模块QuotedPrintableDecode
    类未处理的代码错误<标准错误
    def初始化(代码)
    超级(“未处理的引用可打印代码:'#{code}.”)
    结束
    结束
    @@qp_text_regex=%r{
    =\?\开头文字:`=`
    (?[^\?]+)#字符集,例如“=?Windows-1252”中的“Windows-1252”`
    \?#文字:``
    (?
    [a-zA-Z])#编码,例如,`?Q`(`B`ase64)`中的“Q”;[BbQq]预期,其他人会提高
    \?#文字:``
    (?[^\?]+?)#编码文本,惰性(非贪婪)匹配,例如`?Foo_栏中的“Foo_栏”`
    \?=#结束文字:`=`
    (?:[]+(?==\?)?#如果后面跟着另一个Q编码,则可选分隔线性空白
    }x#自由间距模式允许上述注释,也会更改空格匹配
    细化字符串吗
    def解码(至:“UTF-8”)
    self.gsub(@@qp\u text\u regex)do
    代码,发件人,text=Regexp.last\u match.values\u at(:code,:charset,:text)
    q_p_字符集_to_字符集(代码、文本、发件人、收件人)
    结束
    结束
    私有的
    def q_p_charset_to_charset(代码、文本、发件人、收件人)
    案例代码
    当“q”,“q”
    text.gsub(“”,“=20”).解包(“M”)
    当“b”,“b”
    文本。解包(“m”)
    其他的
    引发未处理的代码错误。新建(代码)
    end.first.encode(到,从)
    结束
    结束
    结束
    类TestQPDecodetest "email test" do
      UserMailer.confirm_email(user).deliver_now
      assert_equal 1, ActionMailer::Base.deliveries.size
      delivery = ActionMailer::Base.deliveries.last
      assert_equal "Please confirm your email", delivery.subject
      assert delivery.html_part.decoded =~ /Click the link below to confirm your email/ # DECODING HERE
    end
    
    > Mail::Encodings.value_decode("=?UTF-8?Q?Greg_of_Google?=")
    => "Greg of Google"