Come leggere una riga specifica utilizzando il numero di riga specifico da un file in Java?


90

In Java, esiste un metodo per leggere una particolare riga da un file? Ad esempio, leggi la riga 32 o qualsiasi altro numero di riga.


5
So di essere molto in ritardo, ma nel caso qualcuno trovasse questa domanda: nota che un file è solo una sequenza di byte. E una nuova riga è solo un carattere (due su Windows) e un carattere è solo uno (o più, a seconda della codifica) byte. E, senza avere un indice delle posizioni di un byte in un file, l'unico modo per sapere dove si trovano quei byte è leggerlo e cercarlo. (questo non significa che devi caricare l'intero file in memoria).
szgal

Risposte:


71

A meno che tu non abbia una conoscenza precedente delle righe nel file, non c'è modo di accedere direttamente alla 32a riga senza leggere le 31 righe precedenti.

Questo vale per tutte le lingue e tutti i file system moderni.

Quindi in modo efficace leggerai semplicemente le righe finché non avrai trovato la 32a.


4
La "conoscenza precedente" può essere semplice come il modello della dimensione esatta della riga nel file in modo da poter cercare una certa posizione.
Murali VP

2
@Murali: ovviamente. Potrebbe anche essere un indice del numero di riga per l'offset di byte o qualsiasi combinazione di questi.
Joachim Sauer

103

Per file piccoli:

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

Per file di grandi dimensioni:

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

Ciò restituisce java.nio.charset.MalformedInputException: lunghezza di input = 1 se utilizzato con il valore di salto 1
canadiancreed

Non è un problema con il codice che ho pubblicato, è un problema con la codifica del tuo file. Vedi questo post .
aioobe

3
"Si noti che questo metodo è inteso per casi semplici in cui è conveniente leggere tutte le righe in un'unica operazione. Non è inteso per la lettura di file di grandi dimensioni." - Javadoc di Files.readAllLines ()
PragmaticProgrammer

Il codice seguente richiede il livello API 26 in Android. Se il nostro minimo è inferiore a questo livello, allora non funziona
Ali Azaz Alam

38

Non che io sappia, ma quello che potresti fare è scorrere le prime 31 righe senza fare nulla usando la funzione readline () di 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();

1
Si noti che qui si legge la riga numero 30, che è la 31a riga perché iniziamo i numeri di riga da 0. Quindi suggerirei di cambiarla in modo che corrisponda esattamente alla domanda.
kiedysktos

15

Joachim ha ragione, ovviamente, e un'implementazione alternativa a quella di Chris (solo per file piccoli perché carica l'intero file) potrebbe essere quella di usare commons-io da Apache (anche se probabilmente potresti non voler introdurre una nuova dipendenza solo per questo, se lo trovi utile anche per altre cose, potrebbe avere senso).

Per esempio:

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
Anche se questo caricherà l'intero file in memoria prima di passare alla 32a riga, il che potrebbe non essere pratico con un file di grandi dimensioni e sarebbe più lento della lettura di util che trova la 32a riga ...
beny23

Giusto punto, sì. L'ho usato nei casi di test, in cui i file di test erano piccoli, su file di grandi dimensioni però, d'accordo, non un buon approccio.
Charlie Collins,

Post di risposta aggiornato per riflettere che FileUtils è una cattiva idea in questo caso d'uso se si dispone di un file di grandi dimensioni e non è necessario nulla oltre la riga X.
Charlie Collins

11

Puoi provare il lettore di file indicizzati (licenza Apache 2.0). La classe IndexedFileReader ha un metodo chiamato readLines (int from, int to) che restituisce un SortedMap la cui chiave è il numero di riga e il valore è la riga che è stata letta.

Esempio:

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

L'esempio precedente legge un file di testo composto da 50 righe nel seguente formato:

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

Disclamer: ho scritto questa libreria


6

Sebbene come detto in altre risposte, non sia possibile arrivare alla linea esatta senza conoscere prima l'offset (puntatore). Quindi, ho ottenuto questo risultato creando un file di indice temporaneo che memorizzerebbe i valori di offset di ogni riga. Se il file è abbastanza piccolo, puoi semplicemente memorizzare gli indici (offset) in memoria senza bisogno di un file separato per esso.

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

Visita anche i documenti java per ulteriori informazioni: https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html#mode

Complessità : è O (n) poiché legge l'intero file una volta. Si prega di essere consapevoli dei requisiti di memoria. Se è troppo grande per essere in memoria, crea un file temporaneo che memorizzi gli offset invece di ArrayList come mostrato sopra.

Nota : se tutto quello che vuoi nella riga "32", devi solo chiamare readLine () disponibile anche attraverso le altre classi "32" volte. L'approccio precedente è utile se si desidera ottenere più volte una riga specifica (basata sul numero di riga, ovviamente).

Grazie !


C'è qualcosa che deve essere modificato per far funzionare il codice sopra. E se qualcuno bisogno charset interessato, u davvero dovrebbe leggere questo: stackoverflow.com/a/37275357/3198960
rufushuang

5

Un altro modo.

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
Elegante, mi piace.
Florescu Cătălin

4

No, a meno che in quel formato di file le lunghezze delle righe non siano predeterminate (es. Tutte le righe con una lunghezza fissa), sarà necessario iterare riga per riga per contarle.


2

Se stai parlando di un file di testo, non c'è davvero modo di farlo senza leggere tutte le righe che lo precedono - Dopo tutto, le righe sono determinate dalla presenza di una nuova riga, quindi deve essere letta.

Usa un flusso che supporti readline, leggi le prime righe X-1 e scarica i risultati, quindi elabora quello successivo.


2

In Java 8,

Per file piccoli:

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

Per file di grandi dimensioni:

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

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

Fonte: lettura dell'ennesima riga dal file


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

Per me funziona: ho combinato la risposta di Leggere un semplice file di testo

Ma invece di restituire una stringa restituisco una LinkedList of Strings. Quindi posso selezionare la riga che desidero.

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

Puoi quindi fare: linkedlist.get (31)
Tachenko

1

Usa questo codice:

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

Puoi usare LineNumberReader invece di BufferedReader. Passa attraverso l'API. Puoi trovare i metodi setLineNumber e getLineNumber.


non aiuta - devi ancora leggere tutte le righe prima ... tranne che barare e impostare la prima riga a 32 <g>
kleopatra

0

Puoi anche dare un'occhiata a LineNumberReader, sottoclasse di BufferedReader. Insieme al metodo readline, ha anche metodi setter / getter per accedere al numero di riga. Molto utile per tenere traccia del numero di righe lette, durante la lettura dei dati dal file.


0

puoi usare la funzione skip () per saltare le righe dall'inizio.

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

Sono tutti sbagliati, l'ho scritto in circa 10 secondi. Con questo sono riuscito a chiamare semplicemente object.getQuestion ("linoumber") nel metodo principale per restituire qualsiasi riga voglio.

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; }}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.