1.前置知识:字符集
简介
使用ASCII码表示128个字符,第一位都是0。
GBK:一个中文字符编码成两个字节存储。包含ASCII,汉字第一个字节第一位必须是1,以此来区分是汉字还是字母或者其他。
Unicode:万国码。
UTF-32:四个字节表示一个字符。
UTF-8:可变长编码方案。一到四个字不等。ASCII占一个字节,汉字占三个字节
字符集的编码与解码
2.IO流
概述
输入流和输出流。
(1)字节输入流 InputStream FileInputStream 从磁盘读内容到内存
(2)字节输出流 OutStream FileOutStream
(3)字符输入流 Reader FileReader
(4)字符输出流 Writer FileWriter
以上四个都是抽象类,后面是他们的实现类。
(1)FileInputStream 文件字节输入流(读一个字节)
public class FileInputStreamTest1 {
public static void main(String[] args) throws Exception {
// 1. 创建文件字节输入流对象,与源文件接通。
// InputStream is = new FileInputStream(new File("file-io-app\\src\\a01.txt"));
// 简化写法,推荐使用。
InputStream is = new FileInputStream("file-io-app\\src\\a01.txt");
// 2. 开始读取文件的字节数据:
// public int read():每次读一个字节返回,如果没有数据了,返回-1。
int b1 = is.read();
System.out.println((char) b1);
int b2 = is.read();
System.out.println((char) b2);
int b3 = is.read();
System.out.println(b3);
//循环读取,读中文会出错
while((b1 = is.read()) != -1){
System.out.println((char) b1);
is.close();//关闭通道
}
}
上面代码有很多去缺陷。1.读取性能很差,因为这是到硬盘去读,效率低。2.读取汉字会乱码。
流使用之后需要关闭。
(2)读取多个字节
public class FileInputStreamTest2 {
public static void main(String[] args) throws Exception {
// 1. 创建一个字符输入流对象以代表字符输入流管道与源文件接通。
InputStream is = new FileInputStream("file-io-app\\src\\a\\data.txt");//存的是abcde
// 2. 开始读取文件中的字节数据,每次读取多个字节。
byte[] buffer = new byte[3];
int len = is.read(buffer);
String rs = new String(buffer);
System.out.println(rs);
System.out.println("当次读取的字节数是:" + len);//abc
// 第二次读取
int len2 = is.read(buffer);
String rs2 = new String(buffer, 0, len2);//不加后边的0-len2,读的是dec,加了读的是de
System.out.println(rs2);
System.out.println("当次读取的字节数是:" + len2);
// 第三次读取
int len3 = is.read(buffer);
System.out.println(len3); // -1
//循环,
byte[] buffer = new byte[3];
int len; // 记录每次读取了多少个字节
while ((len = is.read(buffer)) != -1) {
// 注意:读取多少,输出多少
String rs = new String(buffer, 0, len);
System.out.print(rs);
}
// 关闭输入流
is.close();
}
}
上面代码读取性能的搭配提升,但读取汉字也会出现问题,会出现乱码。
(3)一次性读完全部字节
public class FileInputStreamTest3 {
public static void main(String[] args) throws Exception {
// 1. 一次性读取完文件的全部字节到一个字节数组中去。
// 创建一个字符输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\a03.txt");
// 2. 准备一个字节数组,大小与文件的大小正好一样大。
File f = new File("file-io-app\\src\\a03.txt");
long size = f.length();
byte[] buffer = new byte[(int) size];
int len = is.read(buffer);
System.out.println(new String(buffer));
// 输出文件大小和读取的字节数
System.out.println(size);
System.out.println(len);
// 使用 readAllBytes() 方法读取文件所有字节
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));
// 关闭输入流以释放资源
is.close();
}
}
可以解决中文乱码的问题,适合读相对较较小的文件。如果文件过大,创建的字节数组也会过大,会引起内存溢出。
读取文本内容更适合用字符流。
字节流适合做数据的转移,如文件复制。
(4)文件字节输出流
把内存的数据以字节的形式写到文件中去。
public class FileOutputStreamTest4 {
public static void main(String[] args) throws Exception {
// 追加数据到文件的管道
OutputStream os = new FileOutputStream("file-io-app/src/itheima04out.txt", true);
// 2. 开始写字节数据出去
os.write(97); // 97是一个字节,代表'a'
os.write('b'); // 'b'也是一个字节
byte[] bytes = "我爱你中国abc".getBytes();
os.write(bytes);
os.write(bytes, 0, 15);// 从bytes数组的0开始,写入15个字节
//换行符
os.write("\r\n".getBytes());
// 关闭流
os.close();
}
}
案例:文件复制
public class FileCopy {
public static void main(String[] args) throws Exception {
// 1. 创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("D:/resource/meinv.png");
// 2. 创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("C:/data/meinv.png");//这里需要自己先写出文件名,因为系统不会自己创建
// 3. 创建一个字节数组,负责转移字节数据
byte[] buffer = new byte[1024]; // 1KB
// 4. 从字节输入流中读取字节数据,每次读到字节数组中,读多少写出多少
int len; // 记录每次读取了多少字节
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 关闭流
os.close();
is.close();
System.out.println("复制完成!");
}
}
一切文件的复制,不会出错,虽然读的时候是分开读的,但是传输完成会接着读,只要编码合适就能成功显示。
释放资源的方式
前面学的close方法关闭通道会出现问题,比如程序异常了,就不会执行后边的程序,通道关不上。下面这两种是解决方法。
(1)try-catch-finally
finally代码区的特点:无论try的程序是否正常执行了,还是出现了异常,最后一定会执行finally的代码,除非jvm终止,加return也没用。
注意:在finally不要加return语句。
finally作用:一般用于执行完程序释放资源。
public class FileCopyWithExceptionHandling {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;//放在前边,是因为如果放在try中,finally无法调用is os
try {
// 1. 创建一个字节输入流管道与源文件接通
is = new FileInputStream("file-io-app\\src\\a03.txt");
// 2. 创建一个字节输出流管道与目标文件接通
os = new FileOutputStream("file-io-app\\src\\a03copy.txt");
// 3. 创建一个字节数组,负责转移字节数据
byte[] buffer = new byte[1024]; // 1KB
// 4. 从字节输入流中读取字节数据,每次读到字节数组中,读多少写出多少
int len; // 记录每次读取了多少个字节
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("复制完成!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源的管道
try {
if (os != null) os.close();
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)try-with-resource
更简单的资源释放方案。
public class Test3 {
public static void main(String[] args) {
try (
// 1. 创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");
// 2. 创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("file-io-app\\src\\itheima03copy.txt")
//这里只能放资源,int a = 1;会报错
//资源是实现了AutoCloseable接口的对象,在try语句块结束时自动调用close()方法释放资源
//也可以自己写一个类实现AutoCloseable接口,在try语句块结束时自动调用close()方法释放资源
) {
// 3. 创建一个字节数组,负责转移字节数据
byte[] buffer = new byte[1024]; // 1KB
// 4. 从字节输入流中读取字节数据,每次读到字节数组中,读多少写出多少
int len; // 记录每次读取了多少字节
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("复制完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
try这里只能放资源,int a = 1;会报错
资源是实现了AutoCloseable接口的对象,在try语句块结束时自动调用close()方法释放资源
也可以自己写一个类实现AutoCloseable接口,在try语句块结束时自动调用close()方法释放资源
标签:字节,buffer,IO,new,out,os,读取 From: https://blog.51cto.com/u_16382144/11875205