DOBON.NETプログラミング道掲示板

[ 最新記事及び返信フォームをトピックトップへ ]

■34079 / inTopicNo.1)  文字列を8bit に戻してから UTF-8 に変換する方法
  
□投稿者/ sato 一般人(1回)-(2019/01/04(Fri) 13:29:42)
  • アイコン環境/言語:[windows10 MicrosoftVisualStudio Community2017] 
    分類:[.NET] 

    例えば "漢字" という文字列の場合、UTF-8 では e6 bc a2 e5 ad 97 といったデータになりますが、「あるDLL」を用いて"漢字" という文字列を取得すると、00e6 00bc 00a2 00e5 00ad 0097 と 16bit 化してstring変数に格納しています。

    あるDLLの使い方としては、例えば、
    Dim str1 As String=「あるDLL("漢字")」
    としたとき、変数str1の内容が、00e6 00bc 00a2 00e5 00ad 0097 となります。

    「あるDLL」のヘルプによりますと、「 8bit に戻してから UTF-8 として扱います。ユーザーが独自にデコードする場合、バイト文字列に戻してから文字コードを UTF-8 として扱ってください。」とありました。

    そこで、以下のようなコードで試しましたが、変数str2の内容が文字化けになります。

    「バイト文字列に戻してから文字コードを UTF-8 」にする方法、ご教示いただけますでしょうか?
    よろしくお願いします。


    Dim str1 As String=「あるDLL("漢字")」

    Dim bytesData As Byte() = System.Text.Encoding.UTF8.GetBytes(str1)
    Dim str2 As String = System.Text.Encoding.UTF8.GetString(bytesData)


マルチポストを報告
違反を報告
引用返信 削除キー/
■34080 / inTopicNo.2)  Re[1]: 文字列を8bit に戻してから UTF-8 に変換する方法
□投稿者/ Hongliang 大御所(547回)-(2019/01/04(Fri) 13:50:44)
  • アイコン確認ですが、その「あるDLL」(の関数)で返される文字列は、
    ・UTF-8でエンコードしたバイト配列を、
    ・1バイトずつ区切って、
    ・各バイトを16進4桁で表現し、
    ・それぞれの前半2桁は00固定であり、
    ・区切り文字には半角空白文字1文字を使用している
    で問題ないですかね?

    Dim bytes As Byte() = Array.ConvertAll(str1.Split(" "c), Function(h) Convert.ToByte(h, 16))
違反を報告
引用返信 削除キー/
■34081 / inTopicNo.3)  Re[2]: 文字列を8bit に戻してから UTF-8 に変換する方法
□投稿者/ sato 一般人(2回)-(2019/01/04(Fri) 15:20:51)
  • アイコンNo34080に返信(Hongliangさんの記事)
    > 確認ですが、その「あるDLL」(の関数)で返される文字列は、
    > ・UTF-8でエンコードしたバイト配列を、
    > ・1バイトずつ区切って、
    > ・各バイトを16進4桁で表現し、
    > ・それぞれの前半2桁は00固定であり、
    > ・区切り文字には半角空白文字1文字を使用している
    > で問題ないですかね?
    お返事ありがとうございます。
    質問の文章が下手で、正確に伝えられていませんでした。お手数をおかけします。

    「あるDLL」(の関数)で返される文字列の文字コードを解析しますと、00e6 00bc 00a2 00e5 00ad 0097というコードで、文字列が「00e6 00bc・・・」というものではないです。
    この文字列を
    Using writer As StreamWriter = New StreamWriter("ファイル名.txt")
    writer.Write(str1)
    writer.Close()
    End Using
    で保存し、バイナリエディタで「ファイル名.txt」を開くと、00 e6 00 bc 00 a2 00 e5 00 ad となるということです。

    「あるDLL」(の関数)で返される文字列は、UTF-8 の 8bit データを UTF-16 の 16bit データとした文字列、という説明で伝わりますでしょうか?

    よろしくお願いします。



違反を報告
引用返信 削除キー/
■34082 / inTopicNo.4)  Re[3]: 文字列を8bit に戻してから UTF-8 に変換する方法
□投稿者/ Hongliang 大御所(548回)-(2019/01/06(Sun) 20:11:13)
  • アイコン
    StreamWriterは既定ではUTF-8で出力するのでそういう出力になることはあり得ませんが、 
    多分こういうことかなと思います。
    
    String.Join(" ", Array.ConvertAll(str1.ToCharArray(), Function (c) AscW(c).ToString("x4")))
    ' case1: 00e6 00bc 00a2 00e5 00ad 0097
    ' case2: 0000 00e6 0000 00bc 0000 00a2 0000 00e5 0000 00ad 0000 0097
    
    であればこんな感じになりますね。
    case1:
    Dim bytes As Byte() = Array.ConvertAll(str1.ToCharArray(), Function(c) CByte(AscW(c)))
    
    case2:
    Dim bytes As Byte() = str1.Where(Function (c, i) (i Mod 2) = 1).Select(Function(c) CByte(AscW(c))).ToArray()

違反を報告
引用返信 削除キー/
■34084 / inTopicNo.5)  Re[1]: 文字列を8bit に戻してから UTF-8 に変換する方法
□投稿者/ 魔界の仮面弁士 大御所(1186回)-(2019/01/07(Mon) 11:55:41)
  • アイコンNo34079に返信(satoさんの記事)
    > Dim str1 As String=「あるDLL("漢字")」
    > Dim bytesData As Byte() = System.Text.Encoding.UTF8.GetBytes(str1)
    > Dim str2 As String = System.Text.Encoding.UTF8.GetString(bytesData)

    Imports System.Text
    Imports System.IO
    Module Module1
     Sub Main()
      ' ここはテスト用のコードなので無視して OK
      Dim chars(5) As Char
      chars(0) = ChrW(&H00E6) 'U+00E6: Latin Small Letter Ae
      chars(1) = ChrW(&H00BC) 'U+00BC: Vulgar Fraction One Quarter
      chars(2) = ChrW(&H00A2) 'U+00A2: Cent Sign
      chars(3) = ChrW(&H00E5) 'U+00E5: Latin Small Letter A with Ring Above
      chars(4) = ChrW(&H00AD) 'U+00AD: Soft Hyphen (SHY)
      chars(5) = ChrW(&H0097) 'U+0097: <End of Guarded Area> (EPA)

      Dim str0 As String = New String(chars)
      MsgBox(str0, vbInformation)

      'Dim str1 As String=「あるDLL("漢字")」の代用
      Dim str1 As String = Encoding.Unicode.GetString(New Byte() {&HE6, &H0, &HBC, &H0, &HA2, &H0, &HE5, &H0, &HAD, &H0, &H97, &H0})

      'str0 と str1 が同じ文字列であることの確認
      MsgBox(str0.Equals(str1))

      'ファイルとして吐き出してみる
      Using writer As New StreamWriter("D:\utf-8.txt")
       writer.Write(str1)
       writer.Close()
      End Using
      Using writer As New StreamWriter("D:\utf-16.txt", False, New UnicodeEncoding(False, False))
       writer.Write(str1)
       writer.Close()
      End Using
      Using writer As New StreamWriter("D:\utf-16BE.txt", False, New UnicodeEncoding(True, False))
       writer.Write(str1)
       writer.Close()
      End Using


      '★ 本題:これでどうでしょう ★
      Dim str2 As String = Encoding.UTF8.GetString(str1.Select(Function(c) CByte(AscW(c))).ToArray())
      MsgBox(str2, vbInformation) '「漢字」
     End Sub
    End Module




    No34081に返信(satoさんの記事)
    > Using writer As StreamWriter = New StreamWriter("ファイル名.txt")
    > writer.Write(str1)
    上記の構文では、変数 str1 は As String または As Char() であり、
    かつ、StreamWriter は BOM 無しの UTF-8 エンコードであることを意味しますね。

    だとしたら
    > 00 e6 00 bc 00 a2 00 e5 00 ad となるということです。
    これはありえないはず。(末尾の 00 97 が欠落している点を抜きにしても)


    もしも本当に UTF-8 として保存された文字列データだとしたら、バイナリとしてみた際に
    &HE6 の後ろには &H80〜&HBF の範囲のバイナリが 2 つなければなりません。

    UTF-8 においては、各バイトの最上位ビットでマルチバイト文字かどうかが判断されるためです。
     U+0000〜U+007F は 00000000,0xxxxxxx → 1 バイト文字: &B0xxxxxxx
     U+0080〜U+07FF は 00000xxx,xxyyyyyy → 2 バイト文字: &B110xxxxx, &B10yyyyyy
     U+0800〜U+FFFF は xxxxyyyy,yyzzzzzz → 3 バイト文字: &B1110xxxx, &B10yyyyyy, &B10zzzzzz
    U+10000〜U+1FFFFFは 00000000,000wwwxx,xxxxyyyy,yyzzzzzzz → 4 バイト文字: &B11110www, &B10xxxxxx, &B10yyyyyy, &B10zzzzzz



    Imports System.Text
    Module Module1
     Sub Main()
      Dim rawBinary1 As Byte() = {&H0, &HE6, &H0, &HBC, &H0, &HA2, &H0, &HE5, &H0, &HAD, &H0, &H97}
      Dim rawBinary2 As Byte() = {&HE6, &H0, &HBC, &H0, &HA2, &H0, &HE5, &H0, &HAD, &H0, &H97, &H0}

      Dim enc1 = Encoding.GetEncoding("UTF-8", New EncoderExceptionFallback(), New DecoderExceptionFallback())
      Dim enc2 = Encoding.GetEncoding("UTF-16LE", New EncoderExceptionFallback(), New DecoderExceptionFallback())
      Dim enc3 = Encoding.GetEncoding("UTF-16BE", New EncoderExceptionFallback(), New DecoderExceptionFallback())

      For Each enc In {enc1, enc2, enc3}
       Console.WriteLine(enc.BodyName)
       For Each binary In {rawBinary1, rawBinary2}
        Try
         Console.WriteLine(enc.GetString(binary))
        Catch ex As DecoderFallbackException
         '★ UTF-8 として解釈すると、E6 の部分でデコードに失敗してしまう ★
         Console.Error.WriteLine(ex.Message)
        End Try
       Next
       Console.WriteLine("-------------")
      Next
      Console.ReadKey()
     End Sub
    End Module



    > 「あるDLL」を用いて"漢字" という文字列を取得すると、00e6 00bc 00a2 00e5 00ad 0097 と 16bit 化してstring変数に格納しています。
    > 「あるDLL」(の関数)で返される文字列は、UTF-8 の 8bit データを UTF-16 の 16bit データとした文字列、という説明で伝わりますでしょうか?
    "漢字" を UTF-8 でエンコードすると E6 BC A2 E5 AD 97 なので、それを 16bit 化しているわけですね。


    × StreamWriter で (既定の) UTF-8 として出力したときに「00 e6 00 bc 00 a2 ……」になる
    × StreamWriter で UTF-16 として出力したときに「00 e6 00 bc 00 a2 ……」になる
    ○ StreamWriter で UTF-16 として出力したときに「e6 00 bc 00 a2 00 ……」になる
    ○ StreamWriter で UTF-16 として出力したときに「00e6 00bc 00a2 ……」になる
違反を報告
引用返信 削除キー/
■34085 / inTopicNo.6)  Re[2]: 文字列を8bit に戻してから UTF-8 に変換する方法
□投稿者/ 魔界の仮面弁士 大御所(1187回)-(2019/01/07(Mon) 13:55:49)
  • アイコンNo34084に追記(魔界の仮面弁士の記事)
    > '★ 本題:これでどうでしょう ★
    > Dim str2 As String = Encoding.UTF8.GetString(str1.Select(Function(c) CByte(AscW(c))).ToArray())
    > MsgBox(str2, vbInformation) '「漢字」


    '☆ 念のため:エンディアンが逆だった場合 ☆
    Dim str2 As String = Encoding.UTF8.GetString(str1.Select(Function(c) CByte(AscW(c) >> 8)).ToArray())
違反を報告
引用返信 削除キー/
■34087 / inTopicNo.7)  Re[3]: 文字列を8bit に戻してから UTF-8 に変換する方法
□投稿者/ sato 一般人(3回)-(2019/01/09(Wed) 12:45:45)
  • アイコンHongliang 様
    魔界の仮面弁士 様

    ご教示、ありがとうございます。
    ase1:
    Dim bytes As Byte() = Array.ConvertAll(str1.ToCharArray(), Function(c) CByte(AscW(c)))

    Dim str2 As String = Encoding.UTF8.GetString(str1.Select(Function(c) CByte(AscW(c))).ToArray())

    両方のコード、どちらも、こちらの希望の通り動作しました。

    文字コードとその変換について理解が進んでいなく、また、説明がうまくなく、お手数をおかけしました。

    背景を簡単に説明します。
    「あるDLL」は、email受信用のDLLで、その関数はメール本文です。
    メールをeml形式で保存するのが目的です。
    そのDLLですが、送られてくるメールの文字コードがUTF8の場合、
    質問のような処理が必要とのことをでした。

解決み!
違反を報告
引用返信 削除キー/



トピック内ページ移動 / << 0 >>

このトピックに書きこむ

Mode/  Pass/


- Child Tree -