Javaのファイルから特定の行番号を使用して特定の行を読み取る方法は?


90

Javaでは、ファイルから特定の行を読み取る方法はありますか?たとえば、32行目またはその他の行番号を読み取ります。


5
私は非常に遅いことを知っていますが、誰かがこの質問を見つけた場合に備えて:ファイルは単なるバイトのシーケンスであることに注意してください。そして、改行は単なる文字(Windowsでは2つ)であり、文字は1つ(エンコードによっては1つ以上)バイトです。そして、ファイル内のバイトの位置のインデックスがない場合、それらのバイトがどこにあるかを知る唯一の方法は、それを読み取って探すことです。(ただし、ファイル全体をメモリにロードする必要があるという意味ではありません)。
szgal 2015

回答:


71

ファイル内の行に関する事前の知識がない限り、前の31行を読み取らずに32行目に直接アクセスする方法はありません。

これは、すべての言語とすべての最新のファイルシステムに当てはまります。

つまり、32行目が見つかるまで、単に行を読み取るだけです。


4
「事前知識」は、ファイル内の正確な行サイズのパターンと同じくらい単純なので、特定の位置にシークできます。
Murali副社長、

2
@Murali:もちろんです。また、行番号からバイトオフセットへのインデックス、またはそれらの任意の組み合わせでもかまいません。
Joachim Sauer、

103

小さなファイルの場合:

String line32 = Files.readAllLines(Paths.get("file.txt")).get(32)

大きなファイルの場合:

try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    line32 = lines.skip(31).findFirst().get();
}

これはjava.nio.charset.MalformedInputExceptionを返します。スキップ値が1の場合、入力の長さ= 1
canadiancreed

これは私が投稿したコードの問題ではなく、ファイルのエンコードの問題です。この投稿を参照してください。
aioobe 2017年

3
「このメソッドは、1回の操作ですべての行を読み取るのに便利な単純なケースを対象としています。大きなファイルを読み取ることを目的としていません。」-Files.readAllLines()のJavadoc
PragmaticProgrammer

次のコードでは、AndroidのAPIレベル26が必要です。私たちの最小値がこのレベルよりも小さい場合、それは機能しません
Ali Azaz Alam

38

私が知っていることではありませんが、あなたができることは、BufferedReaderのreadline()関数を使用して何もしない最初の31行をループすることです

FileInputStream fs= new FileInputStream("someFile.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
for(int i = 0; i < 31; ++i)
  br.readLine();
String lineIWant = br.readLine();

1
ここでは、行番号30を読んでいることに注意してください。行番号は0から始まるため、31行目です。質問に正確に一致するように変更することをお勧めします。
kiedysktos

15

もちろん、Joachimは正解です。Chrisの代替実装(ファイル全体をロードするためだけの小さなファイルの場合)は、Apacheからcommons-ioを使用することです(ただし、おそらく、これは、他のことにも役立つ場合は、理にかなっています)。

例えば:

String line32 = (String) FileUtils.readLines(file).get(31);

http://commons.apache.org/io/api-release/org/apache/commons/io/FileUtils.html#readLines(java.io.File、java.lang.String


2
32行目に移動する前にファイル全体をメモリにロードしますが、これは大きなファイルでは実用的ではない可能性があり、32行目を検出するutilを読み取るよりも遅くなります...
beny23

そうですね。私はそれをテストケースで使用しましたが、テストファイルは小さいですが、大きいファイルでは同意しましたが、良いアプローチではありません。
チャーリーコリンズ

あなたが大規模なファイルを持っていると線Xを越えて何も必要としない場合のfileutilsは、このユースケースには悪い考えであることを反映して答えポストを更新しました
チャーリー・コリンズ

11

indexed-file-reader(Apache License 2.0)を試してみてください。IndexedFileReaderクラスにはreadLines(int from、int to)というメソッドがあり、キーが行番号で値が読み取られた行であるSortedMapを返します。

例:

File file = new File("src/test/resources/file.txt");
reader = new IndexedFileReader(file);

lines = reader.readLines(6, 10);
assertNotNull("Null result.", lines);
assertEquals("Incorrect length.", 5, lines.size());
assertTrue("Incorrect value.", lines.get(6).startsWith("[6]"));
assertTrue("Incorrect value.", lines.get(7).startsWith("[7]"));
assertTrue("Incorrect value.", lines.get(8).startsWith("[8]"));
assertTrue("Incorrect value.", lines.get(9).startsWith("[9]"));
assertTrue("Incorrect value.", lines.get(10).startsWith("[10]"));      

上記の例では、50行で構成されるテキストファイルを次の形式で読み取ります。

[1] The quick brown fox jumped over the lazy dog ODD
[2] The quick brown fox jumped over the lazy dog EVEN

Disclamer:このライブラリを作成しました


6

他の答えで述べたように、以前にオフセット(ポインタ)を知らなければ、正確な行に到達することはできません。したがって、すべての行のオフセット値を格納する一時インデックスファイルを作成することで、これを実現しました。ファイルが十分に小さい場合は、個別のファイルを必要とせずに、インデックス(offset)をメモリに格納できます。

The offsets can be calculated by using the RandomAccessFile

    RandomAccessFile raf = new RandomAccessFile("myFile.txt","r"); 
    //above 'r' means open in read only mode
    ArrayList<Integer> arrayList = new ArrayList<Integer>();
    String cur_line = "";
    while((cur_line=raf.readLine())!=null)
    {
    arrayList.add(raf.getFilePointer());
    }
    //Print the 32 line
    //Seeks the file to the particular location from where our '32' line starts
    raf.seek(raf.seek(arrayList.get(31));
    System.out.println(raf.readLine());
    raf.close();

詳細については、java docsもご覧ください。https//docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html#mode

複雑さ:ファイル全体を1回読み取るため、これはO(n)です。メモリ要件に注意してください。大きすぎてメモリに収まらない場合は、上記のようにArrayListではなくオフセットを格納する一時ファイルを作成します。

注意:すべてが「32」行で必要な場合は、他のクラスでも利用可能なreadLine()を「32」回呼び出すだけです。上記のアプローチは、特定の行を(もちろん行番号に基づいて)複数回取得する場合に役立ちます。

よろしくお願いします!


上記のコードを機能させるには何か編集が必要です。そして、誰かが心配する文字セットを必要とするならば、あなたは本当にこれを読むべきです:stackoverflow.com/a/372​​75357/3198960
rufushuang

5

別の方法。

try (BufferedReader reader = Files.newBufferedReader(
        Paths.get("file.txt"), StandardCharsets.UTF_8)) {
    List<String> line = reader.lines()
                              .skip(31)
                              .limit(1)
                              .collect(Collectors.toList());

    line.stream().forEach(System.out::println);
}

1
エレガントで気に入っています。
FlorescuCătălin17年

4

いいえ、そのファイル形式では、行の長さが事前に決定されていない限り(たとえば、固定長のすべての行)、それらを数えるために行ごとに繰り返す必要があります。


2

テキストファイルについて話している場合、その前にあるすべての行を読み取らずにこれを行う方法はありません-結局のところ、行は改行の存在によって決定されるため、読み取る必要があります。

readlineをサポートするストリームを使用し、最初のX-1行を読み取って結果をダンプし、次の行を処理します。


2

Java 8では

小さなファイルの場合:

String line = Files.readAllLines(Paths.get("file.txt")).get(n);

大きなファイルの場合:

String line;
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    line = lines.skip(n).findFirst().get();
}

Java 7

String line;
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    for (int i = 0; i < n; i++)
        br.readLine();
    line = br.readLine();
}

出典:ファイルからn番目の行を読み取る


1
public String readLine(int line){
        FileReader tempFileReader = null;
        BufferedReader tempBufferedReader = null;
        try { tempFileReader = new FileReader(textFile); 
        tempBufferedReader = new BufferedReader(tempFileReader);
        } catch (Exception e) { }
        String returnStr = "ERROR";
        for(int i = 0; i < line - 1; i++){
            try { tempBufferedReader.readLine(); } catch (Exception e) { }
        }
        try { returnStr = tempBufferedReader.readLine(); }  catch (Exception e) { }

        return returnStr;
    }

1

それは私のために働きます:私は単純なテキストファイル読むという答えを組み合わせました

しかし、文字列を返す代わりに、文字列のLinkedListを返しています。次に、必要な行を選択できます。

public static LinkedList<String> readFromAssets(Context context, String filename) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(context.getAssets().open(filename)));
    LinkedList<String>linkedList = new LinkedList<>();
    // do reading, usually loop until end of file reading
    StringBuilder sb = new StringBuilder();
    String mLine = reader.readLine();
    while (mLine != null) {
        linkedList.add(mLine);
        sb.append(mLine); // process line
        mLine = reader.readLine();


    }
    reader.close();
    return linkedList;
}

その後、次のことができます:linkedlist.get(31)
Tachenko

1

このコードを使用してください:

import java.nio.file.Files;

import java.nio.file.Paths;

public class FileWork 
{

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

        String line = Files.readAllLines(Paths.get("D:/abc.txt")).get(1);

        System.out.println(line);  
    }

}

0

BufferedReaderの代わりにLineNumberReaderを使用できます。APIを通過します。setLineNumberメソッドとgetLineNumberメソッドを見つけることができます。


役に立たない-チートして最初の行を32に設定する以外は、前にすべての行を読む必要があります<g>
kleopatra

0

また、BufferedReaderのサブクラスであるLineNumberReaderを確認することもできます。readlineメソッドの他に、行番号にアクセスするためのsetter / getterメソッドもあります。ファイルからデータを読み取る間、読み取られた行数を追跡するのに非常に役立ちます。


0

skip()関数を使用して、行の先頭からスキップできます。

public static void readFile(String filePath, long lineNum) {
    List<String> list = new ArrayList<>();
    long totalLines, startLine = 0;

    try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
        totalLines = Files.lines(Paths.get(filePath)).count();
        startLine = totalLines - lineNum;
        // Stream<String> line32 = lines.skip(((startLine)+1));

        list = lines.skip(startLine).collect(Collectors.toList());
        // lines.forEach(list::add);
    } catch (IOException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

    list.forEach(System.out::println);

}

-7

それらはすべて間違っています。私はこれを約10秒で書きました。これにより、メインメソッドでobject.getQuestion( "linenumber")を呼び出すだけで、必要な行を返すことができました。

public class Questions {

File file = new File("Question2Files/triviagame1.txt");

public Questions() {

}

public String getQuestion(int numLine) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(file));
    String line = "";
    for(int i = 0; i < numLine; i++) {
        line = br.readLine();
    }
    return line; }}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.