IO流概述
![](https://img.haomeiwen.com/i1969836/eaa270a5c1d3b6ee.png)
- IO流介绍
- IO:输入/输出(Input/Output)
- 流:是一种抽象概念,是对数据传输的总称.也就是说数据在设备间的传输称为流,流的本质是数据传输
- IO流就是用来处理设备间数据传输问题的.常见的应用: 文件复制; 文件上传; 文件下载
- IO流的分类
- 按照数据的流向
- 输入流:读数据
- 输出流:写数据
- 按照数据类型来分
- 字节流
- 字节输入流
- 字节输出流
- 字符流
- 字符输入流
- 字符输出流
- 字节流
- 按照数据的流向
- IO流的使用场景
- 如果操作的是纯文本文件,优先使用字符流
- 如果操作的是图片、视频、音频等二进制文件,优先使用字节流
- 如果不确定文件类型,优先使用字节流.字节流是万能的流
一.File类
1.File类介绍
- 它是文件和目录路径名的抽象表示
- 文件和目录是可以通过File封装成对象的
- 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已.它可以是存在的,也可以是不存在的.将来是要通过具体的操作把这个路径的内容转换为具体存在的
2.File类的构造方法
方法名 | 说明 |
---|---|
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 |
File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例 |
File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例 |
//File(String pathname): 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
File f1 = new File("/Users/Downloads/File");
System.out.println(f1);
//File(String parent, String child): 从父路径名字符串和子路径名字符串创建新的 File实例
File f2 = new File("/Users/Downloads/File","java.txt");
System.out.println(f2);
//File(File parent, String child): 从父抽象路径名和子路径名字符串创建新的 File实例
File f3 = new File("/Users/Downloads/File");
File f4 = new File(f3,"java.txt");
System.out.println(f4);
3.File类创建和删除
-
方法分类
方法名 说明 public boolean createNewFile() 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 public boolean mkdir() 创建由此抽象路径名命名的目录 public boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 public boolean delete() 删除由此抽象路径名表示的文件或目录
//1.创建文件
File f1 = new File("/Users/Downloads/File/java.txt");
System.out.println(f1.createNewFile());
System.out.println("--------");
//2.创建一个目录JavaSE
File f2 = new File("/Users/Downloads/File/JavaSE");
System.out.println(f2.mkdir());
System.out.println("--------");
//3.创建一个多级目录JavaWEB\\HTML
File f3 = new File("/Users/Downloads/File/HTML/1");
System.out.println(f3.mkdirs());
System.out.println("--------");
//4:删除目录下的文件或文件夹
File f4 = new File("/Users/Downloads/File/JavaSE");
System.out.println(f2.delete());
4.File类判断和获取
-
判断功能
方法名 说明 public boolean isDirectory() 测试此抽象路径名表示的File是否为目录 public boolean isFile() 测试此抽象路径名表示的File是否为文件 public boolean exists() 测试此抽象路径名表示的File是否存在 -
获取功能
方法名 说明 public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串 public String getPath() 将此抽象路径名转换为路径名字符串 public String getName() 返回由此抽象路径名表示的文件或目录的名称 public File[] listFiles() 返回此抽象路径名表示的目录中的文件和目录的File对象数组
//创建一个File对象
File f = new File("filepro");
// public boolean isDirectory():测试此抽象路径名表示的File是否为目录
// public boolean isFile():测试此抽象路径名表示的File是否为文件
// public boolean exists():测试此抽象路径名表示的File是否存在
System.out.println(f.isDirectory());
System.out.println(f.isFile());
System.out.println(f.exists());
//public String getAbsolutePath():返回此抽象路径名的绝对路径名字符串
//public String getPath():将此抽象路径名转换为路径名字符串
//public String getName():返回由此抽象路径名表示的文件或目录的名称
System.out.println(f.getAbsolutePath());
System.out.println(f.getPath());
System.out.println(f.getName());
System.out.println("--------");
// public File[] listFiles():返回此抽象路径名表示的目录中的文件和目录的File对象数组
File f2 = new File("filepro");
File[] fileArray = f2.listFiles();
for(File file : fileArray) {
System.out.println(file);
}
5.实例
删除一个多级文件夹
public static void main(String[] args) {
//delete方法,只能删除文件和空文件夹.
//如果现在要删除一个有内容的文件夹?
//先删掉这个文件夹里面所有的内容.最后再删除这个文件夹
File src = new File("filepro");
deleteDir(src);
}
//1.定义一个方法,接收一个File对象
private static void deleteDir(File src) {
//先删掉这个文件夹里面所有的内容.
//递归 方法在方法体中自己调用自己.
//注意: 可以解决所有文件夹和递归相结合的题目
//2.遍历这个File对象,获取它下边的每个文件和文件夹对象
File[] files = src.listFiles();
//3.判断当前遍历到的File对象是文件还是文件夹
for (File file : files) {
//4.如果是文件,直接删除
if(file.isFile()){
file.delete();
}else{
//5.如果是文件夹,递归调用自己,将当前遍历到的File对象当做参数传递
deleteDir(file);//参数一定要是src文件夹里面的文件夹File对象
}
}
//6.参数传递过来的文件夹File对象已经处理完成,最后直接删除这个空文件夹
src.delete();
}
统计一个文件夹中每种文件的个数并打印
public static void main(String[] args) {
//统计一个文件夹中,每种文件出现的次数.
//统计 --- 定义一个变量用来统计. ---- 弊端:同时只能统计一种文件
//利用map集合进行数据统计,键 --- 文件后缀名 值 ---- 次数
File file = new File("/File项目");
HashMap<String, Integer> hm = new HashMap<>();
getCount(hm, file);
System.out.println(hm);
}
//1.定义一个方法,参数是HashMap集合用来统计次数和File对象要统计的文件夹
private static void getCount(HashMap<String, Integer> hm, File file) {
//2.遍历File对象,获取它下边的每一个文件和文件夹对象
File[] files = file.listFiles();
for (File f : files) {
//3.判断当前File对象是文件还是文件夹
if(f.isFile()){
//如果是文件,判断这种类型文件后缀名在HashMap集合中是否出现过
String fileName = f.getName();
String[] fileNameArr = fileName.split("\\.");
if(fileNameArr.length == 2){
String fileEndName = fileNameArr[1];
if(hm.containsKey(fileEndName)){
//出现过,获取这种类型文件的后缀名出现的次数,对其+1,在存回集合中
Integer count = hm.get(fileEndName);
//这种文件又出现了一次.
count++;
//把已经出现的次数给覆盖掉.
hm.put(fileEndName,count);
}else{
// 没出现过,将这种类型文件的后缀名存入集合中,次数存1
hm.put(fileEndName,1);
}
}
}else{
//如果是文件夹,递归调用自己,HashMap集合就是参数集合,File对象是当前文件夹对象代码实现
getCount(hm,f);
}
}
}
二.字节流
-
字节流抽象基类
- InputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类名作为子类名的后缀
1.字节输出流
-
FileOutputStream(String name):创建文件输出流以指定的名称写入文件
-
使用字节输出流写数据的步骤
- 1.创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法
- 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
-
字节流写数据如何实现换行
- windows:\r\n
- linux:\n
- mac:\r
-
字节流写数据如何实现追加写入
- public FileOutputStream(String name,boolean append)
- 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
-
写数据的方法分类
方法名 说明 void write(int b) 将指定的字节写入此文件输出流 一次写一个字节数据 void write(byte[] b) 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据 void write(byte[] b, int off, int len) 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据
static void test() throws IOException {
//1.创建字节输出流的对象
// 注意点:如果文件不存在,会帮我们自动创建出来.
// 如果文件存在,会把文件清空.
FileOutputStream fos = new FileOutputStream("a.txt");
//2,写数据
// 传递一个整数时,那么实际上写到文件中的,是这个整数在码表中对应的那个字符.
fos.write(98);
//添加换行符一个换行
fos.write("\r\n".getBytes());
// 添加中文,需要进行转码
fos.write("哈哈".getBytes());
// 添加数组
byte [] bys = {97,98,99,100,101,102,103};
fos.write(bys,0,4);
//3,释放资源
fos.close(); //告诉操作系统,我现在已经不要再用这个文件了.
}
-
字节流写数据加异常处理
try-catch-finally
static void test1(){
FileOutputStream fos = null;
try {
//System.out.println(2/0);
fos = new FileOutputStream("a.txt");
fos.write(97);
}catch(IOException e){
e.printStackTrace();
}finally {
//finally语句里面的代码,一定会被执行.
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.字节输入流
-
字节输入流
- FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
-
字节输入流读取数据的步骤
- 1.创建字节输入流对象
- 2.调用字节输入流对象的读数据方法
- 3.释放资源
static void test2() throws IOException {
//如果文件存在,那么就不会报错.
//如果文件不存在,那么就直接报错.
FileInputStream fis = new FileInputStream("a.txt");
//一次读取一个字节,返回值就是本次读到的那个字节数据.
//也就是字符在码表中对应的那个数字.
//如果我们想要看到的是字符数据,那么一定要强转成char
int read = fis.read();
System.out.println((char)read);
//读多个字节,每调用一次就读取一个字节
int b;
while ((b = fis.read())!=-1){
System.out.println((char) b);
}
//释放资源
fis.close();
}
3.字节读写数据
- 单个字节读写
static void test3() throws IOException {
//1 创建了字节输入流,准备读数据.
FileInputStream fis = new FileInputStream("a.avi");
//创建了字节输出流,准备写数据.
FileOutputStream fos = new FileOutputStream("b.avi");
//一次读写一个字节
int b;
while((b = fis.read())!=-1){
fos.write(b);
}
// 3.释放资源
fis.close();
fos.close();
}
- 字节数组读写
static void test4() throws IOException {
FileInputStream fis = new FileInputStream("a.avi");
FileOutputStream fos = new FileOutputStream("c.avi");
// //一次读写一个字节数组
byte [] bytes = new byte[1024];
int len; //本次读到的有效字节个数 -- 这次读了几个字节.
while((len = fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fis.close();
fos.close();
}
4.字节缓冲流读写
-
字节缓冲流介绍
- BufferOutputStream:该类实现缓冲输出流.通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
-
构造方法:
方法名 说明 BufferedOutputStream(OutputStream out) 创建字节缓冲输出流对象 BufferedInputStream(InputStream in) 创建字节缓冲输入流对象 -
字节缓冲流读写一个字节
public static void method6() throws IOException {
//字节缓冲流一次读写一个字节
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.avi"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.avi"));
int by;
while ((by=bis.read())!=-1) {
bos.write(by);
}
bos.close();
bis.close();
}
- 字节缓冲流数组读写
static void test5() throws IOException{
//字节缓冲流一次读写一个字节数组
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.avi"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.avi"));
byte[] bys = new byte[1024];
int len;
while ((len=bis.read(bys))!=-1) {
bos.write(bys,0,len);
}
bos.close();
bis.close();
}
三.字符流
1为什么会出现字符流
-
字符流的介绍
由于字节流操作中文不是特别的方便,所以Java就提供字符流
字符流 = 字节流 + 编码表
-
中文的字节存储方式
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
2.编码表
-
什么是字符集
是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
l计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
-
常见的字符集
-
ASCII字符集:
lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
-
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
-
Unicode字符集:
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助字符,使用四字节编码
-
3.字符串中的编码解码
方法名 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节 |
String(byte[] bytes) | 使用平台的默认字符集解码指定的字节数组来创建字符串 |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来创建字符串 |
- 中文解码编码
static void method1() throws UnsupportedEncodingException {
String str = "中文编码";
//利用idea默认的UTF-8将中文编码为一系列的字节
byte[] bytes1 = str.getBytes();
System.out.println(Arrays.toString(bytes1));
//利用默认的UTF-8进行解码
String s1 = new String(bytes1);
System.out.println(s1);
// 指定编码类型:GBK
byte[] bytes2 = str.getBytes("GBK");
System.out.println(Arrays.toString(bytes2));
//利用指定的GBK进行解码
String s2 = new String(bytes2,"GBK");
System.out.println(s2);
}
- 控制台打印
[-28, -72, -83, -26, -106, -121, -25, -68, -106, -25, -96, -127]
中文编码
[-42, -48, -50, -60, -79, -32, -62, -21]
中文编码
4.字符流写数据
-
Writer
: 用于写入字符流的抽象父类 -
FileWrite
r: 用于写入字符流的常用子类 -
构造方法
方法名 说明 FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象 FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象 FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象 FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象 -
成员方法
方法名 说明 void write(int c) 写一个字符 void write(char[] cbuf) 写入一个字符数组 void write(char[] cbuf, int off, int len) 写入字符数组的一部分 void write(String str) 写一个字符串 void write(String str, int off, int len) 写一个字符串的一部分 -
刷新和关闭的方法
方法名 说明 flush() 刷新流,之后还可以继续写数据 close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
static void test1() throws IOException {
//创建字符输出流的对象
FileWriter fw = new FileWriter("a.txt");
// 写一个字符
fw.write(97);
fw.write("\n\r");
// 写出一个字符数组
char [] chars = {97,98,99,100,101};
fw.write(chars);
// 写出字符数组的一部分
fw.write(chars,0,3);
// 写一个字符串
String line = "输出流的对象123-abc";
fw.write(line);
fw.write(line,0,2);
//释放资源
fw.close();
}
5.字符流读数据
-
Reader
: 用于读取字符流的抽象父类 -
FileReader
: 用于读取字符流的常用子类
-
构造方法
方法名 说明 FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader
-
成员方法
方法名 说明 int read() 一次读一个字符数据 int read(char[] cbuf) 一次读一个字符数组数据 -
一次读取一个字符
static void test2() throws IOException {
//创建字符输入流的对象
FileReader fr = new FileReader("a.txt");
//一次读取一个字符
int ch;
while((ch = fr.read()) != -1){
System.out.println((char) ch);
}
//释放资源
fr.close();
}
- 一次读取多个字符
static void test3() throws IOException {
FileReader fr = new FileReader("a.txt");
char [] chars = new char[1024];
int len;
//read方法还是读取,但是一次读取多个字符
//他把读到的字符都存入到chars数组。
//返回值:表示本次读到了多少个字符。
while((len = fr.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
fr.close();
}
6.缓冲字符流
-
BufferedWriter
:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途 -
BufferedReader
:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途 -
构造方法
方法名 | 说明 |
---|---|
BufferedWriter(Writer out) | 创建字符缓冲输出流对象 |
BufferedReader(Reader in) | 创建字符缓冲输入流对象 |
void newLine() | 写一行行分隔符,行分隔符字符串由系统属性定义 |
String readLine() | 读一行文字。 如果已经到达结尾,则为null |
static void test4() throws IOException {
//字符缓冲输入
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
//写出数据
bw.write("字符缓冲");
//跨平台的回车换行
bw.newLine();
//释放资源
bw.close();
//字符缓冲输出
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//使用循环来进行改进
String line;
//可以读取一整行数据。一直读,读到回车换行为止。
//但是他不会读取回车换行符。
while((line = br.readLine()) != null){
System.out.println(line);
}
//释放资源
br.close();
}
四.转换流
1.概述
-
InputStreamReader
:是从字节流到字符流的桥梁,父类是Reader
1.它读取字节,并使用指定的编码将其解码为字符
2.它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集 -
OutputStreamWriter
:是从字符流到字节流的桥梁,父类是Writer
1.是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
2.它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
2. 构造方法
方法名 | 说明 |
---|---|
InputStreamReader(InputStream in) | 使用默认字符编码创建InputStreamReader对象 |
InputStreamReader(InputStream in,String chatset) | 使用指定的字符编码创建InputStreamReader对象 |
OutputStreamWriter(OutputStream out) | 使用默认字符编码创建OutputStreamWriter对象 |
OutputStreamWriter(OutputStream out,String charset) | 使用指定的字符编码创建OutputStreamWriter对象 |
static void test1() throws IOException {
//从字符流到字节流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"),"GBK");
osw.write("中国");
osw.close();
//从字符流到字节流的桥梁
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"),"GBK");
//一次读取一个字符数据
int ch;
while ((ch=isr.read())!=-1) {
System.out.print((char)ch);
}
isr.close();
}
网友评论