本家「@IT」にはない内容をエンジニアライフで技術紹介するコラム。広く議論する場になることを目指します。

第044回_EncodingDecl4

»
前回の続きで実装の紹介(後半)をします。

文字符号化方式の自動検知機能の実装2:文字符号化方式ファミリ毎の実装

文字符号化方式ファミリの実装は次のようにします。

基底クラスの実装

public abstract class AbstractEncodingFamily
{
    private FileInputStream m_input;
    
    private byte[]   m_startByte;
    private byte[][] m_literalByte;
    private byte[]   m_endByte;
    
    private byte[][] m_supportedEncodingName_ByteOrders;    
    private AbstractEncoding[] m_supportedEncoding;
    private AbstractEncoding   m_defaultEncoding;
    private AbstractEncoding   m_errorEncoding;
    private byte[][] m_sddeclLiteral;
    
    private int m_wordSize;
    
    public AbstractEncodingFamily(
            byte[]   startByte,
            byte[][] literalByte,
            byte[] endByte,
            byte[][] supportedEncodingName_ByteOrders,
            AbstractEncoding[] supportedEncoding,
            AbstractEncoding   defaultEncoding,
            byte[][] sddeclLiteral
        )
    {
        m_input = null;

        m_startByte   = startByte;
        m_literalByte = literalByte;
        m_endByte     = endByte;
        
        m_supportedEncodingName_ByteOrders
             = supportedEncodingName_ByteOrders;
        m_supportedEncoding = supportedEncoding;
        m_defaultEncoding = defaultEncoding;

        ErrorEncoding tmp = new ErrorEncoding();
        tmp.setUnSpportedEncName();
        m_errorEncoding = tmp;
        
        m_sddeclLiteral = sddeclLiteral;
                
        m_wordSize = (endByte==null) ? 0 : endByte.length;
    }

    public AbstractEncoding getDefaultEncoding()
    {
        return m_defaultEncoding;
    }

    public byte[] getLiteral(FileInputStream input)
    {
        m_input = input;
        byte[] retval = null;
        
        //>か先頭"か先頭'を探索する
        byte[] byteOrder = this.searchLiteralStartByte();
        
        //最後まで読み込んでしまった
        if (byteOrder == null)
        {
            //retval = null;
        }
        //最後まで読み込む前にキーワードを見つけた
        else
        {
            //読み込み条件の最後(>)か確認する
            boolean end = this.compareByteOrder(m_endByte, byteOrder);
            
            //読み込み条件の最後の場合
            if(end)
            {
                //retval = null;
            }
            //リテラルの開始を見つけた場合
            else
            {
                //末尾を探索して末尾までのリテラルの内容を取得する
                retval = this.getLiteralUntilEndByte(byteOrder);
            }
        }
        
        return retval;
    }

    //>か先頭"か先頭'を探索する]
    //
    //  戻り値:     null     最後まで読み込んだ
    //             null以外  リテラルのスタートか読み込み条件の
    //                       最後(>)を見つけた
    private byte[] searchLiteralStartByte()
    {
        byte[] retval = null;
        
        byte[] buf = new byte[m_wordSize];
        int len = 0;

        try
        {
            label0:
                
            while(true)
            {
                //指定byte読み取る
                len = m_input.read(buf);
                
                //読み込めなかった場合
                if (len == -1)
                {
                    //終了
                    retval = null;
                    break;
                }
                //読み込めた場合
                else
                {
                    boolean b1 = this.matchOfLiteralByte(buf);
                    boolean b2 = this.compareByteOrder(m_endByte, buf);
                    
                    if (b1 || b2)
                    {
                        retval = buf;
                        break label0;
                    }
                    else
                    {
                        //何もしない
                    }
                }
            }
        }
        catch (IOException e)
        {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        
        return retval;
    }
    

    private boolean matchOfLiteralByte(byte[] buf)
    {
        boolean retval = false;
        
        for(byte[] literalByte : m_literalByte)
        {
            //比較する
            boolean b = compareByteOrder(literalByte, buf);
            
            retval  = retval || b;
            //見つかったところでbreakしても良い
        }
        
        return retval;
    }

    private byte[] getLiteralUntilEndByte(byte[] endByteOfLiteral)
    {
        ArrayList<Byte> blist = new ArrayList<Byte>();
        
        boolean errorFlag=false;
        
        byte[] buf = new byte[m_wordSize];
        int len = 0;

        try
        {
            while(true)
            {
                //1byte読み取る
                len = m_input.read(buf);
                
                //読み込めなかった場合
                if (len == -1)
                {
                    errorFlag = true;
                    //終了
                    break;
                }
                //読み込めた場合
                else
                {
                    //末尾かどうか判定する
                    boolean b
                    = this.compareByteOrder(endByteOfLiteral, buf);

                    //末尾に到達した場合
                    if (b)
                    {
                        //終了
                        break;
                    }
                    //末尾に到達していない場合
                    else
                    {
                        for(byte byteOrder: buf)
                        {
                            Byte tmp = new Byte(byteOrder);
                            blist.add(tmp);
                        }
                    }
                }
            }
        }
        catch (IOException e)
        {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
            
            errorFlag = true;
        }
        
        //戻り値を宣言する
        byte[] retval = null;
        
        //エラーフラグがある場合
        if(errorFlag)
        {
            //何もしない
            //retval = null;
        }
        //エラーフラグがない場合
        else
        {
            //サイズを計算する
            int size = blist.size();
            retval = new byte[size];

            for(int i=0; i<size;i++)
            {
                retval[i]= blist.get(i);
            }
        }
        
        return retval;
    }

    //サポートする文字符号化方式を取得する
    public AbstractEncoding getEncoding(byte[] literal)
    {
        AbstractEncoding retval = m_errorEncoding;
        
        //SDDeclのリテラルか判定する
        boolean b = this.isSDDeclLiteral(literal);
        
        //SDDeclのリテラルの場合
        if (b)
        {
            //エンコーディング宣言未指定なのでデフォルトを設定する
            retval = this.getDefaultEncoding();
        }
        //SDDeclのリテラルでない場合(エンコーディング宣言のリテラルの場合)
        else
        {
            //サポートする文字符号化方式のEncNameのバイト列と比較する
            int len = m_supportedEncodingName_ByteOrders.length;
            for (int i=0; i<len; i++)
            {
                byte[] byteOrder = m_supportedEncodingName_ByteOrders[i];
                
                boolean b2 = compareByteOrder(byteOrder, literal);
                
                //一致する場合
                if (b2)
                {
                    retval = m_supportedEncoding[i];
                }
                //一致しない場合
                else
                {
                    //次へ
                }
            }
        }
        
        return retval;
    }
    
    private boolean isSDDeclLiteral(byte[] literal)
    {
        boolean retval = false;
        
        for(byte[] base : m_sddeclLiteral)
        {
            boolean b = compareByteOrder(base, literal);
            
            retval = retval || b;
        }
        
        return retval;
    }
    
    public boolean startMatch(byte[] buf)
    {
        boolean retval = true;
        
        //基準とbufを比較する
        retval = this.compareByteOrder(m_startByte, buf);
        
        return retval;
    }

    //バイトの並びを比較する
    //  base      比較元(基準)
    //  literal   比較対象
    //
    //  基準のbyteOrderよりliteralのバイト長が小さい場合はfalseを返す
    //
    protected boolean compareByteOrder(byte[] base, byte[] literal)
    {
        boolean retval = true;
        
        int baseLen    = base.length;
        int literalLen = literal.length;
        
        //リテラルのバイト数が基準より少ない場合
        if (baseLen > literalLen)
        {
            retval = false;
        }
        //リテラルのバイト数が基準以上の場合
        else
        {
            for(int i=0; i<baseLen; i++)
            {
                //i番目の文字が一致する場合
                if(base[i]==literal[i])
                {
                    //次へ
                    continue;
                }
                //i番目の文字が一致しない場合
                else
                {
                    retval = false;
                    //比較終了
                    break;
                }
            }
        }
        
        return retval;
    }

    //Unknownだけfalseを返すためデフォルトはtrueを返す
    public boolean needLiteralSeaching()
    {
        return true;
    }
}

UTF-8 BOM

public class Utf_8WithBomFamily extends AbstractEncodingFamily
{
    //                                   BOM Sequence
    private static final byte m_startByte[]
                  ={(byte)0xEF,(byte)0xBB,(byte)0xBF};
    
    private static final byte[][] m_literalByte ={
        {0x22},    // "
        {0x27},    // '
    };

    //                                       <
    private static final byte[] m_endByte = {0x3E};
    
    private static final byte[][] m_supportedEncodingName_ByteOrders = 
    {
        //U    T     F     -     8
        {0x55, 0x54, 0x46, 0x2D, 0x38},
    };
    
    private static final AbstractEncoding[] m_supportedEncoding = 
    {
        new Utf_8WithBomEncoding(),
    };
    
    //もしエンコーディング宣言がなかった場合の符号化文字方式
    private static final AbstractEncoding m_encoding
                                 = new Utf_8WithBomEncoding();

    private static final byte[][] m_sddeclLiteral = 
    {
        //y    e     s
        {0x79, 0x65, 0x73},
        //n    o
        {0x6E, 0x6F},
    };
    
    public Utf_8WithBomFamily()
    {
        super(m_startByte, m_literalByte, m_endByte,
              m_supportedEncodingName_ByteOrders,
              m_supportedEncoding, m_encoding, m_sddeclLiteral);
    }
}

UTF-8 BOM

public class Utf_8WithoutBomFamily extends AbstractEncodingFamily
{
    //                                    <    ?    x     m
    private static final byte m_startByte[]={0x3C,0x3F,0x78,0x6D};
    
    private static final byte[][] m_literalByte ={
        {0x22},    // "
        {0x27},    // '
    };

    //                                       <
    private static final byte[] m_endByte = {0x3E};
    
    private static final byte[][] m_supportedEncodingName_ByteOrders = 
    {
        //U    T     F     -     8
        {0x55, 0x54, 0x46, 0x2D, 0x38},
    };
    
    private static final AbstractEncoding[] m_supportedEncodingName = 
    {
        new Utf_8WithoutBomEncoding(),
    };
    
    //もしエンコーディング宣言がなかった場合の符号化文字方式
    private static final AbstractEncoding m_encoding
                             =new Utf_8WithoutBomEncoding();

    private static final byte[][] m_sddeclLiteral = 
    {
        //y    e     s
        {0x79, 0x65, 0x73},
        //n    o
        {0x6E, 0x6F},
    };
    
    public Utf_8WithoutBomFamily()
    {
        super(m_startByte, m_literalByte, m_endByte,
              m_supportedEncodingName_ByteOrders,
              m_supportedEncodingName, m_encoding, m_sddeclLiteral);
    }
}

UTF-16BE

public class Utf_16BEWithBomFamily extends AbstractEncodingFamily
{
    
    //                                   BOM Sequence
    private static final byte m_startByte[]={(byte) 0xFE,(byte) 0xFF};

    private static final byte[][] m_literalByte ={
        {0x00, 0x22},
        {0x00, 0x27},
    };
    
    private static final byte[] m_endByte = {0x00, 0x3E};

    private static final byte[][] m_supportedEncodingName_ByteOrders = 
    {
        //     U           T           F           -   
        {0x00, 0x55, 0x00, 0x54, 0x00, 0x46, 0x00, 0x2D,
        //     1           6
         0x00, 0x31, 0x00, 0x36},
    };
    
    private static final AbstractEncoding[] m_supportedEncodingName = 
    {
        new Utf_16Encoding(),
    };

    //もしエンコーディング宣言がなかった場合の符号化文字方式
    private static final AbstractEncoding m_encoding
                                =new Utf_16Encoding();
    
    
    private static final byte[][] m_sddeclLiteral = 
    {
        //     y           e           s
        {0x00, 0x79, 0x00, 0x65, 0x00, 0x73},
        //     n           o
        {0x00, 0x6E, 0x00, 0x6F},
    };
    
    public Utf_16BEWithBomFamily()
    {
        super(m_startByte, m_literalByte, m_endByte,
              m_supportedEncodingName_ByteOrders,
              m_supportedEncodingName, m_encoding, m_sddeclLiteral);
    }
}

UTF-16LE

public class Utf_16LEWithBomFamily extends AbstractEncodingFamily
{
    //                                   BOM Sequence
    private static final byte m_startByte[]={(byte) 0xFF,(byte) 0xFE};
    
    private static final byte[][] m_literalByte ={
        {0x22, 0x00},    // "
        {0x27, 0x00},    // '
    };

    //                                       <
    private static final byte[] m_endByte = {0x3E, 0x00};
    
    private static final byte[][] m_supportedEncodingName_ByteOrders = 
    {
        //U          T           F           -    
        {0x55, 0x00, 0x54, 0x00, 0x46, 0x00, 0x2D, 0x00,
        //1           6
          0x31, 0x00, 0x36, 0x00},
    };
    
    private static final AbstractEncoding[] m_supportedEncodingName = 
    {
        new Utf_16Encoding(),
    };
    
    private static final byte[][] m_sddeclLiteral = 
    {
        //y          e           s
        {0x79, 0x00, 0x65, 0x00, 0x73, 0x00},
        //n          o
        {0x6E, 0x00, 0x6F, 0x00},
    };
    
    //もしエンコーディング宣言がなかった場合の符号化文字方式
    private static final AbstractEncoding m_encoding=new Utf_16Encoding();

    public Utf_16LEWithBomFamily()
    {
        super(m_startByte, m_literalByte, m_endByte,
              m_supportedEncodingName_ByteOrders,
              m_supportedEncodingName, m_encoding, m_sddeclLiteral);
    }
}

Unknown

public class UnknownEncodingFamily extends AbstractEncodingFamily
{
    //デフォルトエンコーディング
    private static final AbstractEncoding m_encoding
                         =new Utf_8WithoutBomEncoding();
    
    public UnknownEncodingFamily()
    {
        super(null, null, null, null,null, m_encoding, null);
    }

    public boolean needLiteralSeaching()
    {
        return false;
    }
}

文字符号化方式の自動検知機能の実装2:文字符号化方式ファミリ毎の実装

文字符号化方式の実装は次のようにします。

基底クラス

public abstract class AbstractEncoding
{
    public boolean isError()
    {
        return false;
    }
    
    public abstract BufferedReader getBR(String path);

    protected BufferedReader getBR(String path, String encoding)
    {
        BufferedReader br = null;
        //FileReaderを作成し、1行トークナイザに設定する
        try
        {
            File file = new File(path);
            
            @SuppressWarnings("resource")
            FileInputStream fis = new FileInputStream(file);

            InputStreamReader reader = new InputStreamReader(fis, encoding);
            
            br = new BufferedReader(reader);
        }
        catch (FileNotFoundException e)
        {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        
        return br;
    }
}

UTF-8BOM付

public class Utf_8WithBomEncoding extends AbstractEncoding
{
    private static final String m_encoding = "UTF-8";
    
    public BufferedReader getBR(String path)
    {
        BufferedReader br = null;
        //FileReaderを作成し、1行トークナイザに設定する
        try
        {
            File file = new File(path);
            FileInputStream fis = new FileInputStream(file);

            //BOMは自動で無視できないのでスキップする
            this.skipBOM(fis);
            
            InputStreamReader reader
              = new InputStreamReader(fis, m_encoding);
            
            br = new BufferedReader(reader);
        }
        catch (FileNotFoundException e)
        {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        
        return br;
    }

    private void skipBOM(FileInputStream fis)
    {
        //BOMは3バイト
        byte[] b= new byte[3];
        
        try
        {
            //3バイト読み込み、捨てる
            fis.read(b);
        }
        catch (IOException e)
        {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
    }
}

UTF-8BOM無

public class Utf_8WithoutBomEncoding extends AbstractEncoding
{
    private static final String m_encoding = "UTF-8";
    
    public BufferedReader getBR(String path)
    {
        return getBR(path, m_encoding);
    }
}

UTF-16

public class Utf_16Encoding extends AbstractEncoding
{
    private static final String m_encoding = "UTF-16";

    @Override
    public BufferedReader getBR(String path)
    {
        return getBR(path, m_encoding);
    }
}

Error

public class ErrorEncoding extends AbstractEncoding
{
    private enum Error_Value
    {
        NOT_ERROR,           //0 初期値
        FILE_NOT_FOUND,      //1 ファイルがオープンできなかった
        NO_VERSION_NUM,      //3 version_numのリテラルが見つけられなかった
        UNSUPPORTED_ENC_NAME //4 対応していないEncName
    };
    
    private Error_Value m_errorValue;
    
    public ErrorEncoding()
    {
        m_errorValue = Error_Value.NOT_ERROR;
    }
    
    @Override
    public boolean isError()
    {
        return true;
    }
    
    @Override
    public BufferedReader getBR(String path)
    {
        return null;
    }

    public void setFileNotException()
    {
        m_errorValue = Error_Value.FILE_NOT_FOUND;
    }
    
    public void setNoVersionNumLiteral()
    {
        m_errorValue = Error_Value.NO_VERSION_NUM;
    }

    public void setUnSpportedEncName()
    {
        m_errorValue = Error_Value.UNSUPPORTED_ENC_NAME;
        
    }
}
Comment(0)

コメント

コメントを投稿する