目录
1. File类
File类是一种对文件或路径的封装,提供了很多操作方法。
File类的构造器两种:可以选择将路径写死或者分开写,分开写更灵活
注:在eclipse中,通过相对路径创建File对象,绝对路径自动设置为当前项目的根目录
1.1 File类的常用方法
创建文件夹:mkdirs() 可以创建多层文件夹;mkdir()只能创建一层
创建文件:createNewFile()
删除:delete() 文件和文件夹都能删,删除的文件不走回收站,永久性删除
获取:
getName()获取路径中最后部分表示的文件或文件夹名
getPath()获取File对象的路径字符串,类似file.toString()
length()返回路径中的文件的字节数,返回long型
getAbsolutePath()获取路径表示文件的绝对路径(返回String)
getAbsoluteFile()获取路径表示文件的绝对路径(返回File)
getParent() 获取文件/文件夹的父路径
getParentFile() 获取文件/文件夹的父路径对应的File对象
判断:
exist()判断File构造方法中封装的路径是否存在
isDirectory()判断File构造方法中封装的路径是不是目录(文件夹)
isFile()判断File构造方法中封装的路径是不是文件
文件/文件夹遍历获取:
list()返回String[] 获取到File构造方法中封装的路径下的所有文件名和文件夹名(遍历目录)
listFile() 同上,返回File,可以获取绝对路径
1.2 文件过滤器FileFilter
可用于传递参数给File类的listFiles(FileFilter ff),用于路径下File的过滤
FileFilter接口没有实现类,需要自定义过滤器
文件过滤器例子:遍历一个路径下的全部.java文件并打印到控制台
首先自定义过滤器:
package ch22;
import java.io.File;
import java.io.FileFilter;
public class MyFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".java");
}
}
遍历文件夹,包括文件夹中子文件夹下的文件,使用过滤器过滤出.java文件
package ch22;
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File file = new File("d:\\test");
System.out.println("============.java文件============");
fileFilter(file);
}
public static void fileFilter(File file) {
if (!file.exists()) {
System.out.println("目录不存在!");
} else {
MyFileFilter filter = new MyFileFilter();
if (file.isDirectory()) {
// 如果是文件夹,打印该文件夹下的.java文件
File[] filteredFiles = file.listFiles(filter);
for (File result : filteredFiles) {
System.out.println(result.getAbsolutePath());
}
// 继续扫描文件夹下的全部文件,找到文件夹进行递归
File[] files = file.listFiles();
for (File aFile : files) {
if (aFile.isDirectory()) {
fileFilter(aFile);
}
}
}
}
}
}
1.3 递归遍历文件夹练习
递归遍历文件夹下全部文件和文件夹,并按一定的格式打印出来。
package ch22;
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
// 递归遍历全目录
File file = new File("d:\\test");
String[] fileNames = file.list();
for (String fileName : fileNames) {
System.out.print(fileName + " ");
}
System.out.println();
System.out.println("========================");
fileTraverse(file, 0);
}
public static void fileTraverse(File file, int depth) {
// 定义depth变量,用于记录当前文件夹是第几层
int currentDepth = depth;
if (file.exists()) {
for (int i = 0; i < currentDepth; i++) {
System.out.print("--");
}
System.out.println(file.getName());
File[] files = file.listFiles();
currentDepth ++;
for (File listedFile : files) {
if (listedFile.isFile()) {
for (int i = 0; i < currentDepth; i++) {
System.out.print("--");
}
System.out.println(listedFile.getName());
} else if (listedFile.isDirectory()) {
fileTraverse(listedFile, currentDepth);
}
}
}
}
}
a b demo.txt
========================
test
--a
----a6.txt
----aaaa
--b
----b5.txt
----wode
--demo.txt
2. 字节流
字节流的超类是InputStream和OutputStream,常用的实现类为FileInputStream和FileOutputStream。字节流就像一个管道,插入文件中,就可以源源不断地读写字节数据。
注:所有流对象,使用完之后必须在finally块中关闭资源
2.1 基本操作
构造方法:可以通过传递File类型或String类型文件全路径来构造字节流
FileInputStream常用方法:
int |
read()
Reads a byte of data from this input stream. |
int |
read(byte[] b)
Reads up to |
read()方法返回读取到的字节内容,如果没有读到,则返回-1
read(byte[] b)方法将读取到的内容存放在数组b中,返回读取到的字节个数,没有数据时返回-1,返回值可用于文件读取结束的判断.
注:这里字节数组byte[]起到缓冲作用,虚拟机将不再每读一个字节都去系统内存存储一次,而是将结果存储在数组中,待数组存满后一起处理,这样提高了效率,比单个字节读取要快很多。
FileOutputStream常用方法:
void |
write(byte[] b)
Writes |
void |
write(byte[] b, int off, int len)
Writes |
void |
write(int b)
Writes the specified byte to this file output stream. |
注:这里读写都是通过单个字节或者字节数组byte[],读取到字节后,去编码表查询,才能转换为真正的文件内容,写的时候也是将文件内容通过编码表转换为字节码,再进行写入。
2.2 字节流读写示例
1. 将字节数组写入文件
/**
* @Title: FileOutput
* @Description: 将byte[]字节数组中的内容输出到文件中
* @param: @param file
* @return: void
* @throws
*/
public static void FileOutput(File file) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
byte[] b = {'a','b','c', 106, 107};
fos.write(b);
} catch (FileNotFoundException e) {
e.printStackTrace();
// 没有继续执行的必要了,所以抛一个运行时异常终止程序
throw new RuntimeException("文件不存在!");
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("I/O出错!");
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2. 从文件中一个字节一个字节读取内容
/**
* @Title: FileInput
* @Description: 读取文件中的内容,打印到控制台
* @param: @param inFile
* @return: void
* @throws
*/
public static void FileInput(File file) {
if (file.exists() && file.isFile()) {
byte[] in = new byte[1024];
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
int len = 0;
while ((len = fis.read(in)) != -1) {
System.out.print(new String(in, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
// 找不到文件,没有往下执行程序的必要,直接抛运行时异常终止程序
throw new RuntimeException("找不到指定文件!");
} catch (IOException e) {
e.printStackTrace();
// io失败,没有往下执行的必要
throw new RuntimeException("I/O操作失败!");
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.3 使用字节流进行文件复制
/**
* @Title: copy
* @Description: 字节流读取单个字节复制文件
* @param: @param fromFile
* @param: @param toFile
* @return: void
* @throws
*/
public static void copy(File fromFile, File toFile) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(fromFile);
fos = new FileOutputStream(toFile);
int len = 0;
while ((len = fis.read()) != -1) {
// fromFile中读出来的数据立刻写入toFile
fos.write(len);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
/**
* @Title: copyByArray
* @Description: 字节流读取字节数组来复制文件
* @param: @param fromFile
* @param: @param toFile
* @return: void
* @throws
*/
public static void copyByArray (File fromFile, File toFile) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(fromFile);
fos = new FileOutputStream(toFile);
byte[] b = new byte[1024];
int len = 0;
while ((len = fis.read(b)) != -1) {
// 从0开始,写每次读到的字节个数len
fos.write(b, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("复制文件失败!");
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
// finally里嵌套finally,保证两个finally关流操作都执行到
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
注:关于字节数组开辟多大空间,过小则效率提升不明显,过大会占用太多系统内存资源,所以要按需求进行衡量,这里采用了一般的1024个字节。
2.4 文件续写和换行
new FileOutputStream(file):这样创建对象,写入数据,会覆盖原有的文件
FileOutputStream(File file, boolean append)
:构造方法中append值传true,这样写入数据,就会在原有文件的结尾续写
换行:在windows中,换行符为\r\n,在Linux系统中,换行符为\n
3. 字符流
字符流的超类是java.io.Reader和java.io.Writer,常用的子类为FileReader和FileWriter。字符流只用于操作文本文件!!!
3.1 基本方法
read():读取单个字符并返回
read(char[]):将数据读取到数组中,并返回读取的个数。
注:FileWriter写入时,必须进行flush(),内容才能从缓冲区真正写入文件。close()也自带flush()功能,如果写入数据很多,不能依靠close()的刷新功能,必须边写边刷。
3.2 字符流实现文本文件复制
/**
* @Title: copyByChar
* @Description: 通过字符流复制文本文件
* @param: @param fromFile
* @param: @param toFile
* @return: void
* @throws
*/
public static void copyByChar(File fromFile, File toFile) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader(fromFile);
fw = new FileWriter(toFile);
char[] cbuf = new char[1024];
// fw.write("你好");
int len = 0;
while ((len = fr.read(cbuf)) != -1) {
System.out.println(new String(cbuf, 0, len));
fw.write(cbuf, 0, len);
// 必须flush()一下
fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("复制文件失败!");
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
注:如果文本文档中包含中文字符,则可能会产生乱码问题。
涉及到字节和字符的转换,首先了解编码和解码的过程:
文字--->(数字) :编码。 “abc”.getBytes() 获取byte[]
(数字)--->文字 : 解码。 byte[] b={97,98,99} new String(b)获取到字符串
使用字符流读写时,虚拟机会自动进行解码和编码的工作,如果编码表不相同,则会产生乱码问题。一般文件编码默认是ANSI编码,简体中文下是GB2312。
解决办法:
1. 通过转换流FileInputStream转码。
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "GBK"); //或GB2312,GB18030
BufferedReader read = new BufferedReader(isr);
2. 如非要用FileReader的话,可以将要读取的文件改为通用的编码(如UTF-8).如txt的文件可以在另存为中设置编码。然后读取 显示写入都是正常的。
4. 转换流
4.1 概述
转换流包括InputStreamReader和OutputStreamWriter,这两个类是Reader和Writer类的子类,也是FileReader和FileWriter的父类。转换流是字符和字节之间的桥梁,通过包装一个字节流,对文件进行操作。
OutputStreamWriter:可使用指定的字符编码表,将要写入流中的字符编码成字节。它的作用的就是,将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去。
OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
其实在OutputStreamWriter流中维护自己的缓冲区,当我们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。
InputStreamReader:同理
查看api文档可以知道,InputStreamReader和OutputStreamWriter用法基本与FileReader和FileWriter相同,只有构造方法有区别:
Constructor and Description |
---|
InputStreamReader(InputStream in)
Creates an InputStreamReader that uses the default charset. |
InputStreamReader(InputStream in, Charset cs)
Creates an InputStreamReader that uses the given charset. |
InputStreamReader(InputStream in, CharsetDecoder dec)
Creates an InputStreamReader that uses the given charset decoder. |
InputStreamReader(InputStream in, String charsetName)
Creates an InputStreamReader that uses the named charset. |
Constructor and Description |
---|
OutputStreamWriter(OutputStream out)
Creates an OutputStreamWriter that uses the default character encoding. |
OutputStreamWriter(OutputStream out, Charset cs)
Creates an OutputStreamWriter that uses the given charset. |
OutputStreamWriter(OutputStream out, CharsetEncoder enc)
Creates an OutputStreamWriter that uses the given charset encoder. |
OutputStreamWriter(OutputStream out, String charsetName)
Creates an OutputStreamWriter that uses the named charset. |
注:传递String类型的参数,目前只用得到两种"GBK"和"utf-8",如果不传递charset参数,则默认GBK字符集。
4.2 总结
FileReader 是InputStreamReader的子类,也就是一种简化实现,它不需要传递字符集,只能依据默认字符集,完成字节到字符的转换。以下三种写法的功能是相同的:
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
FileWriter同理。
5. 缓冲流
前面进行字节流和字符流的读写时,为了提高读写效率,我们采用数组作为缓冲区,其实Java本身也提供了高效率的IO流,就是缓冲流。
缓冲流分为字节缓冲流和字符缓冲流。
5.1 字节缓冲流
BufferedInputStream(InputStream in):封装一个基本流,以提高它的读取效率
BufferedOutputStream(OutputStream out):封装一个基本流,以提高它的写入效率
其他用法与字节流相同。
例子:字节缓冲流复制文件:
/**
* @Title: bufferedCopy
* @Description: 使用缓冲字节流进行文件复制
* @param: @param fromFile
* @return: void
* @throws
*/
public static void bufferedCopy(File fromFile, File toFile) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(fromFile));
bos = new BufferedOutputStream(new FileOutputStream(toFile));
int len = 0;
byte[] b = new byte[1024];
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
bos.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
5.2 字符缓冲流
BufferedReader(Reader in):封装一个基本字符流,提高它的读取效率
BufferedWriter(Writer out):封装一个基本字节流,提高它的写入效率
除了几个基本方法,BufferedReader的特有方法:
public String nextLine():返回一个文本行,不包含任何换行符,如果已经到流末尾,则返回null
相应地,BufferedWriter的特有方法:
public void newLine():写入一个换行符,至于换行符具体是什么,根据当前系统而定,win是\n\r,linux是\n
例子:字符缓冲流复制文件
/**
* @Title: bufferedCopyByChar
* @Description: 缓冲字符流实现文件复制
* @param: @param fromFile
* @param: @param toFile
* @return: void
* @throws
*/
public static void bufferedCopyByChar(File fromFile, File toFile) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(fromFile));
bw = new BufferedWriter(new FileWriter(toFile));
String line = null;
while((line = br.readLine()) != null) {
// 写入读出的一行,不包括换行符标志
bw.write(line);
// 写入换行!!!
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
5.3 效率总结
以字节流为例:
基本字节流单个字节复制文件效率极低,不要使用这种方法;采用字节数组byte[]缓冲可以明显提高效率
采用java自带的缓冲流,单个字节复制文件效率比基本流+缓冲数组的方法稍低一些;缓冲流+字节数组的方法效率最高,推荐使用这种方法,即5.1中缓冲流的例子
任何情况下都尽量不要使用单个字节读写的方式。