文章目录
IO流
IO流概述和分类
IO流概述
IO:输入/输出(Input/Output)
流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输,IO流就是用来处理设备间数据传输问题的,常见的应用有:文件复制;文件上传;文件下载。
IO流分类
按照数据的流向: 输入对应的是读数据,输出对应的是写数据
按照数据类型: 字节流(字节输入流,字节输出流),字符流(字符输入流,字符输出流),能读的懂的(例如用记事本打开一个txt文档)称为字符流,不能读懂的(例如用记事本打开一张图片,看到的是一堆乱码)称为字节流(如果不知道用字节流还是用字符流,默认 使用字节流)。
字节流
抽象父类:
字节输入流(InputStream)
字节输出流(OutputStream)
字节流写数据:
FileOutputStream: 文件输出流用于将数据写入File
步骤:一,创建字节流输出流对象,二,调用字节输出流对象的写数据方法,三,释放资源。
public static void main(String[] args) throws IOException {
//一,创建字节流输出对象 FileOutputStream(String name): 创建文件输出流以指定的名称写入文件
//1.调用系统功能创建了文件
//2.创建了字节输出流对象
//3.让字节输出流对象指向创建好的文件
FileOutputStream fos = new FileOutputStream("fos.txt");
//二,void write(int b):将指定的字节写入此文件输出流
fos.write(97);
//三,释放资源
//void close(): 关闭此文件输出流并释放与此相关联的任何系统资源
fos.close();
}
字节流写数据的3种方式
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流一次写一个字节数据 |
void write(byte[] b) | 将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据 |
void write(byte[]b,int off,int len) | 将len字节从指定的字节数组开始,从偏移量(索引)off开始写入此文件输出流一次写一个字节数据的部分数据 |
public static void main(String[] args) throws IOException {
//构造方法1:FileOutputStream(String name): 创建文件输出流以指定的名称写入文件
FileOutputStream fos=new FileOutputStream("fos.txt");
//构造方法2:FileOutputStream(File file):创建文件输出流以写入由指定的File对象表示的文件
File file=new File("fos.txt");
FileOutputStream fos2=new FileOutputStream(file);
//写数据的三种方式:
//方式1:void write(int b):将指定的字节写入此文件输出流
fos.write(97);
fos.write(98);
fos.write(99);
fos.write(100);
fos.write(101);
//方式2:void write(byte[] b):将b.length字节从指定的字节数据写入此文件输出流
byte[] bys={97,98,99,100,101};
fos.write(bys);
//方式2的简化写法(用到String类的getBytes();返回字符串对应的字节数组方法)
String str="abcde";
byte[] bytes = str.getBytes();
fos.write(bytes);
//方式3:void write(byte[]b,int off,int len):将len字节从指定的字节数组开始,从偏移量off处开始写数据
String str2="abcde";
byte[] bytes1 = str2.getBytes();
fos.write(bytes1,0,bys.length);
}
字节流写数据的两个小问题
- 字节流写数据如何实现换行?
public static void main(String[] args) throws IOException {
//问题1:字节流写数据如何实现换行呢?
//创建字节流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
//写数据
for (int i = 0; i < 100; i++) {
fos.write("hello".getBytes());
//换行
fos.write("\n".getBytes());
}
//释放资源
fos.close();
}
- 字节流写数据如何实现追加写入呢?
public static void main(String[] args) throws IOException {
//问题2:如何追加写入?true追加数据
FileOutputStream fos1 = new FileOutputStream("fos.txt", true);
//写数据
for (int i = 0; i < 100; i++) {
fos1.write("hello".getBytes());
//换行
fos1.write("\n".getBytes());
}
//释放资源
fos1.close();
}
字节流读数据(一次读一个字节数据)
FileInputStream : 从文件系统中的文件获取输入字节
需求:把文件fos.txt中的内容读取出来在控制台输出
使用字节输入流读取数据的步骤:
- 创建字节输入流对象
- 调用字节输入流对象的读数据方法
- 释放资源
public static void main(String[] args) throws IOException {
//1.创建字节输入流对象
FileInputStream fis=new FileInputStream("fos.txt");
//2.调用字节流输入流对象的读数据方法
//int read(): 从该输入流读取一个字节的数据
int by;
while ((by=fis.read())!=-1){
System.out.println((char) by);
}
//3.释放资源
fis.close();
}
字节流读数据(一次读一个字节数组数据)
需求:把文件fos.txt中的内容读取出来在控制台输出
使用字节输入流读数据的步骤:
- 创建字节输入流对象
- 调用字节输入流对象的读数据方法
- 释放资源
public static void main(String[] args) throws IOException {
//1.创建字节输入流对象
FileInputStream fis=new FileInputStream("fos.txt");
//2.调用字节输入流对象的读数据方法
//int read(byte[] b):从该输入流读取最多 b.length个字节的数据到一个字节数组
byte []bys=new byte[1024];//1024及其整数倍
int len;
while ((len=fis.read(bys))!=-1){
String s = new String(bys, 0, len);
System.out.println(s);
}
//3.释放资源
fis.close();
}
字节缓冲流
BufferedOutputStream:该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。
构造方法:
- 字节缓冲输出流:BufferedOutputStream(OutputStream out)
- 字节缓冲输入流:BufferedInputStream(InputStream in)
为什么构造方法需要的是字节流,而不是具体的文件或路径呢?
字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作
public static void main(String[] args) throws IOException {
//字节缓冲输出流:BufferedOutputStream(OutputStream out)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
//写数据
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
//释放资源
bos.close();
}
public static void main(String[] args) throws IOException {
//字节缓冲输入流:BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bos.txt"));
//一次读取一个字节数据
//int by;
//while ((by=bis.read())!=-1){
// System.out.println((char)by);
//}
//一次读取一个字节数组数据
byte[] bys = new byte[1024];
int len;
while ((len = bis.read(bys)) != -1) {
System.out.println(new String(bys, 0, len));
}
//释放资源
bis.close();
}
字符流
为什么会出现字符流?
由于字节流操作中文不是特别方便,所以Java就提供字符流
字符流=字节流+编码表
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因时最终底层操作会自动进行字节拼接成中文,如何识别时中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
编码表
计算机中存储的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析出来,称为解码。这里强调一下:按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象。
字符集
是一个系统支持的所有字符的集合,包括各个国家文字、标点符号、图形符号、数字等。计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集。
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合包括7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名等都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码、这就是常说的“全角”字符,而原来在127号以下的那些就叫“半角”字符了。
GBK:最常用的中文码表。是在GB2312标准的基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
Unicode字符集:为了表示任意语言的任意字符而设计,是业界的一种标准,也称为统一码,标准万国码。它最多使用4个字节的数字来表示每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的是UTF-8编码。
UTF-8编码可以用来表示Unicode标准中的任意字符,它是电子邮件、网页及其其他存储或传送文字应用中,优先采用的编码。互联网工程小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
- 128个US-ASCII字符,只需要一个字节编码
- 拉丁文等字符,需要二个字节编码
- 大部分常用字(含中文),使用三个字节编码
- 其他极少数使用的Unicode辅助字符,使用四个字节编码
小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码
字符串中的编码解码问题
编码:
- byte[] getBytes(); 使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
- byte[] getBytes(String charsetName); 使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码:
- String(byte[] bytes); 通过使用平台的默认字符集解码指定的字节数组来构造新的String
- String(byte[] bytes,String charsetName); 通过指定的字符集解码指定的字节数组来构造新的String
使用默认编码:
public static void main(String[] args) {
//编码
//1.先定义一个字符串
String s="中国";
//2.编码 使用平台默认的字符集编码
byte[] bytes = s.getBytes();
System.out.println(Arrays.toString(bytes));
}
结果如下:
[-28, -72, -83, -27, -101, -67]
使用指定编码:
public static void main(String[] args) throws UnsupportedEncodingException {
//编码
//1.先定义一个字符串
String s="中国";
//2.编码 使用指定编码 如GBK
byte[] bytes = s.getBytes("GBK");
System.out.println(Arrays.toString(bytes));
}
打印结果如下:
[-42, -48, -71, -6]
使用默认解码:
public static void main(String[] args) throws UnsupportedEncodingException {
//解码
//1.先定义一个字符串
String s="中国";
byte[] bytes = s.getBytes();
//2.使用默认解码
String str=new String(bytes);
System.out.println(str);
}
打印结果如下:
中国
使用指定字符集解码:
public static void main(String[] args) throws UnsupportedEncodingException {
//解码
//1.先定义一个字符串
String s="中国";
//使用GBK字符集编码
byte[] bytes = s.getBytes("GBK");
//2.使用GBK字符集解码
String str=new String(bytes,"GBK");
System.out.println(str);
}
打印结果如下:
中国
字符流中的编码解码问题
字符流抽象基类
Reader:字符输入流的抽象类
Writer:字符输出流的抽象类
字符流中和编码解码问题相关的两个类:
InputStreamReader: 是从字节流到字符流的桥梁;他读取字节,并使用指定的charset将其解码为字符。它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
OutputStreamWriter: 是从字符流到字节流的桥梁;使用指定的charset将写入的字符编码为字节。它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
OutputStreamWriter
构造方法:
- OutputStreamWriter(OutputStream out):创建一个使用默认字符编码的OutputStreamWriter
- OutputStreamWriter(OutputStream out,String charsetName):创建一个使用命名字符集的OutputStreamWriter
使用OutputStreamWriter(OutputStream out)构造方法写数据:
public static void main(String[] args) throws IOException {
//1.创建字符流输出对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
//2.写数据
osw.write("中国");
//3.关闭
osw.close();
}
使用OutputStreamWriter(OutputStream out,String charsetName)构造方法写数据:
public static void main(String[] args) throws IOException {
//1.创建字符流输出对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"),"GBK");
//2.写数据
osw.write("中国");
//3.关闭
osw.close();
}
InputStreamReader:
- InputStreamReader(InputStream in); 创建一个使用默认字符集的InputStreamReader
- InputStreamReader(InputStream in,String charsetName); 创建一个使用命名字符集的InputStreamReager
使用InputStreamReader(InputStream in,String charsetName); 构造方法
public static void main(String[] args) throws IOException {
//1.创建字符流输出对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"),"GBK");
//2.写数据
osw.write("中国");
//3.关闭
osw.close();
InputStreamReader isr=new InputStreamReader(new FileInputStream("osw.txt"),"GBK");
//一次读取一个字符数据
int ch;
while ((ch=isr.read())!=-1){
System.out.println((char)ch);
}
isr.close();
}
字符流写数据的5种方式
方法名 | 说明 |
---|---|
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) | 写一个字符串的一部分 |
- 写入一个字符
public static void main(String[] args) throws IOException {
//1.创建一个默认字符编码的OutputStreamWriter对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
//2.写一个字符
osw.write(97);
//3.刷新流(不刷新时,写入的数据还存在于缓冲区中,刷新后才写入文件)
osw.flush();
//4.释放资源(关闭流,会先刷新)
osw.close();
}
- 写入一个字符数组
//1.创建一个默认字符编码的OutputStreamWriter对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
//2.写入一个字符数组的数据
char [] chs={'a','b','c','d','e'};
//3.写入
osw.write(chs);
//4.关闭资源
osw.close();
- 写入部分的字符数组
//1.创建一个默认字符编码的OutputStreamWriter对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
//2.写入一个字符数组的数据
char [] chs={'a','b','c','d','e'};
//3.写入一部分(从索引1往后写3个)
osw.write(chs,1,3);
//4.关闭资源
osw.close();
- 写一个字符串
//1.创建一个默认字符编码的OutputStreamWriter对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
//3.写入一个字符串
osw.write("abcde");
//4.关闭资源
osw.close();
- 写入字符串的一部分
//1.创建一个默认字符编码的OutputStreamWriter对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
//2.写入一个字符串(从索引1往后写3个)
osw.write("abcde",0,3);
//3.释放资源
osw.close();
字符流读数据的2种方式
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据 |
int read(char [] cbuf) | 一次读一个字符数组数据 |
一次读一个字符数据:
//1.创建一个默认字符集对象
InputStreamReader isr=new InputStreamReader(new FileInputStream("osw.txt"));
//2.读数据
int ch;
while ((ch=isr.read())!=-1){
System.out.println((char)ch);
}
//3.释放资源
isr.close();
一次读一个字符数组数据:
//1.创建一个默认字符集对象
InputStreamReader isr=new InputStreamReader(new FileInputStream("osw.txt"));
//2.读数据
char[] chs=new char[1024];
int len;
while ((len=isr.read(chs))!=-1){
System.out.println(new String(chs,0,len));
}
//3.释放资源
isr.close();
File类
File类概述和构造方法
File:它是文件和目录路径名的抽象表示,文件和目录可以通过File封装成对象,对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的。
方法名 | 说明 |
---|---|
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的File实例 |
File(String parent,String child) | 从父路径名字符串和子路径字符串创建新的File实例 |
File(String parent,String child) | 从父抽象路径和子路径名字符串创建新的File实例 |
public static void main(String[] args) {
//1.通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
File f1=new File("E:\\test\\java.txt");
System.out.println(f1);
//2.从父路径名字符串和子路径字符串创建新的File实例
File f2=new File("E:\\test","java.txt");
System.out.println(f2);
//3.从父抽象路径和子路径名字符串创建新的File实例
File f3=new File("E:\\test");
File f4=new File(f3,"java.txt");
System.out.println(f4);
}
File类创建功能
方法名 | 说明 |
---|---|
public boolean createNewFile() | 当具有该文件不存在时,创建一个由该抽象路径名命名的新文件 |
public boolean mkdir() | 创建由此抽象路径命名的目录 |
public boolean mkdirs() | 创建由此抽象路径命名的目录,包括任何必须但不存在的父目录 |
public static void main(String[] args) throws IOException {
//1.在E:\\test目录下创建一个文件java.txt
File f1=new File("E:\\test\\java.txt");
//如果文件不存在,就创建文件,并返回true,如果文件存在,就不创建文件,返回false
boolean newFile = f1.createNewFile();
System.out.println(newFile);
//2.在E:\\test目录下创建一个目录JavaSE
File f2=new File("E:\\test\\JavaSE");
//如果目录不存在,就创建目录,并返回true,如果目录存在,就不创建目录,并返回false
boolean mkdir = f2.mkdir();
System.out.println(mkdir);
//3.在E:\\test目录下创建一个多级目录JavaWEB\\HTML
File f3=new File("E:\\test\\JavaWEB\\HTML");
//如果目录不存在,就创建多级目录,并返回true,如果该多级目录存在,就不创建多级目录,并返回false
boolean mkdirs = f3.mkdirs();
System.out.println(mkdirs);
//4.在E:\\test目录下创建一个文件java.txt
File f4=new File("E:\\test\\javase.txt");
//如果该文件存在,就创建文件,并返回true,如果该文件不存在,就不创建文件,并返回false
boolean newFile1 = f4.createNewFile();
System.out.println(newFile1);
}
File类判断和获取功能
方法名 | 说明 |
---|---|
public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public boolean String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或目录的名称 |
public String[] list() | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 |
public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
public static void main(String[] args) {
//创建一个File对象
File file=new File("test\\java.txt");
//测试此路径抽象路径名表示的File是否为目录
boolean directory = file.isDirectory();
System.out.println(directory);
//测试此抽象的路径名表示的File是否为文件
boolean file1 = file.isFile();
System.out.println(file1);
//测试此抽象路径名表示的File是否存在
boolean exists = file.exists();
System.out.println(exists);
//返回此抽象路径名的绝对路径名字符串
String absolutePath = file.getAbsolutePath();
System.out.println(absolutePath);
//将此抽象路径名转换为路径名字符串
String path = file.getPath();
System.out.println(path);
//返回此抽象路径名表示的文件或目录的名称
String name = file.getName();
System.out.println(name);
File file2=new File("E:\\test");
//返回此抽象路径名表示的目录中的文件和目录名称字符串
String[] list = file2.list();
for(String str:list){
System.out.println(str);
}
//返回此抽象路径名表示的目录中的文件和目录的File对象数组
File[] files = file2.listFiles();
for(File file3:files){
System.out.println(file3);
}
}
File类删除功能
方法名 | 说明 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的文件或目录 |
删除目录时的注意事项:如果一个目录中有内容(目录、文件),不能直接删除。应该先删除目录中的内容,最后才能删除目录