Jak odczytać konkretny wiersz przy użyciu określonego numeru wiersza z pliku w Javie?

91

Czy w Javie jest jakaś metoda odczytu określonego wiersza z pliku? Na przykład przeczytaj wiersz 32 lub inny numer wiersza.

trójca
źródło
5
Wiem, że jestem bardzo spóźniony, ale na wypadek, gdyby ktoś znalazł to pytanie: Zauważ, że plik to tylko sekwencja bajtów. A znak nowej linii to tylko znak (dwa w systemie Windows), a znak to tylko jeden (lub więcej, w zależności od kodowania) bajt. I bez indeksu pozycji bajtu w pliku, jedynym sposobem, aby dowiedzieć się, gdzie te bajty są, jest przeczytanie go i wyszukanie. (nie oznacza to jednak, że musisz załadować cały plik do pamięci).
szgal

Odpowiedzi:

72

Jeśli nie masz wcześniejszej wiedzy na temat wierszy w pliku, nie ma możliwości bezpośredniego dostępu do 32. linii bez przeczytania 31 poprzednich wierszy.

Dotyczy to wszystkich języków i wszystkich nowoczesnych systemów plików.

Tak efektywnie będziesz po prostu czytać wiersze, aż znajdziesz 32..

Joachim Sauer
źródło
4
„Wcześniejsza wiedza” może być tak prosta, jak dokładny wzorzec rozmiaru linii w pliku, dzięki czemu można szukać w określonej pozycji.
Murali VP
2
@Murali: oczywiście. Może to być również indeks numeru linii do przesunięcia bajtów lub dowolna ich kombinacja.
Joachim Sauer
104

W przypadku małych plików:

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

W przypadku dużych plików:

try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    line32 = lines.skip(31).findFirst().get();
}
aioobe
źródło
Zwraca java.nio.charset.MalformedInputException: Input length = 1, gdy jest używany z wartością skip 1
canadiancreed
To nie jest problem z kodem, który opublikowałem, to problem z kodowaniem twojego pliku. Zobacz ten post .
aioobe
4
„Zauważ, że ta metoda jest przeznaczona dla prostych przypadków, w których wygodnie jest odczytać wszystkie wiersze w jednej operacji. Nie jest przeznaczona do czytania w dużych plikach”. - Javadoc of Files.readAllLines ()
PragmaticProgrammer
Poniższy kod wymaga interfejsu API na poziomie 26 w systemie Android. Jeśli nasze minimum jest mniejsze niż ten poziom, to nie działa
Ali Azaz Alam
38

Nie żebym o tym wiedział, ale co możesz zrobić, to pętla przez pierwsze 31 linii, nie robiąc nic za pomocą funkcji readline () BufferedReader

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();
Chris Thompson
źródło
1
Zwróć uwagę, że tutaj czytasz wiersz numer 30, który jest wierszem 31, ponieważ numery wierszy zaczynamy od 0, więc sugerowałbym zmianę go, aby dokładnie dopasować pytanie.
kiedysktos
15

Joachim jest oczywiście włączony, a alternatywną implementacją dla Chrisa (tylko dla małych plików, ponieważ ładuje cały plik) może być użycie commons-io z Apache (chociaż prawdopodobnie nie chcesz wprowadzać nowej zależności tylko dla jeśli jednak uznasz to za przydatne również do innych rzeczy, może to mieć sens).

Na przykład:

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)

Charlie Collins
źródło
2
Chociaż to załadowałoby cały plik do pamięci przed przejściem do 32. linii, co może nie być praktyczne w przypadku dużego pliku i byłoby wolniejsze niż czytanie za pomocą znalezienia 32. linii ...
beny23
Słuszna uwaga, tak. Użyłem tego w przypadkach testowych, w których pliki testowe były małe, jednak w przypadku dużych plików zgodziłem się, nie jest to dobre podejście.
Charlie Collins
Zaktualizowany post w odpowiedzi, aby odzwierciedlić, że FileUtils jest złym pomysłem w tym przypadku użycia, jeśli masz duży plik i nie potrzebujesz niczego poza wierszem X.
Charlie Collins,
11

Możesz wypróbować program indexed-file-reader (Apache License 2.0). Klasa IndexedFileReader ma metodę o nazwie readLines (int from, int to), która zwraca SortedMap, którego kluczem jest numer wiersza, a wartością jest odczytany wiersz.

Przykład:

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]"));      

Powyższy przykład odczytuje plik tekstowy złożony z 50 wierszy w następującym formacie:

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

Disclamer: Napisałem tę bibliotekę

jramoyo
źródło
6

Chociaż, jak powiedziano w innych odpowiedziach, nie jest możliwe dotarcie do dokładnej linii bez wcześniejszej znajomości przesunięcia (wskaźnika). Tak więc osiągnąłem to, tworząc tymczasowy plik indeksu, który będzie przechowywać wartości przesunięcia każdej linii. Jeśli plik jest wystarczająco mały, możesz po prostu przechowywać indeksy (przesunięcie) w pamięci bez potrzeby oddzielnego pliku.

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();

Odwiedź również dokumentację java, aby uzyskać więcej informacji: https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html#mode

Złożoność : to jest O (n), ponieważ odczytuje cały plik raz. Należy pamiętać o wymaganiach dotyczących pamięci. Jeśli jest zbyt duży, aby przechowywać go w pamięci, utwórz plik tymczasowy, który przechowuje przesunięcia zamiast ArrayList, jak pokazano powyżej.

Uwaga : Jeśli chcesz mieć wszystko w linii „32”, wystarczy wywołać funkcję readLine (), która jest dostępna również dla innych klas „32” razy. Powyższe podejście jest przydatne, jeśli chcesz uzyskać określoną linię (w oparciu o numer linii oczywiście) wiele razy.

Dzięki !

ShankarDaruga
źródło
Jest coś wymagającego edycji, aby powyższy kod działał. A jeśli ktoś potrzebuje zestawu znaków, naprawdę powinieneś przeczytać to: stackoverflow.com/a/37275357/3198960
rufushuang
5

Inny sposób.

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);
}
rhel.user
źródło
1
Eleganckie, lubię to.
Florescu Cătălin,
4

Nie, chyba że w tym formacie pliku długości linii są z góry określone (np. Wszystkie linie o stałej długości), będziesz musiał iterować wiersz po wierszu, aby je policzyć.

Rosół
źródło
2

Jeśli mówisz o pliku tekstowym, to naprawdę nie ma sposobu na zrobienie tego bez przeczytania wszystkich linii, które go poprzedzają - w końcu linie są określane przez obecność nowej linii, więc należy ją przeczytać.

Użyj strumienia obsługującego readline i po prostu przeczytaj pierwsze wiersze X-1 i zrzuć wyniki, a następnie przetwórz następny.

Uri
źródło
2

W Javie 8

W przypadku małych plików:

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

W przypadku dużych plików:

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

W Javie 7

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

Źródło: odczyt n-tej linii z pliku

Sid
źródło
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;
    }
Cooper Scott
źródło
1

U mnie to działa: połączyłem odpowiedź Czytanie prostego pliku tekstowego

Ale zamiast zwracać String, zwracam LinkedList of Strings. Następnie mogę wybrać żądaną linię.

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;
}
Tachenko
źródło
Możesz wtedy zrobić: linkedlist.get (31)
Tachenko
1

Użyj tego kodu:

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);  
    }

}
Usman Yaqoob
źródło
0

Możesz użyć LineNumberReader zamiast BufferedReader. Przejdź przez interfejs API. Możesz znaleźć metody setLineNumber i getLineNumber.

Raghavendra
źródło
nie pomaga - nadal musisz przeczytać wszystkie linijki przed ... poza tym, że oszukujesz i ustawisz pierwszą linię na 32 <g>
kleopatra
0

Możesz również przyjrzeć się LineNumberReader, podklasie BufferedReader. Wraz z metodą readline posiada również metody setter / getter umożliwiające dostęp do numeru linii. Bardzo przydatne do śledzenia liczby czytanych wierszy podczas odczytywania danych z pliku.

Ankur Shanbhag
źródło
0

możesz użyć funkcji skip (), aby pominąć linie od początku.

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);

}
Neelay Solanki
źródło
0

EASY WAY - Czytanie linii za pomocą numeru linii. Powiedzmy, że numer linii zaczyna się od 1 do zera.

public class TextFileAssignmentOct {
    
    private void readData(int rowNum, BufferedReader br) throws IOException {
        int n=1;                                    //Line number starts from 1
        String row;
        while((row=br.readLine()) != null)  {       // Reads every line
            if (n == rowNum) {                      // When Line number matches with which you want to read
                System.out.println(row);
            }
            n++;                                    //This increments Line number
        }
    }

    public static void main(String[] args) throws IOException {
        File f = new File("../JavaPractice/FileRead.txt");
        FileReader fr = new FileReader(f);
        BufferedReader br = new BufferedReader(fr);
        
        TextFileAssignmentOct txf = new TextFileAssignmentOct();
        txf.readData(4, br);    //Read a Specific Line using Line number and Passing buffered reader
    }
}
Amit Verma
źródło
-7

Wszyscy się mylą. Napisałem to w około 10 sekund. Dzięki temu udało mi się po prostu wywołać object.getQuestion ("numer linii") w głównej metodzie, aby zwrócić dowolną linię.

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; }}
user3526115
źródło