前置知识
ASCII
ASCII ( American Standard Code for Information Interchange ) :美国信息交互标准代码(包括了英文和符号)
标准的 ASCII 使用 1 个字节存储一个字符
1 字节:00000000
GBK
汉字编码字符集,包含了 2 万多个汉字等字符,GBK 中一个中文字符编码成两个字节的形式存储
注意:GBK 兼容了 ASCII 字符集
2 字节:00000000,00000000
Unicode
Unicode 是国际组织制定的,是可以容纳世界上所有文字、符号的字符集
UTF-8:英文字符、数字等只占 1 个字节(兼容标准 ASCII 编码),汉字字符占用 3 个字节
编码与解码
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception {
// 1. 编码
String data = "a我b";
byte[] bytes = data.getBytes(); // 不填参数,默认按照当前编辑器平台UTF-8进行编码
System.out.println(Arrays.toString(bytes));
// 按照指定字符集进行编码
byte[] bytes1 = data.getBytes("GBK");
System.out.println(Arrays.toString(bytes1));
// 2. 解码
String s1 = new String(bytes);
System.out.println(s1);
String s2 = new String(bytes1,"GBK");
System.out.println(s2);
}
}
IO 流
体系介绍
IO 流体系
- 字节流
- 字节输入流(InputStream)===>(FileInputStream)
- 字节输出流(OutputStream)===>(FileOutputStream)
- 字符流
- 字符输入流(Reader)===>(FileReader)
- 字符输出流(Writer)===>(FileWriter)
注意!输入流、输出流都是指对 “内存” 的输入和输出,例如:文件输入流 FileInputStream 指的是"文件对内存的输入流"
文件字节输入流
- 每次读取一个字节
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 1. 创建文件字节输入流管道,与源文件接通
// InputStream is = new FileInputStream(new File("E:/Desktop/temp.txt"));
InputStream is = new FileInputStream("E:/Desktop/temp.txt"); // 简化写法
// 2. 开始读取文件的字节数据(当前文件里面写有"ab"两个字母)
// 每次读取一个字节返回,如果没有数据了,返回-1
// int b1 = is.read();
// System.out.println(b1); // 97
//
// int b2 = is.read();
// System.out.println((char) b2); // b
//
// int b3 = is.read();
// System.out.println(b3); // -1
// 3. 用循环改造上面代码
int b;
while ((b = is.read()) != -1) { // 此方法还是有问题:读取数据性能很差!读取中文字符一定会乱码
System.out.println((char) b);
}
// 运行结果
// a
// b
is.close(); // 一定要记得关闭管道!释放它所占据的系统资源
}
}
- 每次读取多个字节
import java.io.FileInputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 文件中的数据:abc66
// 1. 创建文件字节输入流管道,与源文件接通
InputStream is = new FileInputStream("E:/Desktop/temp.txt");
// 2. 开始读取文件中的字节数据
byte[] buffer = new byte[3];
int len = is.read(buffer); // 每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1
String res = new String(buffer);
System.out.println("读取的内容是: " + res);
System.out.println("当次读取的字节数量: " + len);
int len2 = is.read(buffer);
// 注意: 读取多少,倒出多少
String res2 = new String(buffer, 0, len2);
System.out.println("读取的内容是: " + res2);
System.out.println("当次读取的字节数量: " + len2);
int len3 = is.read(buffer);
System.out.println(len3); // -1
is.close(); // 关闭输入流管道
// 运行结果:
// 读取的内容是: abc
// 当次读取的字节数量: 3
// 读取的内容是: 66
// 当次读取的字节数量: 2
// -1
}
}
// 上面代码的改造
import java.io.FileInputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 文件中的数据:abc66
// 1. 创建文件字节输入流管道,与源文件接通
InputStream is = new FileInputStream("E:/Desktop/temp.txt");
// 2. 读取文件中的字节数据(使用循环的方法)
byte[] buffer = new byte[3];
int len; // 记住读取了多少个字节
while ((len = is.read(buffer)) != -1) {
String res = new String(buffer, 0, len); // 将数组中的 0 ~ len转为字符串输出
System.out.println(res);
}
// 性能得到了明显的提升
// 但是!这种方案也不能避免读取汉字输出乱码的问题
is.close(); // 关闭输入流管道
}
}
- 一次性读取完全部字节
// 方法一
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 文件中的数据:abcd我爱你66
// 一次性读取完文件中的所有字节到数组中去
// 1.创建文件字节输入流管道,与源文件接通
InputStream is = new FileInputStream("E:/Desktop/temp.txt");
// 2.准备一个字节数组,它的大小需要和文件的大小正好一样大
File f = new File("E:/Desktop/temp.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(len);
System.out.println(size);
is.close(); // 关闭输入流管道
// 运行结果
// abcd我爱你66
// 15
// 15
}
}
// 方法二
import java.io.FileInputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 文件中的数据:abcd我爱你66
// 一次性读取完文件中的所有字节到数组中去
// 1.创建文件字节输入流管道,与源文件接通
InputStream is = new FileInputStream("E:/Desktop/temp.txt");
// 2.使用封装好的功能
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer)); // abcd我爱你66
is.close(); // 关闭输入流管道
}
}
文件字节输出流
import java.io.FileOutputStream;
import java.io.OutputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 1. 创建一个字节输出流管道与目标文件接通(默认是覆盖原文件内容的)
// OutputStream ops = new FileOutputStream("E:/Desktop/a.txt");
// 如果是想要一个能够追加数据的管道:请将参数append的值设为true
OutputStream ops = new FileOutputStream("E:/Desktop/a.txt", true);
// 2. 开始写字节数据出去了
// write() : 如果直接写内容进去,每次只能写一个字节; 如果用byte[]数组,则不受限制
ops.write(97);
ops.write('b');
// ops.write('王'); // UTF-8字符集下,汉字占3个字节,因此这里开始会乱码
byte[] bytes = "okok我爱你abc".getBytes();
ops.write(bytes);
// 换行符 : 为了不仅在 Windows 上能使用,我们建议写全,即:把 \n 写为 \r\n
byte[] bytes1 = "\r\n".getBytes();
ops.write(bytes1);
ops.close(); // 关闭输出流管道,释放被占用的系统资源
}
}
文件复制案例
// 以下代码,可以复制一切文件
// 原因:任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后文件格式一致就没问题!
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Test {
public static void main(String[] args) throws Exception {
// 复制照片
// 1. 创建一个字节输入流管道与源文件接通
InputStream inputStream = new FileInputStream("E:/Desktop/photo.jpg");
// 2. 创建一个字节输出流管道与目标文件接通
OutputStream outputStream = new FileOutputStream("E:/Desktop/aaa/photo.jpg");
// 3. 创建一个字节数组,负责转移字节数据
byte[] buffer = new byte[1024]; // 1KB
// 4. 从字节输入流中读取字节数据,写出去到字节输出流中,读多少写多少
int len; // 记住每次读取了多少个字节
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
outputStream.close();
inputStream.close();
System.out.println("Successful!");
}
}
释放资源
// 方式一 : 手动释放
import java.io.*;
public class Test {
public static void main(String[] args) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
System.out.println(10 / 0); // 错误代码(测试用)
// 1. 创建一个字节输入流管道与源文件接通
inputStream = new FileInputStream("E:/Desktop/photo.jpg");
// 2. 创建一个字节输出流管道与目标文件接通
outputStream = new FileOutputStream("E:/Desktop/aaa/photo.jpg");
// 3. 创建一个字节数组,负责转移字节数据
byte[] buffer = new byte[1024]; // 1KB
// 4. 从字节输入流中读取字节数据,写出去到字节输出流中,读多少写多少
int len; // 记住每次读取了多少个字节
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// 释放资源的操作
try {
if (outputStream != null) outputStream.close();
if (inputStream != null) inputStream.close();
System.out.println("Successful!");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
// 方式二 : 自动释放
import java.io.*;
public class Test {
public static void main(String[] args) {
try (
// 注意: 小括号这里面只能放置资源对象(流对象)
// 1. 创建一个字节输入流管道与源文件接通
InputStream inputStream = new FileInputStream("E:/Desktop/photo.jpg");
// 2. 创建一个字节输出流管道与目标文件接通
OutputStream outputStream = new FileOutputStream("E:/Desktop/aaa/photo.jpg");
) {
// System.out.println(10 / 0); // 错误代码(测试用)
// 3. 创建一个字节数组,负责转移字节数据
byte[] buffer = new byte[1024]; // 1KB
// 4. 从字节输入流中读取字节数据,写出去到字节输出流中,读多少写多少
int len; // 记住每次读取了多少个字节
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
System.out.println("Successful!");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}