質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.37%

文字列からバイトへの変換がうまく行かない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,040

yukkuri

score 564

現在、文字コードの変換について行っていて、文字列をバイトに変換する、というのがありました。

package org.jyl.base.io;

import java.util.ArrayList;

import javax.xml.bind.DatatypeConverter;

/**
 * Java のプリミティブ型 {@code char} を基に、
 * 複数文字コードに対応した単一文字を示すクラスです。
 * このクラスでは、Java の仕様上、文字コードとバイトを用いて
 * 文字を特定したあと、{@code UTF-16} に変換されます。
 *
 * @author とあるゆっくり
 */
public class JylChar
{
    //    文字コードごとに変換後の対応する char 値
    private final char c;

    //    文字コード
    private final CharCode code;

    //    文字を対応する文字コードに変換した際のバイト
    private final ArrayList<Byte> charbyte = new ArrayList<Byte>( 0 );

    /**
     * オブジェクトに文字とエンコードを格納します。
     *
     * @param c 文字
     * @param code エンコードの列挙
     */
    public JylChar( char c, CharCode code )
    {
        //    文字をセット
        this.c = c;
        //    文字コードを指定
        this.code = code;

        setByte( this.c );
    }

    /**
     * バイトと文字エンコードから文字を取得します。
     *
     * @param b 一文字のバイト  この中の全てのバイトを用いて文字を決定します。
     * ただし、エンコードごとに決められたサイズを超えた場合、切り捨てされることがあります。
     * @param code 文字のエンコード
     *
     * @throws IllegalArgumentException 指定エンコードと指定バイトで文字が決定しなかった場合
     */
    public JylChar( byte[] b, CharCode code )
    {
        //    文字コードを設定
        this.code = code;

        int loop = 0;        //    1 文字のバイト数( 最大 )
        if( code == CharCode.ASCII ) loop = 1;
        if( code == CharCode.EUC ) loop = 3;
        if( code == CharCode.Shift_JIS ) loop = 2;
        if( code == CharCode.Unicode ) loop = 4;
        if( code == CharCode.UTF_8 ) loop = 6;

        if( b.length > loop ){        //    文字を指定するのに必要なバイト数をオーバーした場合
            byte[] bys = new byte[ loop ];    //    オーバーしたデータを入れられた場合、計算に使われる配列
            for( int l = 0; l < bys.length; l++ ) bys[ l ] = b[ l ];
            //    バイトと文字エンコードから文字を取得
            c = setChar( bys );
        }else{
            c = setChar( b );
        }

        setByte( c );
    }

    /**
     * 指定したエンコードで指定した文字を出力するためのバイト配列を返します。
     *
     * @return 指定文字を示すバイト配列
     */
    public byte[] getBytes()
    {
        //    必要数のバイト配列を作成
        byte[] by = new byte[ charbyte.size() ];

        for( int l = 0; l < charbyte.size(); l++ ){
            by[ l ] = charbyte.get( l );
        }

        return by;
    }

    /**
     * このクラスに格納されている文字を取得します。
     *
     * @return 格納されている文字 エンコードなどによっては機種依存文字などが含まれる可能性があります。
     */
    public char getChar()
    {
        return c;
    }

    /**
     * この文字の文字コードを返します。
     * なお、文字コードを変更することはできません。
     *
     * @return このオブジェクトの文字コード
     */
    public CharCode getCharCode()
    {
        return code;
    }

    //    private のメゾッド( 公開する必要がない )  公開するとデータを壊す可能性があるため
    //    バイトをセットする
    private void setByte( char c )
    {
        String binarydata = "";        //    バイトに変換した際のデータを入れる 16 進数データ

        switch( c ){
            case '0':{
                if( code == CharCode.ASCII     ) binarydata = "30";
                if( code == CharCode.EUC       ) binarydata = "A3B0";
                if( code == CharCode.Shift_JIS ) binarydata = "824F";
                if( code == CharCode.UTF_8     ) binarydata = "EFBC90";
                if( code == CharCode.Unicode   ) binarydata = "FF10";
                break;
            }case '1':{
                if( code == CharCode.ASCII     ) binarydata = "31";
                if( code == CharCode.EUC       ) binarydata = "A3B1";
                if( code == CharCode.Shift_JIS ) binarydata = "8250";
                if( code == CharCode.UTF_8     ) binarydata = "EFBC91";
                if( code == CharCode.Unicode   ) binarydata = "FF11";
                break;
            }
        }

        //    binarydata の文字列を 16 進数に変換
        byte[] bytes = new byte[ binarydata.length() / 2 ];
        for( int l = 0; l < bytes.length; l++ ){
            //    バイト値 一旦保存用
            byte bycp = (byte)Integer.parseInt( binarydata.substring( l * 2, ( l + 1 ) * 2 ), 16 );
            bytes[ l ] = (byte)( ~bycp + 1 );
        }

        //    全てのバイトを charbyte に追加
        for( int l = 0; l < bytes.length; l++ ){ charbyte.add( bytes[ l ] );
        System.out.println( "b" + bytes[ l ] );
        }
    }

    //    バイト配列から char を取得します。
    //    引数から生成できない場合例外
    private char setChar( byte[] b )
    {
        String bdata = DatatypeConverter.printHexBinary( b );
        System.out.println( bdata );

        if( code == CharCode.UTF_8 ){
            switch( bdata ){
                case "EFBC90": return '0';
                case "EFBC91": return '1';
        }

        throw new IllegalArgumentException();
    }
}


上のソースのコンストラクタでバイトを指定して、バイトに{ 0xEF, 0xBC, 0x92 }のデータ、UTF-8というデータを与えました。そしたら、
1 を入力した、というデータは取得できたのですが、コンストラクタで呼び出しているsetByteで正しいバイトが入っていないようでした。
その当時は-の値だったので、ネットで調べ、整数値を出すことに成功しました。
しかし、現在出力されている値は 1 つめが 17 です。ただ、0xEFは 10 進数に直すと 239 なので、正しい値が入っていないようです。
//    binarydata の文字列を 16 進数に変換のところでエラーが起きていると思われるのですが... どうしたらいいでしょうか。

package org.jyl.base.io;

/**
 * 文字コードを定義した列挙型です。
 *
 * @author とあるゆっくり
 */
public enum CharCode
{
    /**
     * {@code ASCII} を示します。
     * 最大バイト数は 1 バイトです。
     */
    ASCII,

    /**
     * {@code EUC-jp} を示します。
     * 半角カタカナの対応予定は現在ありません。
     * 最大バイト数は制御文字も合わせ 3 バイトです。
     */
    EUC,

    /**
     * {@code Shift-JIS} を示します。
     * 最大バイト数は 2 バイトです。
     */
    Shift_JIS,

    /**
     * {@code Unicode}( UTF-16 ) を示します。
     * {@code UTF-8} は {@code UTF_8} を参照してください。
     * 最大バイト数は 4 バイトです。
     */
    Unicode,

    /**
     * {@code UTF-8} を示します。
     * 技術的制限により、_を使用しています。
     * 最大バイト数は 6 バイトです。
     */
    UTF_8,
};
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

0xEF は int として見ると 239 かもしれませんが, (signed) byte としてみると -16 です.

(byte)( ~bycp + 1 );

の結果は 17 ですので, 「現在出力されている値は 1 つめが 17 」というのはコードの動きとしては合っていると思います.
この変換を外しては如何でしょうか.

つまり「その当時は-の値だった」のが正しく, 「整数値を出すことに成功」したのは byte が signed(値範囲:-128~127) ではなく unsigned (値範囲:0~255) と思われていた勘違いからのコードの破壊だったと思います.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/22 19:13

    そういうことだったんですね。ありがとうございました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る