提问



我已经使用下面的成语已经有一段时间了。它似乎是最广泛的,至少在我访问过的网站上。


是否有更好/不同的方式将文件读入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();
    }
}

最佳参考


从文件中读取所有文本



这是Java 7的一个紧凑,强大的习惯用法,包含在一个实用方法中:


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


从文件中读取文本行



Java 7添加了一种方便的方法来将文件读取为文本行,表示为List<String>。这种方法是有损的,因为行分隔符是从每一行的末尾剥离的。[87]


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


在Java 8中,BufferedReader添加了一种新方法lines()来产生Stream<String>。如果在读取文件时遇到IOException,它会被包裹在UncheckedIOException中,因为Stream不接受抛出已检查异常的lambda。[88] [89]]]


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


还有Files.lines()方法做了非常相似的事情,直接返回Stream<String>。但我不喜欢它。Stream需要close()调用;这在API上记录很少,我怀疑很多人甚至没有注意到StreamStream Stream close()方法。所以你的代码看起来非常相似,如下所示:


try (Stream<String> lines = Files.lines(path, encoding)) {
  lines.forEach(System.out::println);
}


不同的是你有一个Stream分配给一个变量,我尝试避免这种做法,以便我不小心尝试两次调用流。


内存利用率



保留换行符的第一种方法可以暂时需要几倍于文件大小的内存,因为短时间内原始文件内容(字节数组)和解码后的字符(即使编码也是16位)因为文件中的8位)一次驻留在内存中。最安全的是应用于您知道相对于可用内存较小的文件。


读取行的第二种方法通常更有效,因为用于解码的输入字节缓冲区不需要包含整个文件。但是,它仍然不适用于相对于可用内存非常大的文件。


对于读取大文件,您需要为程序设计不同的设计,一个从流中读取一块文本,处理它,然后继续下一个,重复使用相同的固定大小的内存块。这里,大取决于计算机规格。如今,这个阈值可能是几千兆字节的RAM。第三种方法,使用Stream<String>是一种方法,如果您的输入记录恰好是单独的行。 (使用BufferedReaderreadLine()方法是这种方法的程序等效。)


字符编码



原始帖子中的示例中缺少的一件事是字符编码。在某些特殊情况下,平台默认值是您想要的,但它们很少见,您应该能够证明您的选择。


StandardCharsets类为所有Java运行时所需的编码定义了一些常量:[90]


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


平台默认值可从Charset类本身获得:[91]


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





注意:这个答案很大程度上取代了我的Java 6版本。 Java 7的实用程序安全地简化了代码,使用映射字节缓冲区的旧答案阻止了读取的文件被删除,直到映射的缓冲区被垃圾收集。您可以通过此答案中的已编辑链接查看旧版本。

其它参考1


下议院FileUtils.readFileToString:[92]



public static String readFileToString(File file)
                       throws IOException

  
  使用默认编码将文件内容读入String
  对于VM。该文件始终关闭。

  
  参数:

  
  

      
  • file - 要读取的文件,不能为空

  •   

  
  返回:
  文件内容,永远不会为空

  
  抛出:
    - IOException - 如果发生I/O错误

  
  以来:
  Commons IO 1.3.1



该类(间接)使用的代码是:


Apache License 2.0下的IOUtils.java。[93] [94]


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


它与Ritche_W使用的非常相似。

其它参考2


从这个页面非常精简的解决方案:[95]


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


要么


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


如果要设置charset

其它参考3


如果您正在寻找不涉及第三方库(例如Commons I/O)的替代方案,您可以使用Scanner类:[96] [97]


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

其它参考4


Guava有一种类似于Commons IOUtils的方法,Willi aus Rohr提到:[98]


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

// ...

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


由Oscar Reyes编辑


这是引用库中的(简化)底层代码:


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


编辑(作者Jonik):上面的内容与最近的Guava版本的源代码不匹配。有关当前的源代码,请参阅com.google.common.io中的Files,CharStreams,ByteSource和CharSource类。包。[99] [100] [101] [102] [103]

其它参考5


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

其它参考6


如果您需要字符串处理(并行处理),Java 8具有出色的Stream API。


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


JDK示例sample/lambda/BulkDataOperations中提供了更多示例,可以从Oracle Java SE 8下载页面下载[104]


另一个班轮示例


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

其它参考7


该代码将规范化换行符,这可能是你真正想做的事情,也可能不是。


这是一个没有做到的替代方案,而且(IMO)比NIO代码更容易理解(虽然它仍然使用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();
    }        
}

其它参考8


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


从java 7开始,你就可以这样做。

其它参考9


如果它是一个文本文件,为什么不使用apache commons-io?[105]


它有以下方法


public static String readFileToString(File file) throws IOException


如果您希望使用行作为列表


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

其它参考10


Java试图在其所有方面都非常通用和灵活。因此,在脚本语言中相对简单的东西(你的代码将被python中的open(file).read()替换)要复杂得多。除了使用外部库(如Willi aus Rohr提到的)之外,似乎没有任何更短的方法。你的选择:



  • 使用外部库。

  • 将此代码复制到您的所有项目中。

  • 创建您自己的迷你库,其中包含您经常使用的功能。



你最好的选择可能是第二个,因为它的依赖性最小。

其它参考11


将文件读取为二进制文件并在结尾处进行转换


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

其它参考12


使用Java 7,这是我读取UTF-8文件的首选选项:


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


从Java 7开始,JDK具有新的java.nio.file API,它提供了许多快捷方式,因此简单文件操作并不总是需要第三方库。

其它参考13


在同一主题上有一个变体,它使用for循环而不是while循环来限制行变量的范围。它是否更好是个人品味的问题。


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

其它参考14


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

其它参考15


从JDK 11开始:


String file = ...
Path path = Paths.get(file);
String content = Files.readString(path);
// Or readString(path, someCharset), if you need a Charset different from UTF-8

其它参考16


如果您无权访问文件,则执行下一步:


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

其它参考17


使用Apache commons-io中的IOUtils与StringWriter结合的灵活解决方案:[107] [108] [109]


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


它适用于任何阅读器或输入流(不仅仅是文件),例如从URL读取时。

其它参考18


在使用fileInputStream.available()时要注意,返回的整数不必表示实际的文件大小,而是系统应该能够在不阻塞IO的情况下从流中读取的猜测字节数。一种安全而简单的方式可能看起来像这样


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


应该认为这种方法适用于UTF-8等多字节字符编码。

其它参考19


这个使用方法RandomAccessFile.readFully,似乎可以从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
        }
    }
}

其它参考20


您可以尝试Scanner和File类,几行解决方案


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

其它参考21


在Scanner之后按Ctrl + F键,我认为扫描仪解决方案也应该列出。以最容易阅读的方式,它是这样的:


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


如果您使用Java 7或更新版本(并且您真的应该),请考虑使用try-with-resources来使代码更易于阅读。没有更多关闭垃圾的东西。但这主要是一种风格选择。


我发布这个主要是为了完成主义,因为如果你需要做很多事情,java.nio.file.Files中应该有更好的工作。[110]


我的建议是使用Files#readAllBytes(Path)来获取所有字节,并将其提供给新的String(byte [[]] Charset)以获得一个你可以信任的String。 Charsets在你的一生中对你意味着什么,所以现在要小心这些东西。[111] [112]


其他人给了代码和东西,我不想偷他们的荣耀。;)

其它参考22


使用这个库,它是一行:[113]


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

其它参考23


此外,如果您的文件恰好在jar中,您也可以使用:


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


例如,如果你的罐子是,那么路径应该以/开头


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


然后你想像这样调用它:


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

其它参考24


在一行(Java 8)中,假设您有一个Reader:


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

其它参考25


基于@ erickson的回答,您可以使用:


public String readAll(String fileName) throws IOException {
    List<String> lines = Files.readAllLines(new File(fileName).toPath());
    return String.join("\n", lines.toArray(new String[lines.size()]));
}

其它参考26


收集了从磁盘或网络读取文件为字符串的所有可能方法。



  • Guava:谷歌使用类ResourcesFiles [114] [115] [116]


    static Charset charset = com.google.common.base.Charsets.UTF_8;
    public static String guava_ServerFile( URL url ) throws IOException {
        return Resources.toString( url, charset );
    }
    public static String guava_DiskFile( File file ) throws IOException {
        return Files.toString( file, charset );
    }
    









  • APACHE - 使用类IOUtils,FileUtils [117]的COMMONS IO


    static Charset encoding = org.apache.commons.io.Charsets.UTF_8;
    public static String commons_IOUtils( URL url ) throws IOException {
        java.io.InputStream in = url.openStream();
        try {
            return IOUtils.toString( in, encoding );
        } finally {
            IOUtils.closeQuietly(in);
        }
    }
    public static String commons_FileUtils( File file ) throws IOException {
        return FileUtils.readFileToString( file, encoding );
        /*List<String> lines = FileUtils.readLines( fileName, encoding );
        return lines.stream().collect( Collectors.joining("\n") );*/
    }
    









  • 使用Stream API的Java 8 BufferReader [118] [119]


    public static String streamURL_Buffer( URL url ) throws IOException {
        java.io.InputStream source = url.openStream();
        BufferedReader reader = new BufferedReader( new InputStreamReader( source ) );
        //List<String> lines = reader.lines().collect( Collectors.toList() );
        return reader.lines().collect( Collectors.joining( System.lineSeparator() ) );
    }
    public static String streamFile_Buffer( File file ) throws IOException {
        BufferedReader reader = new BufferedReader( new FileReader( file ) );
        return reader.lines().collect(Collectors.joining(System.lineSeparator()));
    }
    









  • 带正则表达式的扫描仪类\A。它与输入的开头相匹配。


    static String charsetName = java.nio.charset.StandardCharsets.UTF_8.toString();
    public static String streamURL_Scanner( URL url ) throws IOException {
        java.io.InputStream source = url.openStream();
        Scanner scanner = new Scanner(source, charsetName).useDelimiter("\\A");
        return scanner.hasNext() ? scanner.next() : "";
    }
    public static String streamFile_Scanner( File file ) throws IOException {
        Scanner scanner = new Scanner(file, charsetName).useDelimiter("\\A");
        return scanner.hasNext() ? scanner.next() : "";
    }
    









  • Java 7(java.nio.file.Files.readAllBytes)


    public static String getDiskFile_Java7( File file ) throws IOException {
        byte[] readAllBytes = java.nio.file.Files.readAllBytes(Paths.get( file.getAbsolutePath() ));
        return new String( readAllBytes );
    }
    









  • BufferedReader使用InputStreamReader


    public static String getDiskFile_Lines( File file ) throws IOException {
        StringBuffer text = new StringBuffer();
        FileInputStream fileStream = new FileInputStream( file );
        BufferedReader br = new BufferedReader( new InputStreamReader( fileStream ) );
        for ( String line; (line = br.readLine()) != null; )
            text.append( line + System.lineSeparator() );
        return text.toString();
    }
    








使用main方法访问上述方法的示例。


public static void main(String[] args) throws IOException {
    String fileName = "E:/parametarisation.csv";
    File file = new File( fileName );

    String fileStream = commons_FileUtils( file );
            // guava_DiskFile( file );
            // streamFile_Buffer( file );
            // getDiskFile_Java7( file );
            // getDiskFile_Lines( file );
    System.out.println( " File Over Disk : \n"+ fileStream );


    try {
        String src = "https://code.jquery.com/jquery-3.2.1.js";
        URL url = new URL( src );

        String urlStream = commons_IOUtils( url );
                // guava_ServerFile( url );
                // streamURL_Scanner( url );
                // streamURL_Buffer( url );
        System.out.println( " File Over Network : \n"+ urlStream );
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}


其它参考27


使用JDK 8或更高版本:



没有使用外部库


您可以从文件内容创建一个新的String对象(使用java.nio.file包中的类):


public String readStringFromFile(String filePath) throws IOException {
    String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
    return fileContent;
}

其它参考28


我还不能评论其他条目,所以我会把它留在这里。


这里最好的答案之一(https://stackoverflow.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();
}
}


还有一个缺陷。它总是将新行char放在字符串的末尾,这可能会导致一些奇怪的错误。我的建议是将其改为:


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

其它参考29


使用代码:


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包含String中的输出。