3

データを含む巨大なファイルがあります(〜8Gb /〜8000万レコード)。すべてのレコードには、1つのタブで分割された6〜8個の属性があります。初心者には、特定の属性を別のファイルにコピーしてもらいたいと思います。したがって、たとえば、合計4つのトークンの2番目と最後のトークンのみが必要な場合は、上記よりも洗練されたコードが必要です。

StringTokenizer st = new StringTokenizer(line, "\t");
st.nextToken(); //get rid of the first token
System.out.println(st.nextToken()); //show me the second token
st.nextToken(); //get rid of the third token
System.out.println(st.nextToken()); //show me the fourth token

私はそれが巨大なファイルであることを思い出しているので、チェックする場合は冗長性を避ける必要があります。

4

5 に答える 5

3

あなたの質問は私にパフォーマンスについて疑問に思いました。最近、構文を掘り下げたという理由だけで、可能な限りGuavaのSplitterを使用しています。パフォーマンスを測定したことがないので、4つの解析スタイルの簡単なテストをまとめました。私はこれらを非常に迅速にまとめたので、スタイルとエッジケースの正確さの間違いを許してください。これらは、2番目と4番目の項目にのみ関心があるという理解に基づいています。

私が興味深いと思ったのは、「homeGrown」(実際には粗いコード)ソリューションが、350MBのタブ区切りテキストファイル(4列)を解析するときに最速であるということです。例:

head test.txt 
0   0   0   0
1   2   3   4
2   4   6   8
3   6   9   12

ラップトップで350MBを超えるデータを操作すると、次の結果が得られました。

  • 自家製:2271ms
  • guavaSplit:3367ms
  • 正規表現:7302ms
  • トークン化:3466ms

それを考えると、私はほとんどの作業でGuavaのスプリッターを使い続け、より大きなデータセットのカスタムコードを検討すると思います。

  public static List<String> tokenize(String line){
    List<String> result = Lists.newArrayList();
    StringTokenizer st = new StringTokenizer(line, "\t");
    st.nextToken(); //get rid of the first token
    result.add(st.nextToken()); //show me the second token
    st.nextToken(); //get rid of the third token
    result.add(st.nextToken()); //show me the fourth token
    return result;
  }

  static final Splitter splitter = Splitter.on('\t');
  public static List<String> guavaSplit(String line){
    List<String> result = Lists.newArrayList();
    int i=0;
    for(String str : splitter.split(line)){
      if(i==1 || i==3){
        result.add(str);
      }
      i++;
    }
    return result;
  }

  static final Pattern p = Pattern.compile("^(.*?)\\t(.*?)\\t(.*?)\\t(.*)$");
  public static List<String> regex(String line){
    List<String> result = null;
    Matcher m = p.matcher(line);
    if(m.find()){
      if(m.groupCount()>=4){
        result= Lists.newArrayList(m.group(2),m.group(4));
      }
    }
    return result;
  }

  public static List<String> homeGrown(String line){
    List<String> result = Lists.newArrayList();
    String subStr = line;
    int cnt = -1;
    int indx = subStr.indexOf('\t');
    while(++cnt < 4 && indx != -1){
      if(cnt==1||cnt==3){
        result.add(subStr.substring(0,indx));
      }
      subStr = subStr.substring(indx+1);
      indx = subStr.indexOf('\t');
    }
    if(cnt==1||cnt==3){
      result.add(subStr);
    }
    return result;
  }

これらはすべて、適切な境界チェックとより洗練された実装を使用すると遅くなる可能性があることに注意してください。

于 2012-10-13T23:17:46.763 に答える
0

cutPaul Tomblinが言うように、おそらくunixユーティリティを使用する必要があります。

ただし、Javaでは次のことも試すことができます。

String[] fields = line.split("\t");
System.out.println(fields[1]+" "+fields[3]);

これがより「エレガント」であるかどうかは意見の問題です。大きなファイルで高速かどうかはわかりませんが、システムでベンチマークを行う必要があります。

相対的なパフォーマンスは、1行にフィールドがいくつあるか、およびどのフィールドが必要かによっても異なります。split()行全体を一度に処理しますが、行を段階的に処理しStringTokenizerます(たとえば、20のうちフィールド2と4のみが必要な場合に適しています)。

于 2012-10-13T21:00:58.713 に答える
0

データファイルは巨大ですが、質問は、アイテムがタブで区切られているテキスト行のアイテムに便利にアクセスする方法に関するもののようです。StringTokenizerは、これほど単純なフォーマットには行き過ぎだと思います。

ある種の「分割」を使用して、行をトークンの配列に変換します。特に正規表現が必要ない場合は、String.splitよりもcommons-langで分割されたStringUtilsを好みます。タブは「空白」であるため、区切り文字を指定せずにデフォルトの分割方法を使用できます。

String [] items = StringUtils.split(line);
if (items != null && items.length > 6)
{
    System.out.println("Second: " + items[1]  + "; Fourth: " + items[3]);
}
于 2012-10-13T21:01:51.077 に答える
0

readLinesを実行している場合、実際にはファイルを2回スキャンしています。1)ファイルを一度に1文字ずつ検索して行末文字を探します。2)次に各行をスキャンしてタブを探します。

Csvライブラリの1つを見ることができます。メモリから、flatpackは1回のスキャンを実行します。ライブラリはより良いパフォーマンスを提供するかもしれません(私はそれをテストしたことはありませんが)。

いくつかのJavaライブラリ:-JavaCsvライブラリ -フラットパック

于 2012-10-13T22:03:21.077 に答える
0

速度以外にファイルが巨大な場合は、ファイルを操作するためにファイルをメモリにロードする必要があるため、メモリ消費の問題にも直面します。

アイデアはありますが、これはプラットフォーム固有であり、Javaモビリティに違反していることに注意してください。

Javaからunixコマンドを実行して、速度とメモリ消費量を大幅に増やすことができます。例えば:

    public static void main ( final String[] args)throws Exception {
         Runtime.getRuntime().exec("cat <file> | awk {print $1} >> myNewFile.txt");
    }
于 2012-10-14T00:24:57.030 に答える