一段时间以来,我一直在使用下面的成语。它似乎是最广泛的,至少在我访问过的网站上。
有没有更好/不同的方法将文件读入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();
}
}
byte[] Files.readAllBytes(file);
:您不需要关闭它吗?
从文件中读取所有文本
Java 11 添加了 readString() 方法来读取小文件作为 String
,保留行终止符:
String content = Files.readString(path, StandardCharsets.US_ASCII);
对于 Java 7 和 11 之间的版本,这是一个紧凑、健壮的习惯用法,包含在一个实用方法中:
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>
的 convenience method to read a file as lines of text,。这种方法是“有损的”,因为行分隔符从每行的末尾被剥离。
List<String> lines = Files.readAllLines(Paths.get(path), encoding);
Java 8 添加了 Files.lines()
方法来生成 Stream<String>
。同样,这种方法是有损的,因为行分隔符被剥离。如果在读取文件时遇到 IOException
,则将其包装在 UncheckedIOException
中,因为 Stream
不接受引发检查异常的 lambda。
try (Stream<String> lines = Files.lines(path, encoding)) {
lines.forEach(System.out::println);
}
这个 Stream
确实需要一个 close()
调用;这在 API 上的记录很差,我怀疑很多人甚至没有注意到 Stream
有一个 close()
方法。一定要使用如图所示的 ARM 块。
如果您使用的是文件以外的源,则可以改用 BufferedReader
中的 lines()
方法。
内存利用率
第一种保留换行符的方法可能会暂时需要几倍于文件大小的内存,因为在短时间内原始文件内容(字节数组)和解码的字符(每个字符都是 16 位,即使已编码)作为文件中的 8 位)一次驻留在内存中。应用到您知道相对于可用内存较小的文件是最安全的。
第二种方法,读取行,通常更节省内存,因为用于解码的输入字节缓冲区不需要包含整个文件。但是,它仍然不适合相对于可用内存非常大的文件。
为了读取大文件,您需要对程序进行不同的设计,即从流中读取一大块文本,对其进行处理,然后继续下一个,重用相同的固定大小的内存块。在这里,“大”取决于计算机规格。如今,这个阈值可能是数 GB 的 RAM。如果您输入的“记录”恰好是单独的行,则使用 Stream<String>
的第三种方法是执行此操作的一种方法。 (使用 BufferedReader
的 readLine()
方法与此方法在程序上等价。)
字符编码
原始帖子中的示例中缺少的一件事是字符编码。在某些特殊情况下,平台默认值是您想要的,但它们很少见,您应该能够证明您的选择是合理的。
StandardCharsets
类为所有 Java 运行时所需的编码定义了一些常量:
String content = readFile("test.txt", StandardCharsets.UTF_8);
平台默认值可从 the Charset
class 本身获得:
String content = readFile("test.txt", Charset.defaultCharset());
注意:这个答案在很大程度上取代了我的 Java 6 版本。 Java 7 的实用程序安全地简化了代码,并且使用映射字节缓冲区的旧答案阻止了读取的文件被删除,直到映射缓冲区被垃圾收集。您可以通过此答案的“已编辑”链接查看旧版本。
如果您愿意使用外部库,请查看 Apache Commons IO (200KB JAR)。它包含一个 org.apache.commons.io.FileUtils.readFileToString()
方法,允许您使用一行代码将整个 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);
}
基于 Scanner
的非常精简的解决方案:
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
或者,使用 try-with-resources 块,它将为您调用 scanner.close()
:
try (Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" )) {
String text = scanner.useDelimiter("\\A").next();
}
请记住,Scanner
构造函数可以抛出 IOException
。并且不要忘记导入 java.io
和 java.util
。
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
爪哇 7
String content = new String(Files.readAllBytes(Paths.get("readMe.txt")), StandardCharsets.UTF_8);
爪哇 11
String content = Files.readString(Paths.get("readMe.txt"));
如果您正在寻找不涉及第三方库的替代方案(例如 Commons I/O),您可以使用 Scanner 类:
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 中的方法:
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 包中的类 Files、CharStreams、ByteSource 和 CharSource。
Closer
的使用。答案中的代码不是实际的当前 Guava 源。
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 "";
}
new String(Files.readAllBytes(Paths.get(filename)));
:-)
如果你需要一个字符串处理(并行处理),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 download page 下载
另一个班轮示例
String out = String.join("\n", Files.readAllLines(Paths.get("file.txt")));
Files.lines(Paths.get("file.txt"))
返回的流未关闭,是资源泄漏。您应该包装在 try-with-resources 块中。
该代码将规范换行符,这可能是也可能不是您真正想要做的。
这是一个不这样做的替代方案,并且(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();
}
}
收集了从磁盘或网络读取文件作为字符串的所有可能方法。
Guava:谷歌使用类资源,文件静态字符集 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;公共静态字符串 commons_IOUtils( URL url ) 抛出 IOException { java.io.InputStream in = url.openStream();尝试 { return IOUtils.toString( in, encoding ); } 最后 { IOUtils.closeQuietly(in); } } public static String commons_FileUtils( File file ) throws IOException { return FileUtils.readFileToString( file, encoding ); /*List
Java 8 BufferReader 使用 Stream API public static String streamURL_Buffer( URL url ) throws IOException { java.io.InputStream source = url.openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(source)); //List
带有正则表达式 \A 的扫描仪类。与输入的开头匹配。静态字符串 charsetName = java.nio.charset.StandardCharsets.UTF_8.toString(); public static String streamURL_Scanner( URL url ) 抛出 IOException { java.io.InputStream source = url.openStream(); Scanner 扫描仪 = new Scanner(source, charsetName).useDelimiter("\\A");返回scanner.hasNext() ?扫描仪.next() : ""; } public static String streamFile_Scanner( File file ) throws IOException { Scanner scanner = new Scanner(file, charsetName).useDelimiter("\\A");返回scanner.hasNext() ?扫描仪.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() )) ;返回新字符串(readAllBytes); }
BufferedReader 使用 InputStreamReader。公共静态字符串 getDiskFile_Lines( 文件文件 ) 抛出 IOException { StringBuffer text = new StringBuffer(); FileInputStream fileStream = new FileInputStream( 文件 ); BufferedReader br = new BufferedReader(new InputStreamReader(fileStream)); for ( String line; (line = br.readLine()) != null; ) text.append( line + System.lineSeparator() );返回文本.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();
}
}
@看
将 InputStream 转换为字符串的方法
如果是文本文件,为什么不使用 apache commons-io?
它有以下方法
public static String readFileToString(File file) throws IOException
如果您希望将这些行作为列表使用
public static List<String> readLines(File file) throws IOException
从 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
将文件读取为二进制文件并在最后进行转换
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 文件的首选选项:
String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");
从 Java 7 开始,JDK 具有新的 java.nio.file
API,它提供了许多快捷方式,因此简单的文件操作并不总是需要 3rd 方库。
由于人们仍在支持这个答案,因此这是 Java 11 中引入的更好的解决方案:
String content = Files.readString(path);
Java 试图在它所做的所有事情上都非常通用和灵活。结果,在脚本语言中相对简单的东西(您的代码将在 python 中替换为“open(file).read()
”)要复杂得多。除了使用外部库(如提到的Willi aus Rohr)之外,似乎没有任何更短的方法。您的选择:
使用外部库。
将此代码复制到您的所有项目中。
创建您自己的迷你库,其中包含您经常使用的功能。
您最好的选择可能是第二个,因为它具有最少的依赖性。
byte[] bytes = Files.readAllBytes(someFile.toPath());
使用 JDK 8 或更高版本:
没有使用外部库
您可以从文件内容创建一个新的 String 对象(使用 java.nio.file
包中的类):
public String readStringFromFile(String filePath) throws IOException {
String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
return fileContent;
}
同一主题有一个变体,它使用 for 循环而不是 while 循环来限制 line 变量的范围。是否“更好”是个人品味的问题。
for(String line = reader.readLine(); line != null; line = reader.readLine()) {
stringBuilder.append(line);
stringBuilder.append(ls);
}
line
变量的范围。编辑声明了两次,这将是一个编译错误。
如果您无权访问 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);
}
将 Apache commons-io 中的 IOUtils 与 StringWriter 结合使用的灵活解决方案:
Reader input = new FileReader();
StringWriter output = new StringWriter();
try {
IOUtils.copy(input, output);
} finally {
input.close();
}
String fileContents = output.toString();
它适用于任何阅读器或输入流(不仅仅是文件),例如从 URL 读取时。
请注意,使用 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 这样的多字节字符编码。
available()
方法的 documentation,如果该方法返回 0,则无法保证到达文件末尾。在这种情况下,您可能会得到一个不完整的文件。更糟糕的是,实际读取的字节数可能小于 available()
返回的值,在这种情况下,您会得到损坏的输出。
这个使用方法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
}
}
}
可以试试 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!");
}
根据@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()]));
}
用户 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);
}
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();
}
cannot find symbol
。
我还不能评论其他条目,所以我就把它留在这里。
这里的最佳答案之一(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();
}
}
仍然有一个缺陷。它总是将换行符放在字符串的末尾,这可能会导致一些奇怪的错误。我的建议是将其更改为:
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();
}
}
在 Scanner 之后 Ctrl+F'ing 后,我认为 Scanner 解决方案也应该列出。在最容易阅读的时尚中,它是这样的:
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中应该有一些东西可以更好地完成这项工作。
我的建议是使用 Files#readAllBytes(Path) 获取所有字节,并将其提供给新的 String(byte[] Charset) 以从中获取您可以信任的字符串。字符集在你的一生中对你来说很重要,所以现在要小心这些东西。
其他人提供了代码和东西,我不想窃取他们的荣耀。 ;)
此外,如果您的文件恰好在 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");
在一行(Java 8)中,假设您有一个阅读器:
String sMessage = String.join("\n", reader.lines().collect(Collectors.toList()));
不定期副业成功案例分享