如何从文件的内容创建Java字符串?

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

有没有更好/不同的方式来读取文件到一个字符串在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();}}
1624973 次浏览

如果您愿意使用外部库,请查看Apache Commons IO(200KB JAR)。它包含一个#0方法,允许您使用一行代码将整个File读入String

示例:

import java.io.*;import java.nio.charset.*;import org.apache.commons.io.*;
public String readFile() throws IOException {File file = new File("data.txt");return FileUtils.readFileToString(file, StandardCharsets.UTF_8);}

Java试图在所有方面都非常通用和灵活。因此,在脚本语言中相对简单的东西(你的代码将被Python中的“open(file).read()”替换)要复杂得多。除了使用外部库(如上面提到的威利·奥斯·罗尔),似乎没有任何更短的方法来做到这一点。你的选择:

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

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

从文件中读取所有文本

Java11添加了readString()回调函数方法来读取小文件作为String,保留行终止符:

String content = Files.readString(path, encoding);

对于Java7和11之间的版本,这里有一个紧凑,健壮的习惯用法,包装在实用方法中:

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

从文件中读取文本行

Java7添加了一个将文件读取为文本行的方便方法,,表示为List<String>。这种方法是“有损的”,因为行分隔符从每行的末尾剥离。

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

Java8添加了#0方法来生成Stream<String>。同样,这个方法是有损耗的,因为行分隔符被剥离了。如果在读取文件时遇到IOException,它会被包装在#3中,因为Stream不接受抛出检查异常的lambda。

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

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

如果您使用的是文件以外的源,则可以改用BufferedReader中的#0方法。

内存利用率

如果您的文件相对于可用内存来说足够小,一次读取整个文件可能会很好。但是,如果您的文件太大,一次读取一行,处理它,然后在移动到下一行之前丢弃它可能是一个更好的方法。以这种方式进行流处理可以消除文件总大小作为内存需求的一个因素。

字符编码

原始帖子中示例中缺少的一件事是字符编码。这种编码通常无法从文件本身确定,并且需要元数据(例如HTTP标头)来传达这一重要信息。

#0类为所有Java运行时所需的编码定义了一些常量:

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

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

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

在一些特殊情况下,平台默认值是您想要的,但这种情况很少见。您应该能够证明您的选择是合理的,因为平台默认值不可移植。读取标准输入或写入标准输出时可能是正确的一个例子。


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

如果您正在寻找不涉及第三方库(例如共享资源I/O)的替代方案,您可以使用扫描仪类:

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

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

这是一个不这样做的替代方案,并且(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 streamFileInputStream 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 themstream.close();}}

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

for(String line = reader.readLine(); line != null; line = reader.readLine()) {stringBuilder.append(line);stringBuilder.append(ls);}
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();}

番石榴有一个类似于Willi ausRohr提到的Commons IOUtils的方法:

import com.google.common.base.Charsets;import com.google.common.io.Files;
// ...
String text = Files.toString(new File(path), Charsets.UTF_8);

编辑:PiggyPiglet
Files#toString已弃用,将于2019年10月删除Files.asCharSource(new File(path), StandardCharsets.UTF_8).read();

奥斯卡·雷耶斯编辑

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

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字节源CharSource

将文件读取为二进制并在最后转换

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

基于#0的非常精益的解决方案:

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

或者,使用资源试用块,它将为您调用scanner.close()

try (Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" )) {String text = scanner.useDelimiter("\\A").next();}

请记住,Scanner构造函数可以抛出IOException。不要忘记导入java.iojava.util

来源:Pat Niemeyer的博客

如果它是一个文本文件,为什么不使用apache Commons-io

它有以下方法

public static String readFileToString(File file) throws IOException

如果您想将行作为列表使用

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

这个使用方法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}}}

使用Apache公共资源-io中的票据结合StringWriter的灵活解决方案:

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

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

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

使用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等多字节字符编码。

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

最佳答案(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();}}

如果你需要一个字符串处理(并行处理)Java8有很棒的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中提供了更多示例,可以从OracleJavaSE 8下载页面下载

另一个班轮示例

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

如果您无法访问Files类,则可以使用本机解决方案。

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

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

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

如果您使用Java7或更高版本(您确实应该)考虑使用try-with-资源来使代码更易于阅读。不再有点附近的东西乱扔东西。但这主要是一种风格选择。

我发布这个主要是为了完成主义,因为如果你需要做很多,应该有java.nio.file.档案中的东西应该做得更好。

我的建议是使用文件#读取所有字节(路径)抓取所有字节,并将其提供给新的字符串(字节[]字符集)以从中获取您可以信任的字符串。字符集在您的一生中对您很重要,所以现在要小心这些东西。

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

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

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

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


由于人们仍然支持这个答案,这里有一个更好的解决方案,在Java11中引入:

String content = Files.readString(path);
import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Paths;

Java7

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

Java11

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

在Java 8中,有一个新的类

java.util.stream.流

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

阅读更多关于它:

Oracle文档

这里有一个例子:

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

}
}

使用这个图书馆,它是一行:

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

使用代码:

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

您可以尝试扫描仪和文件类,几行解决方案

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

此外,如果您的文件碰巧在jar中,您也可以使用以下命令:

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

路径应该以/开头,例如,如果您的jar是

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

然后你想像这样调用它:

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

在一行(Java8)中,假设你有一个Reader:

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

根据@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()]));}

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

  • 番石榴:谷歌使用类#0#1

    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-COMMONS IO使用类IOUtils、FileUtils

    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") );*/}

  • Java8缓冲区读取器使用流式接口

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

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

@陈志立

使用JDK 8或以上:

没有使用外部库

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

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

从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

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

public String readFile() throws IOException {File fileToRead = new File("file path");List<String> fileLines = Files.readAllLines(fileToRead.toPath());return StringUtils.join(fileLines, StringUtils.EMPTY);}
Scanner sc = new Scanner(new File("yourFile.txt"));sc.useDelimiter("\\Z");
String s = sc.next();

没有依赖的纯kotlin代码

适用于所有Android版本

val fileAsString = file.bufferedReader().use { it.readText() }