¿Cómo leer una línea específica usando el número de línea específico de un archivo en Java?

90

En Java, ¿existe algún método para leer una línea en particular de un archivo? Por ejemplo, lea la línea 32 o cualquier otro número de línea.

trinidad
fuente
5
Sé que llegué muy tarde, pero en caso de que alguien encuentre esta pregunta: Tenga en cuenta que un archivo es solo una secuencia de bytes. Y una nueva línea es solo un carácter (dos en Windows) y un carácter es solo uno (o más, dependiendo de la codificación) byte. Y, sin tener un índice de las posiciones de un byte en un archivo, la única forma de saber dónde están esos bytes es leerlo y buscarlo. (Sin embargo, esto no significa que tenga que cargar todo el archivo en la memoria).
szgal

Respuestas:

71

A menos que tenga conocimientos previos sobre las líneas del archivo, no hay forma de acceder directamente a la línea 32 sin leer las 31 líneas anteriores.

Eso es cierto para todos los idiomas y todos los sistemas de archivos modernos.

Así que, efectivamente, simplemente leerá las líneas hasta que encuentre la 32ª.

Joachim Sauer
fuente
4
El "conocimiento previo" puede ser tan simple como un patrón de tamaño de línea exacto en el archivo para que pueda buscar una determinada posición.
Murali VP
2
@Murali: por supuesto. También podría ser un índice de número de línea a byte offset o cualquier combinación de los mismos.
Joachim Sauer
103

Para archivos pequeños:

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

Para archivos grandes:

try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    line32 = lines.skip(31).findFirst().get();
}
aioobe
fuente
Esto devuelve java.nio.charset.MalformedInputException: longitud de entrada = 1 cuando se usa con un valor de omisión que es 1
canadiancreed
Eso no es un problema con el código que publiqué, es un problema con la codificación de su archivo. Vea esta publicación .
aioobe
3
"Tenga en cuenta que este método está diseñado para casos simples en los que es conveniente leer todas las líneas en una sola operación. No está diseñado para leer archivos grandes". - Javadoc de Files.readAllLines ()
PragmaticProgrammer
El siguiente código requiere API nivel 26 en Android. Si nuestro mínimo es menor que este nivel, entonces no funciona
Ali Azaz Alam
38

No que yo sepa, pero lo que podría hacer es recorrer las primeras 31 líneas sin hacer nada usando la función readline () de 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
fuente
1
Tenga en cuenta que aquí lee la línea número 30, que es la línea 31 porque comenzamos los números de línea desde 0. Por lo tanto, sugeriría cambiarlo para que coincida con la pregunta con precisión.
kiedysktos
15

Joachim tiene razón, por supuesto, y una implementación alternativa a la de Chris (para archivos pequeños solo porque carga el archivo completo) podría ser usar commons-io de Apache (aunque podría decirse que es posible que no desee introducir una nueva dependencia solo para esto, si lo encuentra útil para otras cosas también, podría tener sentido).

Por ejemplo:

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
fuente
2
A pesar de que se carga todo el archivo en la memoria antes de ir a la línea 32, que puede no ser práctico con un archivo grande y sería más lento que la lectura util para encontrar la línea 32a ...
beny23
Buen punto, sí. Lo he usado en casos de prueba, donde los archivos de prueba eran pequeños, en archivos grandes, aunque, de acuerdo, no es un buen enfoque.
Charlie Collins
Publicación de respuesta actualizada para reflejar que FileUtils es una mala idea en este caso de uso si tiene un archivo grande y no necesita nada más allá de la línea X.
Charlie Collins
11

Puede probar el lector de archivos indexados (Licencia Apache 2.0). La clase IndexedFileReader tiene un método llamado readLines (int from, int to) que devuelve un SortedMap cuya clave es el número de línea y el valor es la línea que se leyó.

Ejemplo:

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

El ejemplo anterior lee un archivo de texto compuesto por 50 líneas en el siguiente formato:

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

Descargo de responsabilidad: escribí esta biblioteca

jramoyo
fuente
6

Aunque como se dijo en otras respuestas, no es posible llegar a la línea exacta sin conocer antes el desplazamiento (puntero). Entonces, lo logré creando un archivo de índice temporal que almacenaría los valores de compensación de cada línea. Si el archivo es lo suficientemente pequeño, puede almacenar los índices (desplazamiento) en la memoria sin necesidad de un archivo separado para él.

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

También visite los documentos de Java para obtener más información: https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html#mode

Complejidad : Esto es O (n) ya que lee todo el archivo una vez. Tenga en cuenta los requisitos de memoria. Si es demasiado grande para estar en la memoria, cree un archivo temporal que almacene las compensaciones en lugar de ArrayList como se muestra arriba.

Nota : Si todo lo que desea en la línea '32', solo tiene que llamar a readLine () también disponible a través de otras clases '32' veces. El enfoque anterior es útil si desea obtener una línea específica (según el número de línea, por supuesto) varias veces.

Gracias !

ShankarDaruga
fuente
Hay algo que se necesita editar para que el código anterior funcione. Y si alguien necesita un juego de caracteres preocupado, realmente debería leer esto: stackoverflow.com/a/37275357/3198960
rufushuang
5

De otra manera.

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
fuente
1
Elegante, me gusta.
Florescu Cătălin
4

No, a menos que en ese formato de archivo las longitudes de línea estén predeterminadas (por ejemplo, todas las líneas con una longitud fija), tendrá que iterar línea por línea para contarlas.

caldo
fuente
2

Si está hablando de un archivo de texto, entonces realmente no hay forma de hacerlo sin leer todas las líneas que lo preceden. Después de todo, las líneas están determinadas por la presencia de una nueva línea, por lo que debe leerse.

Use una secuencia que admita readline, y simplemente lea las primeras líneas X-1 y descargue los resultados, luego procese la siguiente.

Uri
fuente
2

En Java 8,

Para archivos pequeños:

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

Para archivos grandes:

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

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

Fuente: lectura de la enésima línea del archivo

Sid
fuente
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
fuente
1

Me funciona: he combinado la respuesta de Leer un archivo de texto simple

Pero en lugar de devolver una cadena, estoy devolviendo una LinkedList de cadenas. Entonces puedo seleccionar la línea que quiero.

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
fuente
A continuación, puede hacer: Linkedlist.get (31)
Tachenko
1

Utilice este código:

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
fuente
0

Puede utilizar LineNumberReader en lugar de BufferedReader. Revisa la api. Puede encontrar los métodos setLineNumber y getLineNumber.

Raghavendra
fuente
no ayuda, todavía tienes que leer todas las líneas antes ... excepto que haces trampa y estableces la primera línea en 32 <g>
kleopatra
0

También puede echar un vistazo a LineNumberReader, subclase de BufferedReader. Junto con el método readline, también tiene métodos setter / getter para acceder al número de línea. Muy útil para realizar un seguimiento del número de líneas leídas, mientras lee los datos del archivo.

Ankur Shanbhag
fuente
0

puede utilizar la función skip () para omitir las líneas desde el principio.

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
fuente
-7

Están todos equivocados. Escribí esto en unos 10 segundos. Con esto me las arreglé para llamar al object.getQuestion ("linoumber") en el método principal para devolver la línea que quiera.

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