4

私はメールを扱っていますが、名前と件名は次のようにqエンコードされることがあります。

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

Rubyでそれらをデコードする方法はありますか?TMailがそれを処理する必要があるようですが、それは実行していません。

4

7 に答える 7

7

私はこれを使用して電子メールの件名を解析します。

次のことを試すことができます。

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
于 2011-12-02T22:02:14.677 に答える
3

Rubyには、Quoted-Printable文字列をデコードするメソッドが含まれています。

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

しかし、これは文字列全体(最初の部分を含む)では機能しないようです=?UTF-8?Q?。ただし、そこから解決できるかもしれません。

于 2010-08-13T04:50:01.177 に答える
2

これはかなり古い質問ですが、TMail :: Unquoter(またはその新しい化身のMail :: Encodings)も同様に機能します。

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

また

Mail::Encodings.unquote_and_convert_to( str, 'utf-8' )
于 2012-05-07T18:56:15.110 に答える
0

行ごとのデコード:

line.unpack("M")

STDINまたはファイル提供のエンコードされた文字列の入力をデコードされた出力に変換します。

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
于 2017-02-22T20:04:55.537 に答える
0

これは、電子メールをテストしたい人に役立つかもしれません。delivery.html_partは通常エンコードされますが、を使用してストレートHTML本文にデコードできます.decoded

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
于 2018-07-24T15:28:59.340 に答える
0

最も効率的で最新のソリューションは、 Mailgemvalue_decodeの方法を使用しているようです。

> Mail::Encodings.value_decode("=?UTF-8?Q?Greg_of_Google?=")
=> "Greg of Google"

https://www.rubydoc.info/github/mikel/mail/Mail/Encodings#value_decode-class_method

于 2020-05-19T11:27:11.157 に答える
0

以下は、傾斜している場合にカットアンドペーストできるRubyコードです。rubyを使用して直接実行すると、テストが実行されますruby ./copy-pasted.rb。コードで行われているように、私はこのモジュールをStringコアクラスの改良版として使用します。

解決策に関するいくつかの意見:

  1. 他のソリューション.gsub('_', ' ')は、解凍された文字列に対して実行されます。ただし、これが正しいとは思わないため、文字セットによっては誤ったデコードが発生する可能性があります。 RFC2047セクション4.2(2)は、「_常に16進数を表す」と示し20ているため、最初に置き換え=20_から、解凍結果に依存するのが正しいようです。(これにより、実装がよりエレガントになります。)これは、関連する質問への回答でも説明されています。

  2. よりわかりやすくするために、コメントを許可するためにフリースペースモードで正規表現を記述しました(これは一般的に複雑な正規表現に役立ちます)。正規表現を調整する場合は、フリースペースモードで空白の一致が変更されることに注意してください。空白の一致は、エスケープするか、(コードのように)文字クラスとして実行する必要があります。また、regex101に正規表現を追加したので、名前付きキャプチャグループ、遅延数量詞などの説明を読んで、自分で実験することができます。

  3. 正規表現は、stringで示されているように、単一の文字列内の複数のQエンコードされたフレーズ間のスペース( ;ではなく改行)を吸収します。これは、RFC2047セクション5(1)が、複数のQエンコードされたフレーズを線形空白で互いに分離する必要があることを示しているためです。ユースケースによっては、空白を吸収することが望ましくない場合があります。TABtest_4

  4. キャプチャという名前の正規表現codeは、予期しないquoted printableコードを許可します([bBqQ]一致が発生してコードがエラーを発生させる場合を除きます。これは、テキストを処理するときに予期しない値を検出するのに役立ちます。そうでない場合は、codecaptureという名前の正規表現をに変更してください。[bBqQ]この動作が必要です(一致するものはなく、元の文字列が返されます)。

  5. ブロックRegexp.last_match内の利便性としてグローバルを利用します。gsubこれをマルチスレッドコードで使用する場合は注意が必要な場合がありますが、私はこれを考慮していません。

追加の参考資料と読み物:

require "minitest/autorun"

module QuotedPrintableDecode
  class UnhandledCodeError < StandardError
    def initialize(code)
      super("Unhandled quoted printable code: '#{code}'.")
    end
  end

  @@qp_text_regex = %r{
    =\?                # Opening literal: `=?`
    (?<charset>[^\?]+) # Character set, e.g. "Windows-1252" in `=?Windows-1252?`
    \?                 # Literal: `?`
    (?<code>[a-zA-Z])  # Encoding, e.g. "Q" in `?Q?` (`B`ase64); [BbQq] expected, others raise
    \?                 # Literal: `?`
    (?<text>[^\?]+?)   # Encoded text, lazy (non-greedy) matched, e.g. "Foo_bar" in `?Foo_bar?`
    \?=                # Closing literal: `?=`
    (?:[ ]+(?==\?))?   # Optional separating linear whitespace if another Q-encode follows
  }x                   # Free-spacing mode to allow above comments, also changes whitespace match

  refine String do
    def decode_q_p(to: "UTF-8")
      self.gsub(@@qp_text_regex) do
        code, from, text = Regexp.last_match.values_at(:code, :charset, :text)
        q_p_charset_to_charset(code, text, from, to)
      end
    end

    private

    def q_p_charset_to_charset(code, text, from, to)
      case code
        when "q", "Q"
          text.gsub("_", "=20").unpack("M")
        when "b", "B"
          text.unpack("m")
        else
          raise UnhandledCodeError.new(code)
      end.first.encode(to, from)
    end
  end
end

class TestQPDecode < Minitest::Test
  using QuotedPrintableDecode

  def test_decode_single_utf_8_phrase
    encoded = "=?UTF-8?Q?J=2E_Pablo_Fern=C3=A1ndez?="
    assert_equal encoded.decode_q_p, "J. Pablo Fernández"
  end

  def test_decoding_preserves_space_between_unencoded_phrase
    encoded = "=?utf-8?Q?Alfred_Sanford?= <me@example.com>"
    assert_equal encoded.decode_q_p, "Alfred Sanford <me@example.com>"
  end

  def test_decodinge_multiple_adjacent_phrases_absorbs_separating_whitespace
    encoded = "=?Windows-1252?Q?Foo_-_D?= =?Windows-1252?Q?ocument_World=9617=96520;_Recor?= =?Windows-1252?Q?d_People_to_C?= =?Windows-1252?Q?anada's_History?="
    assert_equal encoded.decode_q_p, "Foo - Document World–17–520; Record People to Canada's History"
  end

  def test_decoding_string_without_encoded_phrases_preserves_original
    encoded = "Contains no QP phrases"
    assert_equal encoded.decode_q_p, encoded
  end

  def test_unhandled_code_raises
    klass = QuotedPrintableDecode::UnhandledCodeError
    message = "Unhandled quoted printable code: 'Z'."
    encoded = "=?utf-8?Z?Unhandled code Z?="

    raised_error = assert_raises(klass) { encoded.decode_q_p }
    assert_equal message, raised_error.message
  end
end
于 2021-05-06T12:31:42.663 に答える