今天我们说说java代码中对文件的操作,比如新建删除文件,读取文件内容等。
File类 |
File类用于操作文件和目录,可对文件或目录进行新建,删除和重命名等操作。但是如果要访问文件内容本身,就需要用到IO流了。
(1)访问文件和目录方法
public class FileTest{
public static void main(String[] args) throws IOException{
//以当前路径来创建一个File对象
File file=new File("a");
//在当前路径下创建一个临时文件
File temFile=File.createTempFile("aaa",".txt",file);
//获取文件名
file.getName();
//获取绝对路径
file.getAbsoluteFile();
//判断文件是否存在
tmpFile.exists();
//指定当jvm退出时删除该文件
tmpFile.deleteOnExit();
//使用list()方法列出当前路径下的所有文件和路径
String[] fileList=file.list();
for(String filename:fileList ){
System.out.println(fileName);
}
}
}
(2)文件过滤器
File file=new File("a");
//使用目标类型为FilenameFileter的Lambda表达式实现文件过滤
//如果文件名以.java 结尾,或者文件对应一个路劲,则返回true
String[] nameList=file.list((dir,name)->name.endsWith(".java") || new File(name).isDirectory() );
for(String name:nameList){
System.out.println(name);
}
IO流 |
IO流是用来实现数据的输入/输出操作。使用输入机制,允许程序读取外部数据和用户输入数据,比如来自磁盘、光盘等存储设备的数据。使用输出机制,允许程序记录运行状态,将程序数据输出到磁盘、光盘等存储设备中。
IO流按不同的分类方式,可以分为输入流和输出流、字节流和字符流、节点流和处理流。下面对其进行分别介绍。
(1)输入流、输出流(按流向不同)
这里的输入输出是从程序运行所在内存的角度来说的。举个例子来说,数据从服务器通过网络流向客户端。那服务器的内存负责将数据输出到网络,因此服务器的程序使用的是输出流。同理,客户端使用的是输入流。
(2)字节流、字符流(按操作的数据单元不同)
字节流和字符流的用法几乎一样,只是操作的数据单元不同,字节流操作的数据单元是字节(8位),字符流操作的数据单元是字符(16位)。
类比到现实,你买水果是几个几个的买,小明买水果是几箱几箱的买。东西是一样的东西,只是操作的数据单元不同,一个是“个”,一个是“箱”。
字节流 | 字符流 | |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
使用举例:
- 1、以字节为单位,对数据进行读取。用的是InputStream类。
public class FileInputStreamTest{
public static void main(String[] args) throws IOException
{
//创建字节输入流
FileInputStream fis=new FileInputStream("a.java");
byte[] b=new byte[1024];
int hasRead=0;
while((hasRead=fis.read(bbuf))>0)
{
//取出字节,将字节数组转换为字符串输入
System.out.print(new String(bbuf,0,hasRead));
}
//关闭输入流
fis.close();
}
}
值得一提的是,如果改为以字符为单位,只需把FileInputStream改为FileReader,然后定义数组时不同,其余的几乎不变。
- 2、以字符为单位,对数据进行写入。用的是Writer类。
//在当前目录下输出一个jinse.txt文件
public class FileInputWriterTest{
public static void main(String[] args)
{
try(
FileWriter fw=new FileWriter("jinse.txt");
){
fw.write("锦瑟-李商隐\r\n");
fw.write("锦瑟无端五十弦,一弦一柱思华年。\r\n");
fw.write("庄生晓梦迷蝴蝶,望帝春心托杜鹃。\r\n");
fw.write("沧海月明珠有泪,蓝田日暖玉生烟。\r\n");
fw.write("此情可待成追忆?只是当时已惘然。\r\n");
}catch(IOException ioe){
ioe.printStackTrace();
}
}
}
同样,如果改为字符,修改的主要部分是将Writer改为OutputStream。如此,和本部分开头的表不谋而合了。
(3)节点流、处理流(按角色不同)
节点流(低级流):从/向一个特定的IO设备读/写数据的流。
处理流(高级流):对一个已经存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。
处理流的好处是包了一层,所以处理起来更方便。只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源。下面演示其用法:
//使用PrintStream处理流来包装OutputStream
try(
FileOutputStream fos=new FileOutputStream("test.txt");
PrintStream ps=new PrintStream(fos);
){
//使用PrintStream执行输出
ps.println("普通字符串");
//使用PrintStream输出对象
ps.println(new PrintStreamTest());
}
}catch(IOException ioe)
{
ioe.printStackTrace();
}
(4)转换流
转换流用于将字节流转换为字符流。
那有没有能将字符流转换为字节流的方法呢?没有。我们知道,字符流比字节流的粒度更大,操作起来更方便。既然已经是操作起来更方便的粒度了,就没必要再拆分成更小的粒度了。
就像你在超市买了一箱奶,直接提回家就好。没有必要再把其拆分成一袋一袋的了。
我们使用InputStreamReader将字节输入流转换成字符输入流,OutputStreamWriter将字节输出流转换为字符输出流。下面举个例子来说明:
public class KeyinTest
{
public static void main(String[] args){
try(
//将System.in对象转换成Reader对象
InputStreamReader reader=new InputStreamReader(System.in);
//将普通的Reader包装成BufferedReader
BufferedReader br=new BufferedReader(reader);
){
String line=null;
while((line=br.readLine())!=null){
//如果读取的字符串是“exit”,则程序退出
if(line.equals("exit")){
System.exit(1);
}
//打印读取的内容
System.out.println("输入内容为:"+line);
}
}catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}
重定向 |
重定向是什么意思?在默认情况下,程序通过System.in来从键盘获取输入,通过System.out将数据输出到屏幕。通过重定向,我们可以从指定文件获取输入,输出到指定文件中。下面显示一下重定向到文件输出。
try(
//一次性地创建PrintStream输出流
PrintStream ps=new PrintStream(new FileOutputStream("out.txt"))
){
//将标准输出重定向到ps输出流
System.setOut(ps);
System.out.println("普通字符串");
System.out.println(new RedirectOut());
}
}catch(IOException ex)
{
ex.printStackTrace();
}
运行完此代码后,打开系统当前路径下的out.txt文件,即可看到程序中的输出内容。重定向标准输入类似,不再赘述。
随机访问 |
RandomAccessFile是一个文件内容访问类,可以和IO一样,对文件的内容进行读取和写入。与其不一样的地方在于,RandomAccessFile支持“随机访问”的方式,程序可以直接跳转到文件的任意地方来读写数据。
“随机访问”包括访问指定的中间部分数据、向指定文件后追加内容、向指定位置插入内容等。下面演示使用RandomAccessFile来访问指定的中间部分数据。
//程序从300字节开始读取
try(
//创建一个RandomAccessFile对象,并赋予只读的权限
RandomAccessFile raf=new RandomAccessFile("test.java","r")
){
//获取RandomAccessFile对象文件指针的位置,初始值为0
raf.getFilePointer();
//移动raf的文件记录指针的位置
raf.seek(300);
byte[] bbuf=new byte[1024];
//用于保存实际读取的字节数
int hasRead=0;
while((hasRead=raf.read(bbuf))>0){
System.out.println(new String(bbuf,0,hasRead));
}
}catch(IOException ex)
{
ex.printStackTrace();
}
小结 |
不知读者是否对try…catch结构了解,我们将资源放到try块里,当try块完成后,会自动对资源进行关闭。如果不使用此模块,可在try…catch…finally结构里,将**.close()放到finally块里,对资源进行关闭。
资源可以包括IO流资源,数据库连接关闭资源等。具体可参考【java基础】异常处理是啥?有啥用?
try(
……
){
……
}
}catch(IOException ex)
{
ex.printStackTrace();
}
在本文中,我们对IO流的概念和使用进行了总结,下篇将对序列化/反序列化和NIO进行总结。