我如何从文件中创建Java字符串

How do I create a Java string from the contents of a file?

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

在Java中读取文件到字符串中有更好的方法吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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的一个紧凑、健壮的习惯用法,它包含在一个实用工具中:

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

从文件中读取文本行

Java 7增加了一种方便的方法来读取文件作为文本行,表示为EDCOX1(2)。这种方法是"有损的",因为线路分隔符是从每一条线路的末端剥离出来的。

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

Java 8添加了EDCOX1×3的方法来产生EDCOX1×4。同样,这种方法也是有损的,因为行分隔符被剥离了。如果在读取文件时遇到IOException,则将其包装在UncheckedIOException中,因为Stream不接受抛出检查异常的lambda。

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

这个Stream需要一个close()调用;这个调用在API上记录得很差,我怀疑很多人甚至没有注意到Stream有一个close()方法。确保如图所示使用臂块。

如果您使用的是文件以外的源,则可以在BufferedReader中使用lines()方法。

内存利用率

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

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

对于读取大型文件,您需要为您的程序设计一种不同的设计,即从流中读取文本块,对其进行处理,然后转到下一个,重用相同的固定大小的内存块。这里,"大"取决于计算机规格。现在,这个阈值可能是许多千兆字节的RAM。第三种方法,使用Stream是实现这一点的一种方法,如果您的输入"记录"恰好是单独的行。(使用BufferedReaderreadLine()方法,程序上等同于这种方法。)

字符编码

原始日志中样本中缺少的一件事是字符编码。有些特殊情况下,平台默认值是您想要的,但它们很少,您应该能够证明自己的选择是正确的。

EDCOX1×17类定义了所有Java运行时所需的编码的常数:

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

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

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

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


公地FileUtils.readFileToString

1
2
public static String readFileToString(File file)
                       throws IOException

Reads the contents of a file into a String using the default encoding
for the VM. The file is always closed.

Parameters:

  • file - the file to read, must not be null

Returns:
the file contents, never null

Throws:
- IOException - in case of an I/O error

Since:
Commons IO 1.3.1

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

ioutils.java,根据Apache许可证2.0。

1
2
3
4
5
6
7
8
9
10
11
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使用的。


从本页中可以看出一个非常精简的解决方案:

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

1
2
3
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

如果你想设置字符集


1
2
3
4
import java.nio.file.Files;
import java.nio.file.Paths;

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

自Java 7以来,你可以这样做。


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

1
2
3
4
5
6
7
8
9
10
11
12
private String readFile(String pathname) throws IOException {

    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int)file.length());        

    try (Scanner scanner = new Scanner(file)) {
        while(scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine() + System.lineSeparator());
        }
        return fileContents.toString();
    }
}


Guava的方法类似于Willi Aus Rohr提到的Commons IOutils方法:

1
2
3
4
5
6
import com.google.common.base.Charsets;
import com.google.common.io.Files;

// ...

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

由Oscar Reyes编辑

这是引用库上的(简化)基础代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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包中的类文件、charstreams、bytesource和charsource。


1
import java.nio.file.Files;

……

1
2
3
4
5
6
7
8
9
10
11
12
 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"";
    }


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

这里有一个不这样做的替代方案,它比NIO代码更容易理解(imo)(尽管它仍然使用java.nio.charset.Charset):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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();
    }        
}


如果需要字符串处理(并行处理),Java 8具有大流API。

1
2
3
4
5
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示例EDCOX1(3)中,可以从Oracle Java SE 8下载页面下载更多的示例。

另一个单内衬示例

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


已收集从磁盘或网络以字符串形式读取文件的所有可能方法。

  • guava:google使用类ResourcesFiles

    1
    2
    3
    4
    5
    6
    7
    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 );
    }
  • 使用ioutils、fileutils类的apache-commons IO

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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("
    ") );*/

    }
  • 带regex \A的scanner类。与输入的开头匹配。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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(EDOCX1,4)

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

示例与主方法访问上述方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 :
"
+ 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 :
"
+ urlStream );
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}

参见

  • 将输入流转换为字符串的方法

如果是文本文件,为什么不使用ApacheCommons IO?

它有以下方法

1
public static String readFileToString(File file) throws IOException

如果要将行作为列表,请使用

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

自JDK 11以来:

1
2
3
4
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


以二进制形式读取文件并在末尾转换

1
2
3
4
5
6
7
8
9
10
11
12
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 7,这是我阅读UTF-8文件的首选选项:

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

自Java 7以来,JDK具有新的EDCOX1×5的API,它提供了许多快捷方式,因此对于简单的文件操作并不总是需要第三方库。


Java试图在它所做的一切中都是极其通用和灵活的。因此,在脚本语言中相对简单的一些东西(您的代码将在Python中替换为"open(file).read()")要复杂得多。除了使用一个外部的图书馆(如WilliAusRohr所提到的),似乎没有任何更短的方法来完成这项工作。你的选择:

  • 使用外部库。
  • 将此代码复制到所有项目中。
  • 创建您自己的迷你库,其中包含您经常使用的函数。

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


使用JDK 8或更高版本:

未使用外部库

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

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


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

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


如果您没有访问Files类的权限,则可以使用本机解决方案。

1
2
3
4
5
6
7
8
9
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);
}


一个灵活的解决方案,结合使用来自Apache Commons IO的ioutils和StringWriter:

1
2
3
4
5
6
7
8
Reader input = new FileReader();
StringWriter output = new StringWriter();
try {
  IOUtils.copy(input, output);
} finally {
  input.close();
}
String fileContents = output.toString();

它与任何读卡器或输入流(不只是文件)一起工作,例如从URL读取时。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static String slurp (final File file)
throws IOException {
    StringBuilder result = new StringBuilder();

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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这样的多字节字符编码。


用户java.nio.Files读取文件的所有行。

1
2
3
4
5
public String readFile() throws IOException {
        File fileToRead = new File("file path");
        List<String> fileLines = Files.readAllLines(fileToRead.toPath());
        return StringUtils.join(fileLines, StringUtils.EMPTY);
}

这一个使用了方法RandomAccessFile.readFully,它似乎可以从JDK1.0中获得!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
        }
    }
}

你可以试试扫描器和文件类,几行解决方案

1
2
3
4
5
6
7
8
9
 try
{
  String content = new Scanner(new File("file.txt")).useDelimiter("\\Z").next();
  System.out.println(content);
}
catch(FileNotFoundException e)
{
  System.out.println("not found!");
}


根据@erickson的回答,您可以使用:

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

在一行(Java 8)中,假设您有一个读取器:

1
2
String sMessage = String.join("
"
, reader.lines().collect(Collectors.toList()));

另外,如果您的文件正好在JAR中,您也可以使用它:

1
2
3
4
5
6
public String fromFileInJar(String path) {
    try ( Scanner scanner
            = new Scanner(getClass().getResourceAsStream(path))) {
        return scanner.useDelimiter("\\A").next();
    }
}

例如,如果jar是

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

然后您想这样调用它:

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

使用此库,它是一行:

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


在扫描器完成ctrl+f'ing之后,我认为扫描器解决方案也应该列出。最容易阅读的方式是这样:

1
2
3
4
5
6
7
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或更新器(并且您确实应该)考虑使用尝试资源来使代码更易于阅读。不要再到处乱扔东西了。但我认为,这主要是一种风格选择。

我发布这篇文章主要是为了完成主义,因为如果您需要做很多工作,那么java.nio.file.files中应该有更好的工作。

我的建议是使用文件readallbytes(path)来获取所有字节,并将其馈送到新的字符串(byte[]charset)中以获取您可以信任的字符串。字符集在你的一生中对你很重要,所以现在要小心这些东西。

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


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

这里的最佳答案之一(https://stackoverflow.com/a/326448/1521167):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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();
    }
}


使用代码:

1
2
3
4
5
6
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包含字符串形式的输出。


在Java 8中,有一个新类

java.util.stream.Stream

流表示元素序列,并支持对这些元素执行计算的不同类型的操作。

了解更多信息:

Oracle文档

这里有一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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();


}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Collectors;
/**
 * A simple example program that reads a text file into a String using Files.lines and stream.
 */

public class ReadTextFileExample {
    public static void main(String[] args) throws IOException {
        String contents = Files.lines(Paths.get("c:\\temp\\testfile.txt")).collect(Collectors.joining("
"
));
        System.out.println(contents);
    }
}