org.apache.hadoop.io.Text.getBytes()を使用するときの注意
以前Hadoop入力データの文字エンコーディングに対応するため以下のようなコードを書いた
public class CustomMapper extends Mapper<LongWritable, Text, Text, Text> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { Configuration conf = context.getConfiguration(); String encoding = conf.get(Const.CONF_CHAR_ENCODING, "UTF-8"); String line = new String(value.getBytes(), Charset.forName(encoding)); // エンコードした文字列で処理 } }
テストしていたら変数「line」に前の行のデータが残る現象が発生
正確には同じMapタスクの直前にTextInputFormatで読み込んだデータが後ろに残る
下のような感じ
入力ファイル
AAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBB
CCCCCCCCCC
DDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEE
MapタスクAの入力
AAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBB
MapタスクBの入力
CCCCCCCCCC
DDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEE
MapタスクAの変数「line」
AAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBAAAAA
MapタスクBの変数「line」
CCCCCCCCCC
DDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEDDDDD
1.0.3のjavadocを確認するとgetBytes()で取得した値で正しいのはgetLength()の位置までと書いてある
実際に問題が発生する場合はgetBytes()で取得した配列のサイズがgetLength()の値を超えている
Text (Hadoop 1.0.3 API)
よって以下のように修正
public class CustomMapper extends Mapper<LongWritable, Text, Text, Text> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { Configuration conf = context.getConfiguration(); String encoding = conf.get(Const.CONF_CHAR_ENCODING, "UTF-8"); byte[] lineArray = ArrayUtils.subarray( value.getBytes(), 0, value.getLength()); String line = new String(lineArray, Charset.forName(encoding)); // エンコードした文字列で処理 } }
2.0.1-alphaではcopyBytes()メソッドが追加されそちらを使うように書かれていた。
Text (Apache Hadoop Main 2.0.1-alpha API)