Javaを使用して大きなテキストファイルを1行ずつ読み取るにはどうすればよいですか?


848

Javaを使用して、5〜6 GB程度の大きなテキストファイルを1行ずつ読み取る必要があります。

これをすばやく行うにはどうすればよいですか?


69
@kamaci et。al。この質問は重複としてマークしないでください。「最後の行をすばやく読み取る」は代替手段ではなく、「テキストファイルを1行ずつ読み取る最も速い方法」であるかどうかについては議論の余地があります。何かを行う最も速い方法は、必ずしも一般的な方法ではありません。さらに、以下の回答にはコードが含まれていますが、リストする最も関連性の高い選択肢には含まれていません。この質問は役に立ちます。これは現在、「java read file line by line」の上位のGoogle検索結果です。最後に、スタックオーバーフローに到達し、2問ごとに1問に処分のフラグが立てられていることがわかります。
Patrick Cullen

5
以下は、6つの可能な実装の速度の比較です。
Serg M 10

4
イベントは、SOの密接な方針が悪いと主張するコメントを読んでいますが、SOはそれに固執します。どんな犠牲を払っても冗長性を避けたいというのは、非常に狭い視野の開発者の見方です。ちょうどそれをしましょう!クリームは上に上がり、シャットはそれ自体でうまく底に沈みます。以前に質問された可能性がある質問(どの質問ではないですか?)質問は現在「保護」されています...
Stijn de Witt

3
タイトルを読むだけで質問が重複としてマークされるのは驚くべきことです。
ルーク、

回答:


1064

一般的なパターンは使用することです

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    String line;
    while ((line = br.readLine()) != null) {
       // process the line.
    }
}

文字エンコードがないと想定すると、データをより速く読み取ることができます。たとえば、ASCII-7ですが、それほど大きな違いはありません。データの処理にはさらに時間がかかる可能性が高いです。

編集:lineリークの範囲を回避するために使用する一般的ではないパターン。

try(BufferedReader br = new BufferedReader(new FileReader(file))) {
    for(String line; (line = br.readLine()) != null; ) {
        // process the line.
    }
    // line is not visible here.
}

更新:Java 8では次のことができます

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
        stream.forEach(System.out::println);
}

注:#closeメソッドが確実に呼び出されるように、リソースをtry-with-resourceブロックに配置する必要があります。そうでない場合、基になるファイルハンドルは、GCが後で実行するまで閉じられません。


6
このパターンは、適切な例外処理でどのように見えますか?br.close()がIOExceptionをスローすることに注意してください。これは意外と思われます-読み取り用に開かれているファイルを閉じるとどうなりますか?FileReaderのコンストラクターがFileNotFound例外をスローする場合があります。
MikeB 2013年

3
200MBのファイルがあり、90MB / sで読み取ることができる場合、約3秒かかると思いますか?私の場合、この「遅い」読み方では、数分かかるようです。SSDを使用しているので、読み取り速度は問題になりませんか?
Jiew Meng 2013

4
@JiewMeng SO他に何か時間がかかっているのではないかと思います。あなただけのファイルとの行読んで試すことができ、何も他を。
Peter Lawrey 2013年

44
なぜfor(String line = br.readLine(); line != null; line = br.readLine())ところで、Javaの8にあなたが行うことができますtry( Stream<String> lines = Files.lines(...) ){ for( String line : (Iterable<String>) lines::iterator ) { ... } }憎しみにハードされていません。
Aleksandr Dubinsky

26
@AleksandrDubinsky Java 8のクロージャーで問題となっているのは、コードが非常に簡単に読みにくくなる(そして遅くなる)ことです。多くの開発者が "クール"であるため、使いすぎているのを目にすることがあります。
Peter Lawrey、

155

このブログを見てください:

バッファサイズを指定するか、デフォルトのサイズを使用できます。デフォルトは、ほとんどの目的に十分な大きさです。

// Open the file
FileInputStream fstream = new FileInputStream("textfile.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));

String strLine;

//Read File Line By Line
while ((strLine = br.readLine()) != null)   {
  // Print the content on the console
  System.out.println (strLine);
}

//Close the input stream
fstream.close();

6
私のファイルは1.5 Gigであり、あなたの答えを使用してファイルを読み取ることができません!
Aboozar Rajabi 2016年

3
@AboozarRajabiもちろん可能です。このコードは、任意のテキストファイルを読み取ることができます。
ローンの侯爵

10
リンク品質が悪いため反対投票されました。完全に無意味DataInputStreamであり、間違ったストリームが閉じられます。Javaチュートリアルに問題はなく、このような任意のサードパーティのインターネットのゴミを引用する必要もありません。
ローンの侯爵

1
コメントを破棄します。6行のコードに対して4行の100%冗長コメントがあります。
バッファロー

97

Java 8がリリースされると(2014年3月)、ストリームを使用できるようになります。

try (Stream<String> lines = Files.lines(Paths.get(filename), Charset.defaultCharset())) {
  lines.forEachOrdered(line -> process(line));
}

ファイルのすべての行を印刷します。

try (Stream<String> lines = Files.lines(file, Charset.defaultCharset())) {
  lines.forEachOrdered(System.out::println);
}

1
を使用しStandardCharsets.UTF_8Stream<String>簡潔にするために使用します。forEach()特にforEachOrdered()理由がない限り、特に使用しないでください。
Aleksandr Dubinsky

2
なぜforEach()を避けるのですか?悪いですか?
steventrouble 2014年

forEachOrderedの代わりにforEachを使用すると、行が順不同で印刷される可能性がありますね。
msayag 14年

2
@steventroubleを見てください:stackoverflow.com/questions/16635398/…のような短い関数参照を渡しても悪くはありませんforEach(this::process)が、コードのブロックをラムダとして内部に書き込むと醜くなりますforEach()
Aleksandr Dubinsky 2015年

2
@msayag、その通り、forEachOrderedインオーダーで実行するために必要です。その場合、ストリームを並列化できないことに注意してください。ただし、ファイルに数千行が含まれていない限り、並列化はオンになりません。
Aleksandr Dubinsky 2015年

38

Java 7より前の完全なエラー処理とサポートする文字セット仕様のサンプルを次に示します。Java7を使用すると、try-with-resources構文を使用できるため、コードがすっきりします。

デフォルトの文字セットだけが必要な場合は、InputStreamをスキップしてFileReaderを使用できます。

InputStream ins = null; // raw byte-stream
Reader r = null; // cooked reader
BufferedReader br = null; // buffered for readLine()
try {
    String s;
    ins = new FileInputStream("textfile.txt");
    r = new InputStreamReader(ins, "UTF-8"); // leave charset out for default
    br = new BufferedReader(r);
    while ((s = br.readLine()) != null) {
        System.out.println(s);
    }
}
catch (Exception e)
{
    System.err.println(e.getMessage()); // handle exception
}
finally {
    if (br != null) { try { br.close(); } catch(Throwable t) { /* ensure close happens */ } }
    if (r != null) { try { r.close(); } catch(Throwable t) { /* ensure close happens */ } }
    if (ins != null) { try { ins.close(); } catch(Throwable t) { /* ensure close happens */ } }
}

これが完全なエラー処理を備えたGroovyバージョンです。

File f = new File("textfile.txt");
f.withReader("UTF-8") { br ->
    br.eachLine { line ->
        println line;
    }
}

1
ByteArrayInputStream文字列リテラルによるフィードは、大きなテキストファイルの読み取りとどのような関係がありますか?
ローン侯爵

絶対に役に立たない閉鎖。すべてのストリームを閉じる理由はありません。これらのストリームのいずれかを閉じると、他のすべてのストリームが自動的に閉じられます...
Enerccio

21

Java 8では、次のことができます。

try (Stream<String> lines = Files.lines (file, StandardCharsets.UTF_8))
{
    for (String line : (Iterable<String>) lines::iterator)
    {
        ;
    }
}

いくつかの注意:(Files.linesほとんどのストリームとは異なり)によって返されたストリームは閉じる必要があります。ここで述べた理由により、私はの使用を避けforEach()ます。奇妙なコード(Iterable<String>) lines::iteratorは、ストリームをIterableにキャストします。


Iterableこのコードを実装しないことは、有用ではありますが明らかに醜いです。動作するにはキャスト(つまり(Iterable<String>))が必要です。
Stephan

この方法で最初の行をスキップするにはどうすればよいですか?
2014年

2
@qedfor(String line : (Iterable<String>) lines.skip(1)::iterator)
Aleksandr Dubinsky

1
あなたは、実際に使用することを意図していない場合はStream、機能を使用するFiles.newBufferedReader代わりにFiles.lines繰り返し呼び出すreadLine()まで、null代わりのような構文を使用するのでは(Iterable<String>) lines::iterator...非常に簡単であるように思わ
ホルガー

なぜ::をlines :: iteratorで使うのですか?::で知っている唯一の使用法は、メソッド名をラムダ関数にパッケージ化することです。ループパラメータのために後:あなたが使用して、いくつかのラムダメソッドを取得しながら、::変数でなければなりません
Trismegistos

19

できることは、スキャナーを使用してテキスト全体をスキャンし、テキストを1行ずつ確認することです。もちろん、以下をインポートする必要があります。

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public static void readText throws FileNotFoundException {
    Scanner scan = new Scanner(new File("samplefilename.txt"));
    while(scan.hasNextLine()){
        String line = scan.nextLine();
        //Here you can manipulate the string the way you want
    }
}

スキャナーは基本的にすべてのテキストをスキャンします。whileループは、テキスト全体をトラバースするために使用されます。

この.hasNextLine()関数はブール値で、テキストにまだ行がある場合にtrueを返します。この.nextLine()関数は、1行全体を文字列として提供します。これを好きな方法で使用できます。System.out.println(line)テキストを印刷してみてください。

補足:.txtはファイルタイプのテキストです。


メソッド宣言は次のように見えるべきではありません:´public static void readText throws FileNotFoundException(){´Like:´public static void readText()throws FileNotFoundException {´
Ketcomp

これはに比べてかなり遅いBufferedReader.readLine()ため、最高の方法を求めました。
ローンの侯爵

18

FileReader InputStreamReaderではエンコードを指定できません。エンコードを指定する必要がある場合は、代わりに使用してください。

try {
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "Cp1252"));         

    String line;
    while ((line = br.readLine()) != null) {
        // process the line.
    }
    br.close();

} catch (IOException e) {
    e.printStackTrace();
}

このファイルをWindowsからインポートした場合、ANSIエンコード(Cp1252)が含まれている可能性があるため、エンコードを指定する必要があります。


17

Javaファイルを読み取るための10の異なる方法を文書化してテストし、1KBから1GBのテストファイルで読み取らせることで、それらを互いに実行しました。1GBのテストファイルを読み取るための最速の3つのファイル読み取り方法を次に示します。

パフォーマンステストを実行しているときは、コンソールに何も出力しなかったので、テストの速度が大幅に低下することに注意してください。私は生の読み取り速度をテストしたかっただけです。

1)java.nio.file.Files.readAllBytes()

Java 7、8、9でテストされています。これは全体として最速の方法でした。1 GBのファイルを読み取ると、一貫して1秒弱でした。

import java.io..File;
import java.io.IOException;
import java.nio.file.Files;

public class ReadFile_Files_ReadAllBytes {
  public static void main(String [] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-1GB.txt";
    File file = new File(fileName);

    byte [] fileBytes = Files.readAllBytes(file.toPath());
    char singleChar;
    for(byte b : fileBytes) {
      singleChar = (char) b;
      System.out.print(singleChar);
    }
  }
}

2)java.nio.file.Files.lines()

これはJava 8および9で正常にテストされましたが、ラムダ式のサポートがないため、Java 7では機能しません。1GBのファイルを読み込むのに約3.5秒かかり、大きなファイルを読み込む場合と比べて2位になりました。

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.stream.Stream;

public class ReadFile_Files_Lines {
  public static void main(String[] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-1GB.txt";
    File file = new File(fileName);

    try (Stream linesStream = Files.lines(file.toPath())) {
      linesStream.forEach(line -> {
        System.out.println(line);
      });
    }
  }
}

3)BufferedReader

Java 7、8、9で動作するようにテストされました。1GBのテストファイルを読み取るのに約4.5秒かかりました。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFile_BufferedReader_ReadLine {
  public static void main(String [] args) throws IOException {
    String fileName = "c:\\temp\\sample-1GB.txt";
    FileReader fileReader = new FileReader(fileName);

    try (BufferedReader bufferedReader = new BufferedReader(fileReader)) {
      String line;
      while((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
      }
    }
  }

ここで、10のファイル読み取り方法すべての完全なランキングを確認できます。


1
あなたのガイドは素晴らしいです:)
ファイサルジュライダン

あなたはほとんどSystem.out.print/println()ここでタイミングを計っています。また、最初の2つのケースでは、ファイルがメモリに収まると想定しています。
ローンの侯爵

けっこうだ。たぶん私はそれらの仮定を私の答えでより明示的にすることができたでしょう。
gomisha

16

Java 7の場合:

String folderPath = "C:/folderOfMyFile";
Path path = Paths.get(folderPath, "myFileName.csv"); //or any text file eg.: txt, bat, etc
Charset charset = Charset.forName("UTF-8");

try (BufferedReader reader = Files.newBufferedReader(path , charset)) {
  while ((line = reader.readLine()) != null ) {
    //separate all csv fields into string array
    String[] lineVariables = line.split(","); 
  }
} catch (IOException e) {
    System.err.println(e);
}

9
注意してください!この方法でline.splitを使用しても、フィールドにカンマが含まれ、引用符で囲まれている場合は正しく解析されません。この分割はそれを無視し、内部のコンマを使用してフィールドをチャンクで区切るだけです。HTH、マルセロ。
Marcelo Finki 2014年

CSV:カンマ区切り値ファイル。したがって、別のフィールドを追加するつもりでない限り、csvフィールドでカンマを使用しないでください。したがって、CSVファイルの解析が完全に問題なく正しく行われている場合は、Javaでコンマトークンにsplitを使用してください
Diego Duarte

7
ディエゴ、これは正しくありません。唯一のCSV標準(RFC 4180)は、「改行(CRLF)、二重引用符、およびコンマを含むフィールドは二重引用符で囲む必要がある」と明確に述べています。
serg.nechaev

2
StandardCharsets.UTF_8チェックされた例外を回避するために使用しますCharset.forName("UTF-8")
Aleksandr Dubinsky

2
「ディエゴドゥアルテ」コメントありがとうございます。「serg.nechaev」の返事に同意する。csvファイルに埋め込まれたカンマが「常に」表示されます。人々はこれが受け入れられることを期待しています。すべての敬意をもって。「serg.nechaev」にも感謝します。私見あなたは正しいです。みんな元気。
Marcelo Finki 2015年

13

Java 8では、を使用する代わりの方法もありFiles.lines()ます。入力ソースがファイルではなく、Readerやのようなより抽象的なものである場合、s メソッドを介して行をストリーミングInputStreamできますBufferedReaderlines()

例えば:

try (BufferedReader reader = new BufferedReader(...)) {
  reader.lines().forEach(line -> processLine(line));
}

processLine()、によって読み取られる各入力行を呼び出しますBufferedReader


10

以下のためのファイルを読み込むのJavaと8

package com.java.java8;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

/**
 * The Class ReadLargeFile.
 *
 * @author Ankit Sood Apr 20, 2017
 */
public class ReadLargeFile {

    /**
     * The main method.
     *
     * @param args
     *            the arguments
     */
    public static void main(String[] args) {
        try {
            Stream<String> stream = Files.lines(Paths.get("C:\\Users\\System\\Desktop\\demoData.txt"));
            stream.forEach(System.out::println);
        }
        catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

9

あなたはスキャナークラスを使うことができます

Scanner sc=new Scanner(file);
sc.nextLine();

2
@ティム「恐ろしく爆弾」は私がCSで認識している用語ではありません。どういう意味ですか?
ローン侯爵2013年

行き詰まり、実行速度が非常に遅く、おそらくクラッシュします。私はおそらくこのサイトのイディオムを避けるべきです;)
Tim

4
@ティムなぜそうするのですか?
xehpuk 2015

2
使用Scannerは問題ありませんが、この回答には、適切に使用するための完全なコードは含まれていません。
Aleksandr Dubinsky 2015年

5
@Timこのコードは、「恐ろしく爆弾」も「停止」も「非常にゆっくり実行」も「おそらくクラッシュ」することもありません。実際のところ、書かれているとおり、ほとんど瞬時に1行しか読み取られません。この方法で毎秒メガバイトを読み取ることができますBufferedReader.readLine()が、確かに数倍高速です。それ以外の場合は、理由を教えてください。
ローンの侯爵

7

readLine()メソッドを使用する必要がありますclass BufferedReader。そのクラスから新しいオブジェクトを作成し、このメソッドを操作して、文字列に保存します。

BufferReader Javadoc


BufferReaderAPIへのリンクが壊れているようです
Sandeep

6

これを達成する明確な方法は、

例えば:

dataFile.txt現在のディレクトリにいる場合

import java.io.*;
import java.util.Scanner;
import java.io.FileNotFoundException;

public class readByLine
{
    public readByLine() throws FileNotFoundException
    {
        Scanner linReader = new Scanner(new File("dataFile.txt"));

        while (linReader.hasNext())
        {
            String line = linReader.nextLine();
            System.out.println(line);
        }
        linReader.close();

    }

    public static void main(String args[])  throws FileNotFoundException
    {
        new readByLine();
    }
}

以下のような出力、 ここに画像の説明を入力してください


なぜそれがより明確なのですか?ここにテキストの写真を投稿しないでください。テキストを投稿します。
ローン侯爵

写真を投稿しました。文字の絵です。このページに直接テキストをカットアンドペーストすることもできます。プログラムの投稿については誰も何も言わなかった。テキストの写真を投稿することは、私が気にしないあなたの時間の浪費であり、私が行う帯域幅を無駄にします。
ローン侯爵

6

Java 9:

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
    stream.forEach(System.out::println);
}

2
私はあなたがする必要があると思いますSystem.getProperty("os.name").equals("Linux")
SpringLearner 2015

5
文字列を==!と比較しないでください。
JonasCz-モニカを2016年

6
これは、他の人がすでに投稿した、標準的なJava 8の例です。これが「Java-9」だと主張するのはなぜですか?
Holger 2017

彼が言及するのを忘れていた@Holgerのメモリマップファイルは?
ユージーン

行ごとに処理するには、(Stream <String> stream = Files.lines(Paths.get(inputFile))){stream.forEach((line)-> {System.out.println(line);} ); }
thanos.a

3
BufferedReader br;
FileInputStream fin;
try {
    fin = new FileInputStream(fileName);
    br = new BufferedReader(new InputStreamReader(fin));

    /*Path pathToFile = Paths.get(fileName);
    br = Files.newBufferedReader(pathToFile,StandardCharsets.US_ASCII);*/

    String line = br.readLine();
    while (line != null) {
        String[] attributes = line.split(",");
        Movie movie = createMovie(attributes);
        movies.add(movie);
        line = br.readLine();
    }
    fin.close();
    br.close();
} catch (FileNotFoundException e) {
    System.out.println("Your Message");
} catch (IOException e) {
    System.out.println("Your Message");
}

わたしにはできる。それもあなたを助けることを願っています。


3

ストリームを使用して、より正確に行うことができます。

Files.lines(Paths.get("input.txt")).forEach(s -> stringBuffer.append(s);

2
私はそれが実際には大丈夫であることに同意します。たぶん、変なStringBufferの選択のため、人々はそれを嫌います(変数の名前としては不適切かもしれないが、StringBuilderが一般的に好まれます)。また、すでに上記で説明されているためです。
Andrii Rubtsov

2

私は通常、読書ルーチンを簡単に行います。

void readResource(InputStream source) throws IOException {
    BufferedReader stream = null;
    try {
        stream = new BufferedReader(new InputStreamReader(source));
        while (true) {
            String line = stream.readLine();
            if(line == null) {
                break;
            }
            //process line
            System.out.println(line)
        }
    } finally {
        closeQuiet(stream);
    }
}

static void closeQuiet(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ignore) {
        }
    }
}

0

次のコードを使用できます。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadTextFile {

    public static void main(String[] args) throws IOException {

        try {

            File f = new File("src/com/data.txt");

            BufferedReader b = new BufferedReader(new FileReader(f));

            String readLine = "";

            System.out.println("Reading file using Buffered Reader");

            while ((readLine = b.readLine()) != null) {
                System.out.println(readLine);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

説明が正しいでしょう。
Peter Mortensen

0

org.apache.commons.ioパッケージを使用することにより、特にJava 6以下を使用するレガシーコードでより高いパフォーマンスが得られました。

Java 7のAPIはより優れており、例外処理が少なく、メソッドがより便利です。

LineIterator lineIterator = null;
try {
    lineIterator = FileUtils.lineIterator(new File("/home/username/m.log"), "windows-1256"); // The second parameter is optionnal
    while (lineIterator.hasNext()) {
        String currentLine = lineIterator.next();
        // Some operation
    }
}
finally {
    LineIterator.closeQuietly(lineIterator);
}

メイベン

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

0

Apache Commons IOを使用することもできます。

File file = new File("/home/user/file.txt");
try {
    List<String> lines = FileUtils.readLines(file);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

3
FileUtils.readLines(file)廃止されたメソッドです。さらに、このメソッドはIOUtils.readLines、BufferedReaderとArrayListを使用するを呼び出します。これは行単位の方法ではなく、数GBを読み取るのに実用的な方法でもありません。
vallismortis 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.