Javaで大容量テキストファイルの読み込みについて

このQ&Aのポイント
  • Javaで大容量のテキストファイルを読み込み、特定の文字が含まれている行のみを抽出するプログラムの作成方法について教えてください。
  • 現在の方法ではOutOfMemoryErrorが発生し、30MB以上のファイルを読み込むことができません。改良方法や他の高速な読み込み方法があれば教えてください。
  • または、指定したバイト範囲でテキストファイルを読み込む方法も知りたいです。どうぞよろしくお願いします。
回答を見る
  • ベストアンサー

javaで大容量テキストファイルの読み込みについて

お世話になっております。 現在javaにて大容量のテキストファイル(100M以上)を読み込んで、 特定の文字が含まれている行のみを別ファイルへ抽出するという プログラムを作成しています。 現在のロジックでは、 File file = new File( "読み込みファイル" ); byte[] b = new byte[ (int) file.length() ]; FileInputStream fis = new FileInputStream(file); fis.read(b); String str = new String(b, "Shift-JIS"); return str; 上記の様なのようなロジックのメソッドを作成し、 返り値の文字列を改行コードでsplitし、パターンマッチングして行を抽出する方法をとっています。 しかしこの方法だと、OutOfMemoryErrorが発生していまい、 30MB以上のファイルを読み込むことができません。 原因がJVMのメモリ領域の問題なのは理解していますが、 readLineをでためしてみると処理終了があまりにも遅すぎるため、 なるべく現在の手法を改良する方向で作成したいのですが、 一回目 :0 ~ 30000000byte まで 二回目 :30000001 ~ 60000000byte まで の様にJVMの設定はいじらない方法でファイルのデータを、 指定バイトから指定バイトまで読み込む方法等なないでしょうか? それ以外でも処理が早く、 大容量のテキストファイルを読み込む方法がありましたら、 そちらでも問題ありません。 ぜひとも知恵をお貸しください。 宜しくお願いします。

  • Java
  • 回答数2
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • salsberry
  • ベストアンサー率69% (495/711)
回答No.2

readLine()を試したということですが、どのクラスのreadLine()を使ったのでしょう? BufferedReaderでも遅かったのでしょうか? FileInputStreamで頑張る場合でもそのまま使うのではなくてBufferedInputStreamをかぶせましょう。 (a) 1行の長さより十分長い(たとえば8KB)byteの配列を用意する (b) そのbyte配列にBufferedInputStreamから8KB分のデータを読み込む (c) byte配列の中を自力で行を区切って処理する (split()は使えません) (d) byte配列の最後に1行に満たないデータがnバイト分残った場合は、そのnバイトをbyte配列の先頭へコピーする (e) byte配列の(n+1)バイト目以降に、続きのデータを(8K-n)バイト分だけBufferedInputStreamから読み込む (read(byte[], int, int)が使えます) (f) ファイルの最後を読み込むまで(c)~(e)を繰り返す

heka_11
質問者

お礼

ありがとうございます。 BufferedInputStreamは使用していなかったのですが、 解答いただいた方法を自分なりに改変することで、 うまく実行することが出来ました。 ちなみにBufferedReaderのreadLineを使用していたのですが、 splitでループを回した時と処理終了が断然違っていたのは、 自分なりに調べてみることにします。

その他の回答 (1)

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

マニュアルを読みましょう http://java.sun.com/javase/ja/6/docs/ja/api/java/io/FileInputStream.html#read(byte[]) 『最大 b.length バイトまでのデータを』、この入力ストリームからバイト配列に読み込みます http://java.sun.com/javase/ja/6/docs/ja/api/java/io/FileInputStream.html#read(byte[],%20int,%20int) 『最大 len バイトまでのデータを』、この入力ストリームからバイト配列に読み込みます。 ただし。 複数バイト文字の途中まで/途中から読む可能性があります。 すなおにreadLine使うのがいいと思うのですが。 100Mのデータはそもそも読めてないわけですから、「readが早い」というのも100M全部では比較できてないんですよね? あとは、exec("grep '検索文字列' ファイル > 出力ファイル"); とか

heka_11
質問者

お礼

書き方が悪かったようで、すいません。 実際にファイル自体は読み込めているのですが、 String str = new String(b, "Shift-JIS"); の時点でoutOfMemoryが発生するという意味でした。 基本はやはりreadLineを使用するのですね。 なぜsplitとreadLineの終了時間に大きく差異が発生したのか、 自分なりに調べて見ることにします。

関連するQ&A

  • テキストファイルが読み込めない

    みなさんはじめまして、下記ソースなんですが「FileInputStream fi = new FileInputStream(inputFile);」の手前までは処理が流れるの ですが、その後の処理が流れなくて困っています。 ターゲットとなる「test.txt」ファイルはHTML・JAVAソースファイル クラスファイルと同じフォルダに存在します。 特に保存先等を指定していないので、必然的に同じフォルダ内を見てフ ァイルを読み込みに行くんですけど、読み込まず、そのまま処理が流れ てしまいます。 どこかやり方がいけないところがあるんでしょうか? public String FileInput(){ byte data[] = new byte[16]; String number = ""; try { File inputFile = new File("test.txt"); FileInputStream fi = new FileInputStream(inputFile); /* データを読み込み */ if(fi.read(data)== 16){       /* 0バイト目から9バイト目までを読み込み */ if(new String(data,0,9).equals("010105502")){         /* 0バイト目から16バイト目までをnumberに代入 */ number = new String(data,0,16); } } fi.close(); } catch(FileNotFoundException e){ e.printStackTrace(); } catch(IOException e){} return(number); }

    • ベストアンサー
    • Java
  • バイナリの読み込みから出力まで

    こんばんは、バイナリの扱いについて質問させてください。 やりたいことはこんな感じです。 (1) hoge.mp3ファイルをバイナリで読み込む。 (2) バイナリの1000バイト目から200バイトの範囲を抜き出す。 (3) 抜き出したバイナリをファイルshort.mp3として保存する。 下の構文だとそもそもコンパイルすら通らないのですが、 やりたいことを少しでも伝えたいので、あえて書かせていただきました。 どうかよろしくお願い致します。 FileInputStream fis = new FileInputStream("hoge.mp3"); // hoge.mp3の1000バイト目から200バイト分抜き出す。 byte[] pick = fis.read(fis, 1000, 200); DataOutputStream dout = new DataOutputStream(new FileOutputStream("short.mp3")); dout.write(pick);

    • ベストアンサー
    • Java
  • ファイル内の1行(レコード)から指定バイト数だけを読込みたい。

    ファイル内の1行(レコード)から指定バイト数だけを読込みたい。 // ファイルオープン FileInputStream fis = new FileInputStream(file); InputStreamReader isr; isr = new InputStreamReader(fis); BufferedReader result = new BufferedReader(isr); while (result.readLine() != null) { // 末尾まで移動する } 上記のようにBufferedReaderを使い、ファイルを読み込んでいるのですが、 ファイル内の1行ごとに指定バイト数だけを読み込みたいです。 readLineメソッドでは1行を全部読み込んでしまうため出来ません。 (readLineで一度他の変数へ入れてからというのは無しです。読込む時点で指定バイト数としたいのです。) readメソッドでは、行ごとの指定って出来ないですよね・・・。 ファイル操作系の知識が少ない為、なかなか探せません。 読み込む時点で1行の取得レコード長に制限値を設ける方法はないでしょうか? また、取得レコード長が制限値を超えた場合、エラーメッセージを出したいのですがその判定とかも可能でしょうか? どうかよろしくお願いします。 ロジックとしてはこんな感じにしたいです。 while(ファイルの終端まで行を読み込む){   // 読み込んだ1行が2048バイト未満かを判定   if(読み込んだ1行が2048バイトを超えている場合){     エラーメッセージを出力する(処理はそのまま続行)   }   1行から2048バイト未満を取得 }

    • ベストアンサー
    • Java
  • Return で文字列が返せない、、

    このような関数を作って、mainの関数に渡したいのですが、文法がダメだとコンパイラに怒られます。 private String test(){ try{ FileOutputStream fos=openFileOutput("sample.txt", MODE_PRIVATE); String text="サンプルテキストです"; fos.write(text.getBytes()); fos.close(); }catch(FileNotFoundException e){ }catch(IOException e){ } try{ FileInputStream fis=openFileInput("sample.txt"); byte buffer[]=new byte[100]; fis.read(buffer); String str = new String(buffer).trim(); fis.close(); return str; }catch(FileNotFoundException e){ }catch(IOException e){ } }なぜ返せないのでしょうか。

    • ベストアンサー
    • Java
  • Fileから指定した行を読み取りたい

    現在、例えば1300行目を読み取りたいとしたときに、今現在の私のやり方では、 FileInputStream fis = new FileInputStream("xxxxxx.txt"); InputStreamReader ir = new InputStreamReader(fis , "SJIS"); BufferedReader br = new BufferedReader(ir); String ch; int count=0; while((ch = br.readLine()) != null){  count++;  if(count == 1300)    break; } System.out.println("指定行の内容は:"+ch); br.close(); のように、1300回行を読んだらその内容を出力するようにしているのですが、これは、かなり無駄があると思うのです。なので、指定した行に読み込みヘッダを移動して、その指定した行を読み取りたいのですが、指定行を一発で読み取ることはできますか? skip(long numChars)メソッドを使用することを考えましたが、ファイルの各行のバイト数は各行ごとに異なるので、規則性がなく無理であると考えあきらめました。

  • ファイル読込時の無限ループ

    テキストファイルを読み込んで、String strにファイルの内容を全て代入するというソースコードを書きました。 普通にテキストファイルを読み込むと正しく動くのですが、 ファイル選択ダイアログで間違えてzipファイル等を選択した時に、 readLine()からnullが返らず、無限ループになってしまう時があります。 文字コードを指定しなくてはいけないので、他の方法が見つかりません。 間違えたファイルを読ませた時に、きちんとエラーを出力するようにしたいのですが、 どのようにしたら検知できますでしょうか。 String line; String str = ""; try { br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), Defines.WIN31)); while ((line = br.readLine()) != null) { str += line.trim(); } br.close(); } catch (IOException ex) { ex.printStackTrace(); }

  • ファイル圧縮について。

    ファイル圧縮について質問させてください。 以下のようなメソッドを作成したのですが、 public run(String strFiles[], String strZipFileName) { int i = 0; int iLen = 0; FileInputStream fis = null; FileOutputStream fos = null; ZipOutputStream zos = null; ZipEntry zent = null; byte[] buf = new byte[1024*10*10]; try { fos = new FileOutputStream(strZipFileName); zos = new ZipOutputStream(fos); for (i = 0; i < strFiles.length(); i++) { fis = new FileInputStream(strFiles[i]); zent = new ZipEntry(strFiles[i]); zos.putNextEntry(zent); while (-1 != (iLen = fis.read(buf))) { zos.write(buf, 0, iLen); } zos.flush(); zos.closeEntry(); fis.close(); } } catch(Exception e) { System.err.println(e); } finally { try { zos.close(); // ※1 fos.close(); // ※2 } catch(Exception e) { System.err.println(e); } } } サイズの小さいファイルや、 特定のサイズのファイル(13k程度)を対象とした時、 高い確率で、空の圧縮ファイルが作成される事があります。 ログを出力しながら確認すると、このような現象が起きた場合、 圧縮ファイルをcloseする直前(※1)では作成されたファイルはサイズがあるのですが、 圧縮ファイルをcloseした直後(※2)ではファイルのサイズが0になってしまいます。 また、2台あるサーバの内、1台だけでこの現象が起こっています。 javaのバージョンは1.3です。 何故このような現象が起こってしまうのか、 ご存知の方がいらっしゃいましたら教えていただけないでしょうか。 宜しくお願いします。

    • ベストアンサー
    • Java
  • 開いたファイルをJListに追加したい

    最近、Swingを勉強し始めたばかりです。 開くボタンを作りファイルチューザでcsvファイルを開き、1行ずつ引っ張り出してきて全データをJListに表示したいのですがうんともすんともいいません。 知恵をお貸しくださいお願いします(T_T) 鈴木一郎,東京都,渋谷110,1960 松井一郎,大阪府,大阪市119,1928 class SwingTest extends JFrame implements ActionListener{ private static JFrame frame; private JFileChooser chooser = new JFileChooser(); private Container pane = getContentPane(); private DefaultListModel myList = new DefaultListModel(); 略 public void actionPerformed(ActionEvent e){ String s = e.getActionCommand(); if(s.equals("Open")==true){ JFileChooser chooser = new JFileChooser(); int returnval = chooser.showOpenDialog(this); li.setPreferredSize(new Dimension(500,300)); if(returnval == JFileChooser.APPROVE_OPTION){ File file = chooser.getSelectedFile(); FileInputStream fis = new FileInputStream( file ); InputStreamReader isr = new InputStreamReader( fis ); BufferedReader br = new BufferedReader( isr ); String line = br.readLine(); String[] token = line.split(","); String str = token[0] + "\t" + token[3]; myList.addElement(str); li.setModel(myList); }

    • ベストアンサー
    • Java
  • ファイルロック後の書き込みについて

    ファイルAをロックしファイルBを読み込み ファイルAに書き込むプログラムを作成しています。 単体起動では問題無く動くのですが 複数起動するとファイルの書き込みが行われなくなってしまいます。 ロックしたプログラム以外はロックエラーになり ロックについては正常に動いているように見えます。 なぜ書き込みが行われなくなってしまうかご教授いただけますか? import java.io.* ; import java.nio.*; import java.nio.channels.*; public class CopyFile { public static void main(String[] args) throws Exception { try{ FileInputStream fis = null; FileOutputStream output = null; FileChannel outChannel = null; FileLock outFileLock = null; byte buf[] = new byte[2048]; int len; int count = 0; if (args.length != 2) { System.out.println("使用法: java CopyFile ファイル名1 ファイル名2"); System.exit(0); } //if String source= args[0]; String target= args[1]; File sourceFile = new File(source); File targetFile = new File(target); fis = new FileInputStream(sourceFile); output = new FileOutputStream(targetFile); outChannel = output.getChannel(); outFileLock = outChannel.tryLock(); if (outFileLock == null) { System.out.println("rock error!!"); System.exit(0); } else { while ((len = fis.read(buf)) != -1) { output.write(buf, 0, len); count += len; } Thread.sleep(5000); output.flush(); output.close(); fis.close(); } }catch(Exception e){ System.out.println(e); } } } // Class CopyFile

    • ベストアンサー
    • Java
  • レコード長からのbyteスキップ設定について

    QNo.8229324から あらたに新規でご質問させて頂きます。 1レコードごとに10byteあったとします。 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 レコード長からスキップ設定を35byteと設定したとします。 40byteごとに頭の付与された5byteをスキップさせたいのですが なにかサンプル及びアドバイス頂けますでしょうか? 仕様案 (1)まずはレコード先端の5byteをスキップ (2)次は35byteを超えた時点で5byteスキップを繰り返し。 どうぞ宜しくお願い致します。 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.io.ByteArrayOutputStream; public class dat{ public static void main(String[] args) { String inputFileName = ""; String outputFileName = ""; // ファイルオブジェクトの生成 File inputFile = new File(inputFileName); File outputFile = new File(outputFileName); try { FileInputStream fis = new FileInputStream(inputFile); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(outputFile); BufferedOutputStream bos = new BufferedOutputStream(fos); byte[] buf = new byte[17]; int len = 0; while ((len = bis.read(buf, 0, 17)) == 17) { bos.write(buf, 0, 17); } bos.flush(); bos.close(); bis.close(); } catch(Exception e) { e.printStackTrace(); } }

    • ベストアンサー
    • Java