文章目录
一、File流概念
JAVA中针对文件的读写操作设置了一系列的流,其中主要有 FileInputStream
、FileOutputStream
、FileReader
、FileWriter
四种最为常用的流。
1.1 FileWriter 写文本文件:
import java.io.*;
/*
* 写入到C盘hui目录下的一个文本文件,并往里写入若干行文字。
*如果该次磁盘下有重复的内容,则删除重复的内容后再写入一个新的内容。
*/
public class TestWriteText{
public static void main(String[] args){
// 创建一个File的实例对象
File file=new File("C:\\Users\\huiqiang\\Desktop\\hui");
//判断file是否存在,不存在就创建出一个文件目录
if(!file.exists()){
file.mkdirs();
System.out.println("该目录不存在,已创建");
}
File file1=new File(file,"lian.txt");
// 判断file1是否存在,不存在就创建出一个文件
if(!file1.exists()){
file.mkdirs();
System.out.println("该文件不存在,已创建");
}
// 在main方法中声明抛出IO异常。
// 定义写入路径的字符串。
String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
try
{
// 使用面向字符流的FileWriter类。
// 直接调用构造方法,传递写入路径。FileWriter类的构造方法有五个。
FileWriter writer1 = new FileWriter(fileName);
// OutputStreamWriter writer1 = new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8"); // 转换成utf-8格式
// 相当于在FileWriter类上再套上一层管道。如果要写入的内容更多,就应该用高效的缓冲流类:BufferedWriter.与FileWriter不同是多个了newLine()方法用于换行。
BufferedWriter writer = new BufferedWriter(writer1);
//FileWriter类的Write()方法向文件中写入字符。
writer.write("a1.sources = r1\n");
writer.write("a1.sinks = k1\n");
writer.write("a1.channels = c1\n");
writer.write("\n");
writer.write("a1.sources.r1.type = exec\n");
writer.write("a1.sources.r1.command = tail -F /home/hadoop/messages\n");
writer.write("a1.sources.r1.port = 44444\n");
writer.write("a1.sources.r1.host = 192.168.8.71\n");
writer.write("a1.sources.r1.channels = c1\n");
writer.newLine();
writer.write("a1.sinks.k1.type = logger\n");
writer.write("a1.sinks.k1.type = hbase\n");
writer.write("a1.sinks.k1.table = messages\n");
writer.write("a1.sinks.k1.columnFamily = cf\n");
writer.write("a1.sinks.k1.serializer = com.tcloud.flume.AsyncHbaseLogEventSerializer\n");
writer.write("a1.sinks.k1.channel = memoryChannel\n");
writer.newLine();
writer.write("a1.channels.c1.type = memory\n");
writer.write("a1.channels.c1.capacity = 1000\n");
writer.write("a1.channels.c1.transactionCapacity = 100\n");
writer.write("\n");
writer.write("a1.sources.r1.channels = c1\n");
writer.write("a1.sinks.k1.channel = c1");
// 清空流里的内容并关闭它,如果不调用该方法还没有完成所有数据的写操作,程序就结束了。
writer.close();
}
catch(IOException iox)
{
System.out.println("Problemwriting" + fileName);
}
}
}
1.2 FileReader 读文本文件:
import java.io.*;
/*
* 从C盘的lian.txt中读出文件中的数据到命令行中。
*
*/
public class TestBufferedReader{
public static void main(String[] args){
//在main方法中声明抛出IO异常。
//定义写入路径的字符串。
String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
String line;
try
{
// 利用BufferedReader套在FileReader,处理流套在节点流上。
BufferedReader in = new BufferedReader(new FileReader(fileName));
//首先读取一行的内容。
line = in.readLine();
//判断读写的内容是否为空。
while(line!=null)
{
//输出一行。
System.out.println(line);
//继续读出下一行的内容。
line = in.readLine();
}
//关闭这个“管道”。
in.close();
}
catch(IOException iox)
{
System.out.println("Problemreading" + fileName);
}
}
}
注意:在执行过程中遇到了乱码的问题。
1.执行 javac hehe.java
时报错误: 编码 GBK 的不可映射字符
解决:javac -encoding UTF-8 hehe.java
2.在控制台打印出来的内容有乱码
解决:将上面的相关代码改为
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
BufferedReader in = new BufferedReader(isr);
优化:
import java.util.zip.ZipInputStream;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.charset.Charset;
import java.io.*;
public class Test {
/**
* 多种文件编码方式
*/
private static final String[] charsetNames = new String[]{"GB2312", "UTF-8", "Unicode", "GBK"};
public List<String> getZipDataMap(String filePath) throws IOException {
InputStream inputStream = Files.newInputStream(Paths.get(filePath));
ZipInputStream zis = new ZipInputStream(inputStream);
InputStreamReader streamReader = null;
BufferedReader reader = null;
List<String> lineList = new ArrayList<>();
for (String charsetName : charsetNames) {
streamReader = new InputStreamReader(zis, Charset.forName(charsetName));
reader = new BufferedReader(streamReader);
String line;
while ((line = reader.readLine()) != null) {
lineList.add(line);
}
}
return lineList;
}
}
1.3 FileInputStream:
FileInputStream 流被称为文件字节输入流,意思指对文件数据以字节的形式进行读取操作如读取文本图片视频等。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author: huiq
* @createTime: 2021/9/1 8:57
* @description:
*/
public class Demo {
public static void main(String[] args) {
// 得到一个文件对象
File f = new File("C:\\Users\\9\\Desktop\\临时文件夹\\kafka.yml");
FileInputStream fis = null;
try {
// 因为File没有读写能力,所以需要有个InpuntStream
fis = new FileInputStream(f);
// 定义一个字节数组,相当于缓存
byte []bytes= new byte[1024];
int n = 0;
// 循环读取
while ((n=fis.read(bytes)) != -1) {
// 把字节转成String
String s = new String(bytes, 0, n);
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 这个流最后必须关闭,而且应该在finally方法中关闭
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
InputStream 是字节输入流的所有类的超类,一般我们使用它的子类,如 FileInputStream 等。
OutputStream 是字节输出流的所有类的超类,一般我们使用它的子类,如 FileOutputStream 等。
InputStreamReader 是字节流通向字符流的桥梁,它将字节流转换为字符流。
OutputStreamWriter 是字符流通向字节流的桥梁,它将字符流转换为字节流。
BufferedReader 由 Reader 类扩展而来,提供通用的缓冲方式文本读取,readLine 读取一个文本行,从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
BufferedWriter 由 Writer 类扩展而来,提供通用的缓冲方式文本写入,newLine 使用平台自己的行分隔符,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
InputStream 能从來源处读取一個一個 byte,所以它是最低级的; InputStreamReader 封裝了 InputStream 在里头,它以较高级的方式,一次读取一个一个字符,下例是假设有一个编码为 utf8 的文档, 其內容只有一个中文字 “陳”。
import java.io.*;
public class Main {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
FileInputStream fis=new FileInputStream("d://desktop//test.txt");
try {
InputStreamReader isr=new InputStreamReader(fis,"utf8");
int i;
while((i=isr.read()) != -1){
System.out.println((char)i); //輸出 陳
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
BufferedReader 则是比 InputStreamReader 更高级,它封裝了 StreamReader 类,一次读取取一行的字符。
import java.io.*;
public class Main {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
FileInputStream fis=new FileInputStream("d://desktop//test.txt");
try {
InputStreamReader isr=new InputStreamReader(fis,"utf8");
BufferedReader br=new BufferedReader(isr);
String line;
while((line=br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
参考:
FileInputStream、InputStreamReader和BufferedReader几种读取文件的区别
几种Java读写数据的流性能对比
二、二进制文件
包含在ASCII及扩展ASCII字符中编写的数据或程序指令的文件。
计算机文件基本上分为二种:二进制文件和ASCII(也称纯文本文件),图形文件及文字处理程序等计算机程序都属于二进制文件。这些文件含有特殊的格式及计算机代码。ASCII则是可以用任何文字处理程序阅读的简单文本文件。
2.1 写二进制文件:
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
/*
* 用字节流写二进制文件。将三个int类型255 0 -1写入数据文件lian.txt中。
*/
public class TestDataOutputStream{
/**
* java.io包中的OutputStream及其子类专门用于写二进制数据。
* FileOutputStream是其子类,可用于将二进制数据写入文件。
* DataOutputStream是OutputStream的另一个子类,它可以
* 连接到一个FileOutputStream上,便于写各种基本数据类型的数据。
*/
public static void writeMethod1()
{
String fileName="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
int value0=255;
int value1=0;
int value2=-1;
try
{
// 将DataOutputStream与FileOutputStream连接可输出不同类型的数据
// FileOutputStream类的构造函数负责打开文件kuka.dat,如果文件不存在,
// 则创建一个新的文件,如果文件已存在则用新创建的文件代替。然后FileOutputStream
// 类的对象与一个DataOutputStream对象连接,DataOutputStream类具有写
// 各种数据类型的方法。
DataOutputStream out=new DataOutputStream(new FileOutputStream(fileName));
out.writeInt(value0);
out.writeInt(value1);
out.writeInt(value2);
out.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
//对于大量数据的写入,使用缓冲流BufferedOutputStream类可以提高效率
public static void writeMethod2()
{
String fileName="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
try
{
DataOutputStream out=new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(fileName)));
out.writeInt(10);
System.out.println(out.size()+" bytes have been written.");
out.writeDouble(31.2);
System.out.println(out.size()+" bytes have been written.");
out.writeBytes("JAVA");
System.out.println(out.size()+" bytes have been written.");
out.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args) {
writeMethod1();
// writeMethod2();
}
}
这里列出在 UltraEdit(UE)里看到的东西:
2.2 读二进制文件:
import java.io.*;
public class TestDataInputStream{
/**
* 对二进制文件比较常见的类有FileInputStream,DataInputStream
* BufferedInputStream等。类似于DataOutputStream,DataInputStream
* 也提供了很多方法用于读入布尔型、字节、字符、整形、长整形、短整形、
* 单精度、双精度等数据。
*/
public static void readMethod1()
{
//定义写入路径的字符串。
String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
int sum = 0;
try {
DataInputStream instr= new DataInputStream(
new BufferedInputStream(new FileInputStream(fileName)));
sum += instr.readInt();
sum += instr.readInt();
sum += instr.readInt();
System.out.println( "The sum is: " + sum );
instr.close();
}
catch ( IOException iox ) {
System.out.println("Problem reading " + fileName);
}
}
public static void readMethod2()
{
try
{
FileInputStream stream=new FileInputStream("C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt");
int c;
while((c=stream.read())!=-1)
{
System.out.println(c);
}
} catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args){
readMethod1();
// readMethod2();
}
}
运行结果:
The sum is: 254
readMethod1 方法中当我们不知道是三个数据的时候,多读的话就会抛出 EOFException 异常,所以我们将读操作放在 try中。我们可以使用嵌套的try catch
块来对异常进行进一步的处理:
public static void readMethod1()
{
String fileName ="C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt";
int sum = 0;
try {
DataInputStream instr= new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));
try{
while(true) {
sum += instr.readInt();
}
} catch(EOFException eof){
System.out.println( "The sum is: " + sum );
instr.close();
}
}
catch ( IOException iox ) {
System.out.println("Problem reading " + fileName);
}
}
注意:路径的写法:1. 如果你是在 Windows 的 myeclipse 中运行程序的话要这写 C:\\Users\\huiqiang\\Desktop\\hui\\lian.txt
2. 如果是在 Linux 中运行程序的话要这样写 /home/hadoop/hui/lian.txt
参考:
java IO 流(二)写文件和读文件
IO流中的文件创建并且写入读取
Java.过滤流(包装流)
Java读写二进制文件操作
三、逐行读取文本文件的几种方式以及效率对比
参考:
java 读取文件 效率_Java 逐行读取文本文件的几种方式以及效率对比
Apache Commons-io工具类的使用
1000000 行文本读取结果比对:
BufferedReader 耗时: 49ms
Scanner 耗时: 653ms
Apache Commons IO 耗时: 44ms
InputStreamReader 耗时: 191ms
FileInputStream 耗时: 3171ms
BufferedInputStream 耗时: 70ms
FileUtils 耗时: 46ms
Files 耗时: 99ms
24488656 行文本读取结果比对:
BufferedReader 耗时: 989ms
Scanner 耗时: 11899ms
Apache Commons IO 耗时: 568ms
InputStreamReader 耗时: 3377ms
FileInputStream 耗时: 78903ms
BufferedInputStream 耗时: 1480ms
FileUtils 耗时: 16569ms
Files 耗时: 25162ms
可见,当文件较小时:
ApacheCommonsIO 流表现最佳;
FileUtils, BufferedReader 居其二;
BufferedInputStream, Files 随其后;
InputStreamReader, Scanner, FileInputStream 略慢.
当文件较大时, Apache Commons IO 流, BufferedReader 依然出色, Files, FileUtils 速度开始变慢.
简要分析,使用到的工具类包括:
java.io.BufferedReader
java.util.Scanner
org.apache.commons.io.FileUtils
java.io.InputStreamReader
java.io.FileInputStream
java.io.BufferedInputStream
com.google.common.io.Files
其中:Apache Commons IO
流和 BufferedReader
使用到了缓冲区,所以在不消耗大量内存的情况下提高了处理速度;FileUtils 和 Files 是先把文件内容全部读入内存,然后在进行操作,是典型的空间换时间案例,这种方法可能会大量消耗内存,建议酌情使用;其他几个工具类本来就不擅长逐行读取,效率底下也是情理之中。
建议:在逐行读取文本内容的需求下,建议使用 Apache Commons IO
流,或者 BufferedReader
,既不会过多地占用内存,也保证了优异的处理速度。
疑惑:我看 Apache Commons IO
底层源码就是使用了 BufferedReader
,那为什么 Apache Commons IO
要比 BufferedReader
快呢,我目前实测也是 BufferedReader
比 Apache Commons IO
稍快一些,不知道是我的测试方法是否有问题。
// 源码 commons-io 2.11.0
public class IOUtils {
// IOUtils.readLines 方法
public static List<String> readLines(InputStream input, Charset charset) throws IOException {
InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(charset));
return readLines((Reader)reader);
}
public static List<String> readLines(Reader reader) throws IOException {
BufferedReader bufReader = toBufferedReader(reader);
ArrayList list = new ArrayList();
String line;
while((line = bufReader.readLine()) != null) {
list.add(line);
}
return list;
}
// IOUtils.lineIterator 方法
public static LineIterator lineIterator(InputStream input, Charset charset) {
return new LineIterator(new InputStreamReader(input, Charsets.toCharset(charset)));
}
public LineIterator(Reader reader) throws IllegalArgumentException {
if (reader == null) {
throw new IllegalArgumentException("Reader must not be null");
} else {
if (reader instanceof BufferedReader) {
this.bufferedReader = (BufferedReader)reader;
} else {
this.bufferedReader = new BufferedReader(reader);
}
}
}
}
3.1 Apache Commons Io
参考:Apache Commons IO: 简化文件和IO操作
commons-io
是 Java 文件 IO『第一库』是公认的,它的功能和代码质量都是极佳的,它好到没有人想到再写一个竞品与之竞争,这么干完全是费力不讨好的做法。
在做 Java 编程的时候,经常会遇到各种文件操作和输入输出(IO)的问题。不论是读取一个配置文件,还是把数据写入日志,这些看似简单的任务有时候会让人头疼。传统的 Java IO
操作,虽然功能强大,但往往代码冗长,而且容易出错。这时候,Apache Commons IO
库就像一股清泉,为咱们简化这些操作提供了极大的便利。在 Java 世界里,Apache Commons IO
是一个非常受欢迎的库,它提供了一系列工具类,帮助咱们以更高效、更简洁的方式处理文件和 IO。
Apache Commons IO
库。这个库是 Apache Commons
项目的一部分,目的是为 Java 开发者提供一系列通用的 IO 操作工具。不知道咱们有没有注意到,Java 标准库中的 IO 操作有时候显得有点复杂和笨重。Apache Commons IO
就是为了解决这个问题而诞生的。
问题描述:在读取非常大的文件时,可能会遇到内存溢出的问题。
解决方案:使用流式处理方式,而不是一次性将整个文件加载到内存中。比如,可以使用 LineIterator 来逐行处理文件,这样可以有效地控制内存使用。
代码示例:高效处理大文件。在处理大型文件时,咱们需要特别注意资源管理和效率。下面是一个示例,展示如何高效地读取大文件。
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class LargeFileReadExample {
public static void main(String[] args) throws IOException {
// 方式一:FileUtils.lineIterator
File largeFile = new File("大型文件.txt");
try (LineIterator it = FileUtils.lineIterator(largeFile, "UTF-8")) {
List<String> lineList = new ArrayList<>();
while (it.hasNext()) {
lineList.add(it.nextLine());
}
LineIterator.closeQuietly(it);
} catch (IOException e) {
e.printStackTrace();
}
// 方式二:FileUtils.readLines
try {
List<String> lines = FileUtils.readLines(largeFile, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
// 方式三:IOUtils.lineIterator
InputStream inputStream = Files.newInputStream(Paths.get("大型文件.txt"));
LineIterator it = IOUtils.lineIterator(inputStream, "UTF-8");
List<String> lineList = new ArrayList<>();
while (it.hasNext()) {
lineList.add(it.nextLine());
}
LineIterator.closeQuietly(it);
// 方式四:IOUtils.readLines
List<String> lines = IOUtils.readLines(inputStream, "UTF-8");
}
}
在这个例子中,LineIterator 类被用于逐行读取文件,这样可以防止将整个文件内容一次性加载到内存中,从而提高了处理大文件的效率。