2016-07-13 9 views
1

私は受け入れた回答hereを使用して、ユニコードからUTF-8コードユニットに「手動で」変換しました。問題は、結果のUTF-8がバイト配列に含まれている必要があることです。可能であれば、16進数から8進数へのシフト操作を使用することで、どうすればいいですか?シフト操作を使用してコードポイントをJavaのutf-8バイト配列に変換します

私が既に持っているコードは以下の通りです:

public static void main(String[] args) 
    throws UnsupportedEncodingException, CharacterCodingException { 

    String st = "ñ"; 

    for (int i = 0; i < st.length(); i++) { 
     int unicode = st.charAt(i); 
     codepointToUTF8(unicode); 
    } 
} 

public static byte[] codepointToUTF8(int codepoint) { 
    byte[] hb = codepointToHexa(codepoint); 
    byte[] binaryUtf8 = null; 

    if (codepoint <= 0x7F) { 
     binaryUtf8 = parseRange(hb, 8); 
    } else if (codepoint <= 0x7FF) { 
     binaryUtf8 = parseRange(hb, 16); 
    } else if (codepoint <= 0xFFFF) { 
     binaryUtf8 = parseRange(hb, 24); 
    } else if (codepoint <= 0x1FFFFF) { 
     binaryUtf8 = parseRange(hb, 32); 
    } 

    byte[] utf8Codeunits = new byte[hexStr.length()]; 
    for (int i = 0; i < hexStr.length(); i++) { 
     utf8Codeunits[i] = (byte) hexStr.charAt(i); 
     System.out.println(utf8Codeunits[i]); // prints 99 51 98 49, 
     // which is the same as c3b1, the UTF-8 for ñ 
    } 

    return binaryUtf8; 
    } 


    public static byte[] codepointToHexa(int codepoint) { 
    int n = codepoint; 
    int m; 

    List<Byte> list = new ArrayList<>(); 
    while (n >= 16) { 
     m = n % 16; 
     n = n/16; 
     list.add((byte) m); 
    } 
    list.add((byte) n); 
    byte[] bytes = new byte[list.size()]; 
    for (int i = list.size() - 1; i >= 0; i--) { 
     bytes[list.size() - i - 1] = list.get(i); 
    } 

    return bytes; 
    } 

    private static byte[] parseRange(byte[] hb, int length) { 

    byte[] binarybyte = new byte[length]; 
    boolean[] filled = new boolean[length]; 

    int index = 0; 
    if (length == 8) { 
     binarybyte[0] = 0; 
     filled[0] = true; 
    } else { 
     int cont = 0; 
     while (cont < length/8) { 
     filled[index] = true; 
     binarybyte[index++] = 1; 
     cont++; 
     } 
     binarybyte[index] = 0; 
     filled[index] = true; 
     index = 8; 
     while (index < length) { 
     filled[index] = true; 
     binarybyte[index++] = 1; 
     binarybyte[index] = 0; 
     filled[index] = true; 
     index += 7; 
     } 
    } 

    byte[] hbbinary = convertHexaArrayToBinaryArray(hb); 
    int hbindex = hbbinary.length - 1; 

    for (int i = length - 1; i >= 0; i--) { 
     if (!filled[i] && hbindex >= 0) { 
     // we fill it and advance the iterator 
     binarybyte[i] = hbbinary[hbindex]; 
     hbindex--; 
     filled[i] = true; 
     } else if (!filled[i]) { 
     binarybyte[i] = 0; 
     filled[i] = true; 
     } 
    } 
    return binarybyte; 
    } 

private static byte[] convertHexaArrayToBinaryArray(byte[] hb) { 

    byte[] binaryArray = new byte[hb.length * 4]; 
    String aux = ""; 
    for (int i = 0; i < hb.length; i++) { 

     aux = Integer.toBinaryString(hb[i]); 
     int length = aux.length(); 
     // toBinaryString doesn't return a 4 bit string, so we fill it with 0s 
     // if length is not a multiple of 4 
     while (length % 4 != 0) { 
     length++; 
     aux = "0" + aux; 
     } 

     for (int j = 0; j < aux.length(); j++) { 
     binaryArray[i * 4 + j] = (byte) (aux.charAt(j) - '0'); 
     } 
    } 

    return binaryArray; 
    } 

私は適切にバイトを処理する方法がわからないので、私は私がした事は、おそらく間違っていることを知っています。次のように

+0

この宿題はありますか? 'String.getBytes(" UTF-8 ")'を使って結果を確認することができます。そしてWikipediaはビットパターン10xxxxxxなどを表示します。マスキングとシフトは魔法ではありません。 –

+0

いいえ、それは宿題ではありません。私は個人的なプロジェクトのためにコンバーターが必要であり、私はそれが効率的であることを望んでいます。私はビットパターンを知っている、彼らは私が引用したリンクにあるので。しかし、私は望む結果を得るために何をシフトさせるべきか(いつ行うのか)はわかりません。 – randombee

+0

...ええ、その宿題。証明された、テストされた、容易に利用可能なJREメソッドよりも「効率的」であることを望むのは、ちょっと重複しており、運動のように非常に匂いがします。おそらく大学の試験 - ITの学生は、車を再発明することはすごいことだと言われます。今日は...彼らのキャリアにとって恐ろしいですが、実装の詳細についての無駄な深い知識があるのか​​不思議です。 – specializt

答えて

2

UTF-8は、Unicodeコードポイントを埋める:

0xxxxxxx 
110xxxxx 10xxxxxx 
1110xxxx 10xxxxxx 10xxxxxx 
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
... (max 6 bytes) 

右端ビット数の最下位のものである場合。

static byte[] utf8(IntStream codePoints) { 
    final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    final byte[] cpBytes = new byte[6]; // IndexOutOfBounds for too large code points 
    codePoints.forEach((cp) -> { 
     if (cp < 0) { 
      throw new IllegalStateException("No negative code point allowed"); 
     } else if (cp < 0x80) { 
      baos.write(cp); 
     } else { 
      int bi = 0; 
      int lastPrefix = 0xC0; 
      int lastMask = 0x1F; 
      for (;;) { 
       int b = 0x80 | (cp & 0x3F); 
       cpBytes[bi] = (byte)b; 
       ++bi; 
       cp >>= 6; 
       if ((cp & ~lastMask) == 0) { 
        cpBytes[bi] = (byte) (lastPrefix | cp); 
        ++bi; 
        break; 
       } 
       lastPrefix = 0x80 | (lastPrefix >> 1); 
       lastMask >>= 1; 
      } 
      while (bi > 0) { 
       --bi; 
       baos.write(cpBytes[bi]); 
      } 
     } 
    }); 
    return baos.toByteArray(); 
} 

7ビットASCII以外は、ループでエンコードすることができます。

+0

基本的に、 &と0x3Fをつけてコードポイントの最後の6ビットだけを使用し、最初のビットを1に変更してプレフィックス10を作成し、6ビットを右側に移動して削除します。最後の反復では、最後のプレフィックスと同じことを行います。これは、適切なプレフィックスを使用していることを確認するために、繰り返しごとに11000000から11100000から11110000に変わります。非常に便利です、ありがとう! – randombee

+0

はいマルチバイトシーケンスでは、すべての継続バイトは01xxxxxxです。 –

+1

*標準* UTF-8は技術的に*コードポイントを 'U + 7FFFFFFF'までエンコードするために6バイトまで使用できますが、*合法的に*最大4バイトしか使用できません(Javaの* Modified * UTF- 6バイトまで)。 [RFC 3629](https://tools.ietf.org/html/rfc3629)は、UTF-8が法的に扱える最高のコードページをUTF-16が物理的にエンコードできる最高のコードポイントである「U + 10FFFF」に制限し、 Unicodeが現在定義している最高のコードポイントです。 –

関連する問題