スペースで分割された数百万の列を持つ巨大なファイルがありますが、行の数は限られています。
例.txt:
1 2 3 4 5 ........
3 1 2 3 5 .........
l 6 3 2 2 ........
ここで、2 番目の列を読みたいだけです。
2
1
6
高性能のJavaでそれを行うにはどうすればよいですか。
ありがとう
更新: ファイルは通常、数百行を含む 1.4G です。
ファイルが静的に構造化されていない場合、唯一のオプションは素朴なものです。ファイルのバイトシーケンスをバイトシーケンスごとに読み取り、改行を探し、それぞれの後の2番目の列を取得します。を使用しFileReader
ます。
ファイルが静的に構造化されている場合、ファイル内の特定の行の 2 番目の列がどこにあるseek()
かを直接計算できます。
@geneに同意する必要があります。最初に、BufferedReaderとgetLineを試してみてください。コーディングは簡単です。getLineの結果と使用するサブストリング操作の間でバッキング配列のエイリアスを作成しないように注意してください。String.substring()は特に一般的な原因であり、3文字のサブストリングがそれを参照していたため、マルチMBバイト配列がメモリにロックされていました。
ASCIIを想定すると、これを行うときの私の好みは、バイトレベルにドロップダウンすることです。mmapを使用してファイルをaとして表示し、ByteBuffer
0x20および0x0Aの線形スキャンを実行します(UNIXスタイルの行区切り文字を想定)。次に、関連するバイトを文字列に変換します。8ビットの文字セットを使用している場合、これより速くすることは非常に困難です。
BufferedReader
Unicodeを使用している場合、問題は十分に複雑なので、そのパフォーマンスが本当に許容できない場合を除いて、使用することを強くお勧めします。getLine()
うまくいかない場合は、への呼び出しをループすることを検討してくださいread()
。
とにかく、外部バイトストリームから文字列を初期化するときは、常に文字セットを指定する必要があります。これにより、文字セットの仮定が明示的に文書化されます。だから私は遺伝子の提案に小さな変更を加えることをお勧めします。
int i = Integer.parseInt(new String(buffer, start, length, "US-ASCII"));
int i = Integer.parseInt(new String(buffer, start, length, "ISO-8859-1"));
int i = Integer.parseInt(new String(buffer, start, length, "UTF-8"));
適切に。
FileInputStream
入力として a を使用し、独自のバッファリングを処理する小さなステート マシンを次に示します。ロケール変換はありません。
1/2 Gb のメモリを搭載した 7 年前の 1.4 GHz ラップトップでは、12 億 8000 万バイトのデータを処理するのに 48 秒かかります。4Kb より大きいバッファーは、実行が遅くなるようです。
4Gb の新しい 1 年前の MacBook では、14 秒で動作します。ファイルがキャッシュに入れられると、2.7 秒で実行されます。ここでも、4Kb を超えるバッファとの違いはありません。これは同じ 12 億バイトのデータ ファイルです。
メモリ マップド IO の方がうまくいくと思いますが、これはおそらく移植性が高いでしょう。
指示した列をフェッチします。
import java.io.*;
import java.util.Random;
public class Test {
public static class ColumnReader {
private final InputStream is;
private final int colIndex;
private final byte [] buf;
private int nBytes = 0;
private int colVal = -1;
private int bufPos = 0;
public ColumnReader(InputStream is, int colIndex, int bufSize) {
this.is = is;
this.colIndex = colIndex;
this.buf = new byte [bufSize];
}
/**
* States for a tiny DFA to recognize columns.
*/
private static final int START = 0;
private static final int IN_ANY_COL = 1;
private static final int IN_THE_COL = 2;
private static final int WASTE_REST = 3;
/**
* Return value of colIndex'th column or -1 if none is found.
*
* @return value of column or -1 if none found.
*/
public int getNext() {
colVal = -1;
bufPos = parseLine(bufPos);
return colVal;
}
/**
* If getNext() returns -1, this can be used to check if
* we're at the end of file.
*
* Otherwise the column did not exist.
*
* @return end of file indication
*/
public boolean atEoF() {
return nBytes == -1;
}
/**
* Parse a line.
* The buffer is automatically refilled if p reaches the end.
* This uses a standard DFA pattern.
*
* @param p position of line start in buffer
* @return position of next unread character in buffer
*/
private int parseLine(int p) {
colVal = -1;
int iCol = -1;
int state = START;
for (;;) {
if (p == nBytes) {
try {
nBytes = is.read(buf);
} catch (IOException ex) {
nBytes = -1;
}
if (nBytes == -1) {
return -1;
}
p = 0;
}
byte ch = buf[p++];
if (ch == '\n') {
return p;
}
switch (state) {
case START:
if ('0' <= ch && ch <= '9') {
if (++iCol == colIndex) {
state = IN_THE_COL;
colVal = ch - '0';
}
else {
state = IN_ANY_COL;
}
}
break;
case IN_THE_COL:
if ('0' <= ch && ch <= '9') {
colVal = 10 * colVal + (ch - '0');
}
else {
state = WASTE_REST;
}
break;
case IN_ANY_COL:
if (ch < '0' || ch > '9') {
state = START;
}
break;
case WASTE_REST:
break;
}
}
}
}
public static void main(String[] args) {
final String fn = "data.txt";
if (args.length > 0 && args[0].equals("--create-data")) {
PrintWriter pw;
try {
pw = new PrintWriter(fn);
} catch (FileNotFoundException ex) {
System.err.println(ex.getMessage());
return;
}
Random gen = new Random();
for (int row = 0; row < 100; row++) {
int rowLen = 4 * 1024 * 1024 + gen.nextInt(10000);
for (int col = 0; col < rowLen; col++) {
pw.print(gen.nextInt(32));
pw.print((col < rowLen - 1) ? ' ' : '\n');
}
}
pw.close();
}
FileInputStream fis;
try {
fis = new FileInputStream(fn);
} catch (FileNotFoundException ex) {
System.err.println(ex.getMessage());
return;
}
ColumnReader cr = new ColumnReader(fis, 1, 4 * 1024);
int val;
long start = System.currentTimeMillis();
while ((val = cr.getNext()) != -1) {
System.out.print('.');
}
long stop = System.currentTimeMillis();
System.out.println("\nelapsed = " + (stop - start) / 1000.0);
}
}