¿Cómo creo una cadena de Java a partir del contenido de un archivo?


He estado utilizando el modismo de abajo desde hace un tiempo. Y parece ser el más extendido, al menos en los sitios que he visitado.

¿Hay alguna forma mejor / diferente de leer un archivo en una cadena en Java?

private String readFile(String file) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader (file));
    String         line = null;
    StringBuilder  stringBuilder = new StringBuilder();
    String         ls = System.getProperty("line.separator");

    try {
        while((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }

        return stringBuilder.toString();
    } finally {
        reader.close();
    }
}


Answers


Leer todo el texto de un archivo

Aquí hay un modismo compacto y robusto para Java 7, envuelto en un método de utilidad:

static String readFile(String path, Charset encoding) 
  throws IOException 
{
  byte[] encoded = Files.readAllBytes(Paths.get(path));
  return new String(encoded, encoding);
}

Leer líneas de texto de un archivo

Java 7 agregó un método conveniente para leer un archivo como líneas de texto, representadas como una List<String> . Este enfoque es "con pérdida" porque los separadores de línea se eliminan del final de cada línea.

List<String> lines = Files.readAllLines(Paths.get(path), encoding);

En Java 8, BufferedReader agregó un nuevo método, lines() para producir un Stream<String> . Si se encuentra una IOException al leer el archivo, se incluye en una IOException UncheckedIOException , ya que Stream no acepta lambdas que arrojen excepciones controladas.

try (BufferedReader r = Files.newBufferedReader(path, encoding)) {
  r.lines().forEach(System.out::println);
}

Utilización de la memoria

El primer método, que preserva los saltos de línea, puede requerir memoria temporalmente varias veces el tamaño del archivo, porque durante un tiempo corto el contenido del archivo sin formato (una matriz de bytes) y los caracteres decodificados (cada uno de 16 bits incluso si está codificado como 8 bits en el archivo) residen en la memoria a la vez. Es más seguro aplicarlo a archivos que sabe que son pequeños en relación con la memoria disponible.

El segundo método, líneas de lectura, suele ser más eficiente en cuanto a la memoria, porque el búfer de bytes de entrada para la decodificación no necesita contener el archivo completo. Sin embargo, todavía no es adecuado para archivos que son muy grandes en relación con la memoria disponible.

Para leer archivos de gran tamaño, necesita un diseño diferente para su programa, uno que lea un fragmento de texto de una secuencia, lo procese y pase al siguiente, reutilizando el mismo bloque de memoria de tamaño fijo. Aquí, "grande" depende de las especificaciones de la computadora. Hoy en día, este umbral podría ser muchos gigabytes de RAM. El tercer método, usar Stream<String> es una forma de hacerlo, si sus "registros" de entrada son líneas individuales. (El método readLine() de BufferedReader es el equivalente de procedimiento de este enfoque).

Codificación de caracteres

Una cosa que falta en la muestra en la publicación original es la codificación de caracteres. Hay algunos casos especiales en los que la plataforma predeterminada es lo que desea, pero son raros, y debe poder justificar su elección.

La clase StandardCharsets define algunas constantes para las codificaciones requeridas de todos los tiempos de ejecución de Java:

String content = readFile("test.txt", StandardCharsets.UTF_8);

La plataforma predeterminada está disponible desde la clase Charset sí:

String content = readFile("test.txt", Charset.defaultCharset());

Nota: Esta respuesta reemplaza en gran medida mi versión de Java 6. La utilidad de Java 7 simplifica el código de forma segura, y la respuesta anterior, que utilizaba un búfer de bytes asignado, impedía que el archivo que se leía se borrara hasta que el búfer mapeado fuera basura. Puede ver la versión anterior a través del enlace "editado" en esta respuesta.




Commons FileUtils.readFileToString :

public static String readFileToString(File file)
                       throws IOException

Lee el contenido de un archivo en una cadena utilizando la codificación predeterminada para la máquina virtual. El archivo siempre está cerrado.

Parámetros:

  • file - el archivo para leer, no debe ser nulo

Devoluciones: el contenido del archivo, nunca nulo

Lanza: - IOException - en caso de un error de E / S

Desde: Commons IO 1.3.1

El código utilizado (indirectamente) por esa clase es:

IOUtils.java bajo Apache License 2.0 .

public static long copyLarge(InputStream input, OutputStream output)
       throws IOException {
   byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
   long count = 0;
   int n = 0;
   while (-1 != (n = input.read(buffer))) {
       output.write(buffer, 0, n);
       count += n;
   }
   return count;
}

Es muy similar al utilizado por Ritche_W.




De esta página una solución muy pobre:

Scanner scanner = new Scanner( new File("poem.txt") );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

o

Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

Si quieres establecer el juego de caracteres




Si está buscando una alternativa que no involucre una biblioteca de terceros (por ejemplo, Commons I / O ), puede usar la clase Scanner :

private String readFile(String pathname) throws IOException {

    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int)file.length());
    Scanner scanner = new Scanner(file);
    String lineSeparator = System.getProperty("line.separator");

    try {
        while(scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine() + lineSeparator);
        }
        return fileContents.toString();
    } finally {
        scanner.close();
    }
}



La guayaba tiene un método similar al de Commons IOUtils que Willi aus Rohr mencionó:

import com.google.common.base.Charsets;
import com.google.common.io.Files;

// ...

String text = Files.toString(new File(path), Charsets.UTF_8);

EDIT por Oscar Reyes

Este es el código subyacente (simplificado) en la biblioteca citada:

InputStream in = new FileInputStream(file);
byte[] b  = new byte[file.length()];
int len = b.length;
int total = 0;

while (total < len) {
  int result = in.read(b, total, len - total);
  if (result == -1) {
    break;
  }
  total += result;
}

return new String( b , Charsets.UTF_8 );

Editar (por Jonik): Lo anterior no coincide con el código fuente de las versiones recientes de Guava. Para la fuente actual, consulte las clases Archivos , CharStreams , ByteSource y CharSource en el paquete com.google.common.io .




import java.nio.file.Files;

.......

 String readFile(String filename) {
            File f = new File(filename);
            try {
                byte[] bytes = Files.readAllBytes(f.toPath());
                return new String(bytes,"UTF-8");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
    }



Si necesita un procesamiento de cadena (procesamiento paralelo) Java 8 tiene la gran API de Stream.

String result = Files.lines(Paths.get("file.txt"))
                    .parallel() // for parallel processing 
                    .map(String::trim) // to change line   
                    .filter(line -> line.length() > 2) // to filter some lines by a predicate                        
                    .collect(Collectors.joining()); // to join lines

Hay más ejemplos disponibles en muestras de JDK sample/lambda/BulkDataOperations que se pueden descargar desde la página de descarga de Oracle Java SE 8

Otro ejemplo de un trazador de líneas

String out = String.join("\n", Files.readAllLines(Paths.get("file.txt")));



Ese código normalizará los saltos de línea, que pueden o no ser lo que realmente quieres hacer.

Aquí hay una alternativa que no hace eso, y que es (IMO) más simple de entender que el código NIO (aunque todavía usa java.nio.charset.Charset ):

public static String readFile(String file, String csName)
            throws IOException {
    Charset cs = Charset.forName(csName);
    return readFile(file, cs);
}

public static String readFile(String file, Charset cs)
            throws IOException {
    // No real need to close the BufferedReader/InputStreamReader
    // as they're only wrapping the stream
    FileInputStream stream = new FileInputStream(file);
    try {
        Reader reader = new BufferedReader(new InputStreamReader(stream, cs));
        StringBuilder builder = new StringBuilder();
        char[] buffer = new char[8192];
        int read;
        while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
            builder.append(buffer, 0, read);
        }
        return builder.toString();
    } finally {
        // Potential issue here: if this throws an IOException,
        // it will mask any others. Normally I'd use a utility
        // method which would log exceptions and swallow them
        stream.close();
    }        
}



String content = new String(Files.readAllBytes(Paths.get("readMe.txt")));

desde Java 7 puedes hacerlo de esta manera.




Si es un archivo de texto, ¿por qué no usar apache commons-io ?

Tiene el siguiente método

public static String readFileToString(File file) throws IOException

Si quieres usar las líneas como una lista

public static List<String> readLines(File file) throws IOException



Para leer un archivo como binario y convertir al final

public static String readFileAsString(String filePath) throws IOException {
    DataInputStream dis = new DataInputStream(new FileInputStream(filePath));
    try {
        long len = new File(filePath).length();
        if (len > Integer.MAX_VALUE) throw new IOException("File "+filePath+" too large, was "+len+" bytes.");
        byte[] bytes = new byte[(int) len];
        dis.readFully(bytes);
        return new String(bytes, "UTF-8");
    } finally {
        dis.close();
    }
}



Java intenta ser extremadamente general y flexible en todo lo que hace. Como resultado, algo que es relativamente simple en un lenguaje de scripting (su código sería reemplazado por " open(file).read() " en python) es mucho más complicado. No parece haber una forma más corta de hacerlo, excepto el uso de una biblioteca externa (como se menciona en Willi aus Rohr ). Sus opciones:

  • Use una biblioteca externa.
  • Copie este código en todos sus proyectos.
  • Cree su propia mini biblioteca que contiene funciones que usa con frecuencia.

Su mejor apuesta es probablemente la segunda, ya que tiene la menor cantidad de dependencias.




Existe una variación en el mismo tema que utiliza un bucle for, en lugar de un bucle while, para limitar el alcance de la variable de línea. Si es "mejor" es una cuestión de gusto personal.

for(String line = reader.readLine(); line != null; line = reader.readLine()) {
    stringBuilder.append(line);
    stringBuilder.append(ls);
}



Con Java 7, esta es mi opción preferida para leer un archivo UTF-8:

String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");

Desde Java 7, el JDK tiene la nueva API java.nio.file , que proporciona muchos accesos directos, por lo que las bibliotecas de terceros no siempre son necesarias para operaciones simples de archivos.




public static String slurp (final File file)
throws IOException {
    StringBuilder result = new StringBuilder();

    try {
        BufferedReader reader = new BufferedReader(new FileReader(file));

        char[] buf = new char[1024];

        int r = 0;

        while ((r = reader.read(buf)) != -1) {
            result.append(buf, 0, r);
        }
    }
    finally {
        reader.close();
    }

    return result.toString();
}



Si no tiene acceso a los archivos, haga lo siguiente:

static String readFile(File file, String charset)
        throws IOException
{
    FileInputStream fileInputStream = new FileInputStream(file);
    byte[] buffer = new byte[fileInputStream.available()];
    int length = fileInputStream.read(buffer);
    fileInputStream.close();
    return new String(buffer, 0, length, charset);
}



Una solución flexible que utiliza IOUtils de Apache commons-io en combinación con StringWriter :

Reader input = new FileReader();
StringWriter output = new StringWriter();
try {
  IOUtils.copy(input, output);
} finally {
  input.close();
}
String fileContents = output.toString();

Funciona con cualquier lector o flujo de entrada (no solo con archivos), por ejemplo cuando lee desde una URL.




Tenga en cuenta que al usar fileInputStream.available() el entero devuelto no tiene que representar el tamaño real del archivo, sino la cantidad de bytes que el sistema debería poder leer de la transmisión sin bloquear el IO. Una manera segura y simple podría verse así

public String readStringFromInputStream(FileInputStream fileInputStream) {
    StringBuffer stringBuffer = new StringBuffer();
    try {
        byte[] buffer;
        while (fileInputStream.available() > 0) {
            buffer = new byte[fileInputStream.available()];
            fileInputStream.read(buffer);
            stringBuffer.append(new String(buffer, "ISO-8859-1"));
        }
    } catch (FileNotFoundException e) {
    } catch (IOException e) { }
    return stringBuffer.toString();
}

Se debe considerar que este enfoque no es adecuado para codificaciones de caracteres multibyte como UTF-8.




¡Este usa el método RandomAccessFile.readFully , parece estar disponible desde JDK 1.0!

public static String readFileContent(String filename, Charset charset) throws IOException {
    RandomAccessFile raf = null;
    try {
        raf = new RandomAccessFile(filename, "r");
        byte[] buffer = new byte[(int)raf.length()];
        raf.readFully(buffer);
        return new String(buffer, charset);
    } finally {
        closeStream(raf);
    }
} 


private static void closeStream(Closeable c) {
    if (c != null) {
        try {
            c.close();
        } catch (IOException ex) {
            // do nothing
        }
    }
}



Después de Ctrl + F'ing después del escáner, creo que la solución del escáner también debe aparecer en la lista. De la manera más fácil de leer, dice así:

public String fileToString(File file, Charset charset) {
  Scanner fileReader = new Scanner(file, charset);
  fileReader.useDelimiter("\\Z"); // \Z means EOF.
  String out = fileReader.next();
  fileReader.close();
  return out;
}

Si usa Java 7 o posterior (y realmente debería) considere usar try-with-resources para hacer que el código sea más fácil de leer. No más cosas de punto que ensucian todo. Pero eso es más que nada una elección estilística.

Estoy publicando esto principalmente para el finalismo, ya que si necesitas hacer esto mucho, debería haber cosas en java.nio.file. Archivos que deberían hacer el trabajo mejor.

Mi sugerencia sería usar Archivos # readAllBytes (Ruta) para tomar todos los bytes y alimentarlos a una nueva Cadena (byte [] Charset) para obtener una Cadena de la que pueda confiar. Charsets será malo para ti durante toda tu vida, así que ten cuidado con estas cosas ahora.

Otros han dado código y otras cosas, y no quiero robarles su gloria. ;)




Usando esta biblioteca , es una línea:

String data = IO.from(new File("data.txt")).toString();



Puede probar la clase de escáner y archivo, algunas líneas de solución

 try
{
  String content = new Scanner(new File("file.txt")).useDelimiter("\\Z").next();
  System.out.println(content);
}
catch(FileNotFoundException e)
{
  System.out.println("not found!");
}



Además, si su archivo está dentro de un contenedor, también puede usar esto:

public String fromFileInJar(String path) {
    try ( Scanner scanner 
            = new Scanner(getClass().getResourceAsStream(path))) {
        return scanner.useDelimiter("\\A").next();
    }
}

La ruta debería comenzar con / por ejemplo si tu jar es

my.jar/com/some/thing/a.txt

Entonces quieres invocarlo así:

String myTxt = fromFileInJar("/com/com/thing/a.txt");



No puedo comentar otras entradas todavía, así que lo dejaré aquí.

Una de las mejores respuestas aquí ( https://.com/a/326448/1521167 ):

private String readFile(String pathname) throws IOException {

File file = new File(pathname);
StringBuilder fileContents = new StringBuilder((int)file.length());
Scanner scanner = new Scanner(file);
String lineSeparator = System.getProperty("line.separator");

try {
    while(scanner.hasNextLine()) {        
        fileContents.append(scanner.nextLine() + lineSeparator);
    }
    return fileContents.toString();
} finally {
    scanner.close();
}
}

todavía tiene un defecto Siempre pone un nuevo carácter de línea al final de la cadena, lo que puede causar algunos errores de weirds. Mi sugerencia es cambiarlo a:

    private String readFile(String pathname) throws IOException {
    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int) file.length());
    Scanner scanner = new Scanner(new BufferedReader(new FileReader(file)));
    String lineSeparator = System.getProperty("line.separator");

    try {
        if (scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine());
        }
        while (scanner.hasNextLine()) {
            fileContents.append(lineSeparator + scanner.nextLine());
        }
        return fileContents.toString();
    } finally {
        scanner.close();
    }
}



En una línea (Java 8), suponiendo que tienes un lector:

String sMessage = String.join("\n", reader.lines().collect(Collectors.toList()));



en java 8, hay una nueva clase

java.util.stream.Stream

Una secuencia representa una secuencia de elementos y admite diferentes tipos de operaciones para realizar cálculos sobre esos elementos

Leer más sobre esto:

Documentación de Oracle

Aquí un ejemplo:

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

public Class ReadFile{
  public  static String readFile(String filePath) {
 StringBuilder  stringBuilder = new StringBuilder();
    String ls = System.getProperty("line.separator");
        try {

            try (Stream<String> lines = Files.lines(Paths.get(filePath), StandardCharsets.UTF_8)) {
                for (String line : (Iterable<String>) lines::iterator) {


                      stringBuilder.append(line);
                      stringBuilder.append(ls);


                }
            }

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

      return stringBuilder.toString(); 


}

}



Use el código:

File file = new File("input.txt");
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(
                file));
byte[] buffer = new byte[(int) file.length()];
bin.read(buffer);
String fileStr = new String(buffer);

fileStr contiene salida en String.