java文件处理与IO流知识点整合

常用类的总结

File

文件类,用于访问文件,创建/删除文件或目录。(注意:该类不能对文件内容进行操作)

RandomAccessFile

RAF用于对文件内容进行操作,含有指针(seek),可以自由对文件任意位置进行操作

IO流

IO流按级别分为高级流和低级流;按传输的数据来分可以分为字节流和字符流

注意IO流,不能想读哪里读哪里,只能读取上次读取的最后位置
raf更灵活,有指针,想读文件哪里读哪里
io流适用于网络数据传输

字节流字符流
低级流InputStream
OutputStream
Reader
Writer
FileInputStream
FileOutputStream
高级流BufferedInputStream
BufferedOutputStream
InputStreamReader
OutputStreamWriter
ObjectInputStream
ObjectOutputStream
BufferedReader
BufferedWriter/PrintWriter

注意:IO流的读入与写出的说法

Input是输入流,用于把文件或数据库或网络上的信息读入到程序

Output是输出流,用于程序向文件或数据库或网络上写出数据

输入与输出是以程序为主体,相对于程序来说的

常用类的API总结

File类

主要功能:访问其表示的文件或目录的属性(名字、大小等);创建,删除文件或目录
File常作为访问文件的工具,作为一个参数,传给另外一个new构建对象

File file = new File("./demo.txt"); new一个对象,注意绝对与相对路径

File对象.getName(); 获取文件名

File对象.length(); 获取文件长度(即文件的大小,单位是字节,返回值是long类型)

file.exists(); 判断文件是否已经存在,存在返回true

file.createNewFile(); 创建文件

dir.mkdir(); 创建目录

dir.mkdirs(); 创建多级目录

delete(); 删除文件或目录

访问目录中的子项

File[] listFiles(); 返回一个File的数组,里面存着该目录的所有子项

File[] listFiles(FileFilter filter) 获取一个目录下指定的子项

可以看看递归删除非空目录

RAF类

主要功能:基于指针对文件任意位置进行读写

创建对象时注意传入的参数
第一个参数为文件路径或者文件对象,第二个对象为操作方式(只有 r只读 和 rw读写)

主要操作:read() write() seek()

int read(data) 将文件内容读入到程序

其中传入的data参数为我们指定的字节数组(每次读取多少内容),不写的话读取1字节

不写设置读取的字节数组时:每次调用读取1字节,返回值为读取到的字节,返回-1读取到文件末尾

设置了读取的字节数组时:一次性从文件中读取给定数组总长度的字节量,并将读取到的数据存入该数组中
返回值为实际读取到的字节量,若返回值为-1,则表示读取的为文件末尾(本次没有读取到任何数据)

读入时注意,我们将读入的字节数组转为字符串时用字符串的构造方法
new String(字节数组,字符编码集)

write() 将程序数据写出到文件中

不传参单字节写入,可以传入写入的字节数组,但是要注意,这样的话最后一次若没有读满,将会多写一段倒数第二段的数据,我们要读多少,写多少,针对这种情况

所以块读写时我们常用

void write(byte[] data,int start,int len)
将给定字节数组从下标start处开始连续读写len(读取数据的长度)个字节,再一次性写入文件

在写出时注意,String也提供了字符串转字节数组的方法
byte[] getBytes(“字符集格式”);
将当前字符串按照给定字符集转换为一组字节
传入的参数为字符集名称,不区分大小写,如果不写,按照系统默认的字符集操作

seek() 挪动指针位置的方法

练习:综合练习用户信息系统

这个包含了文件的读写,包含了文件复制

用户需求:

  1. 定义一个java程序User,用于管理用户信息
    定义一个文件user.dat,用于存储用户信息
  2. 定义一个主函数,其功能是用户输入选择的操作
    在循环中定义switch…case…分支结构,循环选择用户操作
    再通过判断用户选择的操作来进行相应操作
    0表示退出循环,1表示用户注册,2表示显示用户信息,3表示修改昵称
  3. 定义一个方法,方法名为addUser(),无返回值无参数
    该方法作用:添加用户信息(用户名,密码,昵称,年龄)
    提示:用户名、密码、昵称都是32字节数组,年龄为4字节int
    要求:提示用户输入信息,获取用户信息后,先通过用户名判断用户存不存在
    不存在的话才进行写入操作,通过write()和getBytes()将信息写入user.dat文件
    注意:每次写入,指针seek到文件末尾
  4. 定义一个方法,方法名为showAllUser(),用于显示所有用户信息
    要求按字节数组读取,信息,并自定义输出格式,
    灵活使用read和new String ,注意Trim去除空格
  5. 定义一个方法upUser() ,用于修改用户昵称
    先提示用户输入需要修改的用户名及其昵称
    查找用户名,如果用户名存在,则进行昵称修改
    注意灵活使用读写和seek,找到用户名后,应该seek到该用户名的后64个位置

按上面要求自己完成一下,如果觉得自己一看就知道怎么写,可以不写,看看我下面自己写的就行了
如果感觉不熟练就练习一下,如果看不太明白需求,可以先看看下面代码(我没写注释,不过你应该看得懂了)

package rafuser;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Scanner;

public class User {
	public static void main(String[] args){
		Scanner scan = new Scanner(System.in);
		System.out.println("******用户信息注册查询系统******");
		while(true){
			System.out.println();
			System.out.println("输入:0(退出),1(用户注册),2(显示用户信息),3(修改昵称)");
			System.out.print("请输入操作名");
			switch (Integer.parseInt(scan.nextLine())){
			case 0:
				System.out.print("谢谢使用,拜拜");
				scan.close();
				return;
			case 1:
				addUser();
				break;
			case 2:
				showAllUser();
				break;
			case 3:
				upUser();
				break;
			}
		}
	}
	public static void addUser(){
		System.out.println("欢迎注册...");
		Scanner scan = new Scanner(System.in);
		System.out.print("请输入用户名:");
		String name = scan.nextLine();
		System.out.print("请输入密码:");
		String pwd = scan.nextLine();
		System.out.print("请输入昵称:");
		String nick = scan.nextLine();
		System.out.print("请输入年龄:");
		int age = scan.nextInt();
		//切记:不要长时间占用文件资源,所以应当先获取用户输入
		//下面再进行文件写入
		try (
				RandomAccessFile raf = new RandomAccessFile("user.dat","rw");
				){
			//先进行判断,查看用户是否已经存在
			boolean flag = true;
			for(int i=0;i<raf.length();i+=100){
				raf.seek(i);
				byte[] data = new byte[32];
				raf.read(data);
				String username = new String(data,"utf-8").trim();
				if(name.equals(username)){
					flag = false;
				}
			}
			//如果用户不存在
			if(flag){
				//可以多次运行着个程序,每次运行从末尾添加注册用户
				raf.seek(raf.length());
				//开始写入信息
				raf.write(Arrays.copyOf(name.getBytes("utf-8"), 32));
				raf.write(Arrays.copyOf(pwd.getBytes("utf-8"), 32));
				raf.write(Arrays.copyOf(nick.getBytes("utf-8"), 32));
				raf.writeInt(age);
				System.out.println("注册成功!");
			}else{
				System.out.println("用户已存在!");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static void showAllUser(){
		System.out.println("开始查询...");
		try (
				RandomAccessFile raf = new RandomAccessFile("user.dat","r");
				){
			byte[] data = new byte[32];
			for(int i=0;i<raf.length()/100;i++){
				//依次读取字节数组长度的数据
				raf.read(data);
				String name = new String(data,"utf-8").trim();
				raf.read(data);
				String pwd = new String(data,"utf-8").trim();
				raf.read(data);
				String nick = new String(data,"utf-8").trim();
				int age = raf.readInt();
				System.out.println("用户"+(i+1)+"信息为:用户名("+name+"),密码("+pwd+"),昵称("+nick+"),年龄("+age+")");
				System.out.println("显示完毕!");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static void upUser(){
		System.out.println("进入修改...");
		Scanner scan = new Scanner(System.in);
		System.out.println("需要修改昵称的用户名:");
		String sname = scan.nextLine();
		System.out.println("修改昵称为:");
		String snick = scan.nextLine();
		try (
				RandomAccessFile raf = new RandomAccessFile("user.dat","rw");
				){
			for(int i=0;i<raf.length();i+=100){
				raf.seek(i);
				byte[] data = new byte[32];
				raf.read(data);
				String name = new String(data,"utf-8").trim();
				if(sname.equals(name)){
					raf.seek(i+64);
					raf.write(Arrays.copyOf(snick.getBytes("utf-8"), 32));
				    System.out.println("修改成功!");
				    return;
				}
			}
			System.out.println("查无此人!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

IO流

理解为管道连通,就拿水管理解就行了,IO流常用于网络数据流通

java IO将"读"与"写"按照方向进行了划分:

输入:从外界到程序的方向,用于让程序获取外界数据
因此输入是"读"数据的操作

输出:从程序到外界的方向,用于将数据"写"出的操作.

输入流(InputStream)、输出流(OutputStream),流动的是字节流

java IO以"流"的形式表示读写功能

InputStream是所有字节输入流的父类,其定义了基础的读取方法

通过输入流我们可以连接上外界设备从而读取该设备数据

常用的方法如下:

  • int read()
    读取一个字节,以int形式返回,该int值的"低八位”有效,若返回值为-1则表示EOF
  • int read(byte[] d)
    尝试最多读取给定数组的length个字节并存入该数组,返回值为实际读取到的字节量。

OutputStream是所有字节输出流的父类,其定义了基础的写出方法

常用的方法如下:

  • void write(int d)
    写出一个字节写的是给定的in的”低八位”
  • void write(byte[] d)
    将给定的字节数组中的所有字节全部写出

字节流:

文件流介绍

文件流是一对低级流,作用是连接到文件上,用于读写文件数据

可以看看,使用文件流完成文件的复制工具

package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 使用文件流完成文件的复制工具
 * @author Tian
 *
 */
public class CopyDemo {
	public static void main(String[] args) throws IOException {
		//创建存取数据的字节数组(数据容器,相当于交换变量值时的第三方变量)
		byte[] data = new byte[1024*10];
		
		//记录运行开始时间
		long start = System.currentTimeMillis();
		
		//读取源文件,创建输入流(注意输入是从文件输入到现在的这个代码程序)
		FileInputStream fis = new FileInputStream("./image.jpg");
		//写入复制文件,创建输出流(注意输出是从现在这个程序写出到文件)
		FileOutputStream fos = new FileOutputStream("./image_copy.jpg");
		
		int len = -1; //记录读取的实际长度
		
		//循环写入每次的10k大小字节数组
		while((len = fis.read(data))!=-1){//读取信息并返回读取长度存储到len中
			//将字节数组的信息写入复制的文件
			fos.write(data,0,len);
		}
		
		//读取完毕,关闭输入流
		fis.close();
		//写出完毕,关闭输出流
		fos.close();
		
		//记录运行结束时间
		long end = System.currentTimeMillis();
		
		//程序运行结束,输出时间
		System.out.println("复制完毕,耗时:"+(end-start)/1000+"s");
	}
}

缓冲流介绍

是一组高级流,在流连接中的提高速读写效率,使得我们在进行读写操作时用单字节读写也能提高读写的效率

BufferedOutputStream缓冲输出流内部维护着一个缓冲区(一个8k字节数组)
每当我们向该流写数据时,都会先将数据存入缓冲区
当缓冲区已满时,缓冲流会将数据一次性全部写出

无论我们使用缓冲流进行何种读写(单字节或块读写),最终都会被缓冲流转换为块读写,来提高效率

缓冲流注意事项

缓冲输出流的缓冲区问题:

使用缓冲输出流可以提高写出效率,但是这也存在着一个问题,就是写出数据缺乏即时性。
有时我们需要在执行完某些写出操作后,就希望将这些数据确实写出
而非在缓冲区中保存直到缓冲区满后才写出
这时我们可以使用缓冲流的一个方法flush

void flush() 清空缓冲区,将缓冲区中的数据强制写出
flush方法是OutputStream中定义的方法
所有的输出流都具有该方法,但是只有缓冲流的该方法实现了,有实际意义
其他的流具有该方法的目的是在流连接中传递缓冲操作给缓冲流

flush的作用是将缓冲流中已经缓存的数据一次性写出
频繁的调用flush方法会提高写出次数从而降低写出效率,但是能保证数据写出的及时性
所以需要根据实际需求选择,比如文件复制不用,但是聊天需要用flush

对象流介绍

对象流是一组高级流,在流连接中的作用是方便读写java对象(对象与字节的转换由对象流完成)
将java对象按照其结构,转换成字节流,存入文件

对象流的三大块:类的创建、类对象的写出(对象输出流)、类对象的读入(对象输入流)

类的创建时,注意要实现Serializable的接口,该接口为序列化操作

类对象的写出(对象输出流):writeObject(序列化过的类的对象)

  1. 注意对象流的写入方法方法可能抛出:NotSerializableException
    这说明写出的对象所属的类没有实现Serializable接口,我们需要在该类中实现该接口
  2. 写入文件后发现该文件的实际数据量比当前对象保存的内容要大
    这是因为这组字节除了包含了该对象的数据外,还含有这个对象的结构信息
    如果想减少不必要的开销,我们可以用 transient 关键字修饰不必要的属性

类对象的读入(对象输入流):Person p = (Person) ois.readObject();

  1. 我们需要先创建一个相应对象,并且读取出来的对象要注意强转为我们新建引用的对象
  2. 注意如果文件中的数据不是相应的数据类型会报ClassNotFoundException的异常

ps. 在进行文件对象的读取时,我们进行了反序列化操作(把字节转换为对应数据)

字符流介绍

字符流是为了方便我们读写文本

java.io.Reader
java.io.Writer
上述两个类是所以字符流的超类,规定了所有字符流都必须具备的读写字符的相关方法

字符流是以字符(char)为单位读写数据的
一次处理一个unicode
字符流的底层仍然是基本的字节流,只是字符与字节的转换,字符流自行完成
字符流封装了字符的编码解码算法

字符流仅仅用于文本数据IO操作

转换流介绍

转换流是一对字符流,同时他们也是高级流,而且是唯一能连接字节流的字符流(相当于转换器)

在实际开发时,我们通常不会直接操作这两个流
但是在读写文本数据时,流连接中,他们是重要的一环
负责衔接其他字符高级流与字节流

在使用转换流时,要注意需要先创建低级流,转换流是高级流,依附于低级流实现

缓冲字符流

缓冲字符输出流

java.io.BufferedWriter (实现PW需要传入这个的对象)

java.io.PrintWriter (一般用这个 pw)

具有自动 行刷新 的缓冲字符输出流
PrintWriter的构造方法传入参数为流和是否开启自动行刷新功能,默认关闭
根据具体需求,看是否需要打开该功能

内部总是连接BufferedWriter作为其缓冲加速操作

注意:这个高级流不用自己创建低级流,因为在这个高级流底层为我们实现了流连接
但是我们根据需求,如果要用其他的流连接线路,我们需要自行完成流连接
看着好像是低级流,其实是因为在这个类的构造方法中已经实现了

查看该类源码我们发现,该类构造方法内部会自动进行流连接操作,
分别连接缓冲字符流、转换流、文件流

/* Private constructor */
    private PrintWriter(Charset charset, File file)
        throws FileNotFoundException
    {
        this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
             false);
    }

所以我们可以省略流连接,但是建议我们自行创建流连接,因为默认的流连接有限制性

其写出的方法与控制台输出的方法类似print与println

PW的自动行刷新功能

PrintWriter的构造方法,还可以再传入一个boolean值的参数,传入true表示打开了自动行刷新功能

会每输入一行,就存入一行,类似于每输入一行就调用一次flush清空缓冲区

缓冲字符输入流

java.io.BufferedReader

块读取文本数据,并且可以按行读取字符串

其读取方法含有行读取
String readLine()
该方法会:连续读取若干字符,直到读取到换行符为止,将换行符之前的字符组成一个字符串返回
如果返回值为null,表示流读取到文件末尾了

练习:实现简易记事本工具

通过缓冲字符流来操作,要求自行使用流连接创建pw

程序启动后,要求用户选择操作,可以进行查看记事本,或者编辑记事本

编辑记事本时:要求输入文件名,然后对文件写入内容
将后续在控制台输入的每行字符串都按行写入到该文件中
当单独输入了exit时,程序退出

查看记事本时:要求输入查看的文件名,按行读取文件,将读取内容显示出来

先自己想怎么做,做不出来的话,可以先看一遍下面代码,有不会的忘记的可以查看以前笔记

package pwtext;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Scanner;

public class PwTxt {
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		System.out.println("******文本编辑器******");
		while(true){
			System.out.println();
			System.out.println("输入:0(退出),1(编辑文本),2(显示文本)");
			System.out.print("请输入操作名");
			switch (Integer.parseInt(scan.nextLine())){
			case 0:
				System.out.print("谢谢使用,拜拜");
				scan.close();
				return;
			case 1:
				editorTxt();
				break;
			case 2:
				showTxt();
				break;
			}
		}
	}
	
	public static void editorTxt(){
		Scanner input = new Scanner(System.in);
		//接收文件名
		System.out.print("请输入文件名:");
		String path = input.nextLine()+".txt";
		try (
				//创建低级流,文件流,接收文件路径
				FileOutputStream fos= new FileOutputStream(path);
				//创建高级流,转换流,转换字节流为字符流,接收文件编码格式
				OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
				//创建缓冲字符流
				BufferedWriter bw = new BufferedWriter(osw);
				//true表示开启自动换行
				PrintWriter pw = new PrintWriter(bw,true);
				){
			//提示用户记事本已经打开,提示输入
			System.out.println("请输入内容:(单行输出exit退出记事本)");
			while(true){
				//创建字符串容器,接收控制台输入的值
				String str = input.nextLine();
				if("exit".equals(str)){
					break;
				}
				pw.println(str);
				//如果想要写一句存一句,可以调用flush方法
				//清空缓冲区,写一句存一句
				//pw.flush();
			}
			//写出完毕
			System.out.println("写出完毕!");
			//关闭pw,关闭时会自动调用缓冲流中的flush方法清空缓冲区,所以要记得写关闭
			//这里我们放在try后面的()中自动关闭了
			//pw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void showTxt(){
		Scanner input = new Scanner(System.in);
		//接收文件名
		System.out.print("请输入文件名:");
		String path = input.nextLine()+".txt";
		
		try (
				FileInputStream fis= new FileInputStream(path);
				InputStreamReader isr = new InputStreamReader(fis);
				BufferedReader br = new BufferedReader(isr);
				){
			//String readLine(),读取行文件方法
			String line = null;
			while((line = br.readLine())!=null){
				System.out.println(line);
			}
			//br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值