IO流 字符流
字符流
编码
默认编码写入和读取
通过以下程序读取带有中文的文件
示例代码
public class Test01 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
// writeText();
readText();//显示乱码
}
public static void writeText() throws IOException{
FileOutputStream fos = new FileOutputStream("001.txt");
fos.write("你好啊!!!".getBytes(""));
fos.close();
}
public static void readText() throws IOException{
FileInputStream fis = new FileInputStream("001.txt");
int ch = 0;
while((ch=fis.read())!=-1){
System.out.println((char)ch);
}
}
}
上面程序在读取含有中文的文件时,我们并没有看到具体的中文,而是看到一些数字,这是什么原因呢?
既然看不到中文,那么我们如何对其中的中文做处理呢?要解决这个问题,我们必须研究下字符的编码过程。
指定编码写入和独处
用指定的编码进行读取数据,是可以正确显示内容的。
示例代码
public class Test02 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileOutputStream fos = new FileOutputStream("002.txt");
byte[] b = "你好啊!!!".getBytes("utf-8");
System.out.println(b.length);
System.out.println(Arrays.toString(b));
fos.write(b);
fos.close();
System.out.println("写入完毕!!!");
FileInputStream fis = new FileInputStream("002.txt");
int ch = 0;
byte[] byteInfo = new byte[3];
StringBuffer sb = new StringBuffer();
while((ch=fis.read(byteInfo))!=-1){
String str = new String(byteInfo, "utf-8");
sb.append(str);
}
System.out.println(sb);
fis.close();
}
}
上面的例子,写入和读取均正常,这是为什么呢?
Java 的IO流中按字节分就有字节流和字符流之分,字节流是按字节为单位来操作的,而字符流当然是按字符为单位来操作的
字节流一般是用来操作一些二进制文件,例如 mp3 文件,JPG 等等。
字符流一般是用来操作一些文本文件。
编码:将源对象内容按照一种标准转换为一种标准格式内容。
String –> getBytes()//使用平台默认的字符集
getBytes(String charset)//指定编码方式
解码:使用和编码相同的标准将编码内容还原为最初的对象内容。
String –> 构造方法:String(byte[] b) : 使用平台默认字符集编码
String(byte[] b,String charset):指定解码方式
对于文本文件,写入和读取使用相同的”编码方式”,就会避免乱码的情况
字符编码表
编码表:其实就是生活中字符和计算机二进制的对应关系表。
- ASCII:一个字节中的 7 位就可以表示。对应的字节都是正数。0-xxxxxxx
- iso-8859-1:拉丁码表 latin,用了一个字节用的 8 位。1-xxxxxxx 负数。
- GB2312:简体中文码表。包含 6000-7000 中文和符号。用两个字节表示。两个字节第一个字节是负数,第二个字节可能是正数
- GBK:目前最常用的中文码表,2 万的中文和符号。用两个字节表示,其中的一部分文字, 第一个字节开头是 1,第二字节开头是 0
- GB18030:最新的中文码表,目前还没有正式使用。
- unicode:国际标准码表:无论是什么文字,都用两个字节存储。
- Java 中的 char 类型用的就是这个码表。charc=’a’;占两个字节。
- Java 中的字符串是按照系统默认码表来解析的。简体中文版默认的码表是 GBK。
- UTF-8:基于 unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表 更加的标准化,在每一个字节头加入了编码信息(后期到 api 中查找)。
能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题。
对于我们开发而言,常见的编码为:GBK、UTF-8、ISO-8859-1
文字 —> (数组):“abc”.getBytes()
(数组)—> 文字:解码。 byte[]b={97,98,99} newString(b)
字符输入流Reader
字符流概述
对文本内容进行读取,如果想从文件中直接读取字符便可以使用字符输入流 FileReader, 通过此流可以从关联的文件中读取一个或一组字符。
什么是文本文件
计算机的文件场被分为文本文件和二进制文件两大类
可以认为:所有可以用记事本打开并且可以看到懂字符内容的文件称为文本文 件,反之则是二进制文件。
计算机中所有的文件都是二进制文件,文本文件只是一种特殊的存在,如果二 进制文件的内容恰好能被正常解析成字符时,则改二进制文件就可以称为文本 文件。
在有些情况下,文本文件使用了错误的字符集打开,会生成乱码,所以如果想 正常查看文本文件,必须在打开文件时使用与保存文件时相同的字符集。
上述程序中我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个 一个字节。只要把这些字节去查阅对应的编码表,就能够得到与之对应的字符。API 中是否 给我们已经提供了读取相应字符的功能流对象,Reader,读取字符流的抽象超类。
方法摘要
int read()
读取单个字符。
int read(char[] cbuf)
将字符读入数组。
abstract int read(char[] cbuf, int off, int len)
将字符读入数组的某一部分。
read():读取单个字符并返回
read(char[]):将数据读取到数组中,并返回读取的个数。
FileReader类
构造方法摘要
FileReader(File file)
在给定从中读取数据的 File 的情况下创建一个新 FileReader。
FileReader(FileDescriptor fd)
在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。
FileReader(String fileName)
在给定从中读取数据的文件名的情况下创建一个新 FileReader。
需要注意的是:FileReader 类没有特有的方法,所有方法均继承自父类
FileReader 读取包含中文的文件
示例代码
public class Test03 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileReader fr = new FileReader("new4_12.txt");
int ch =0 ;
// while((ch = fr.read())!=-1){
// System.out.print((char)ch);
// }
char[] c = new char[100];
// System.out.println(c);
while((ch = fr.read(c))!=-1){
System.out.println(c);
}
fr.close();
}
}
字符输出流Writer
既然有专门用于读取字符的流对象,那么肯定也有写的字符流对象,Writer 类,Writer 是写入字符流的抽象类。其中描述了相应的写的动作。
构造方法摘要
protected Writer()
创建一个新的字符流 writer,其关键部分将同步 writer 自身。
protected Writer(Object lock)
创建一个新的字符流 writer,其关键部分将同步给定的对象。
方法
方法摘要
Writer append(char c)
将指定字符添加到此 writer。
Writer append(CharSequence csq)
将指定字符序列添加到此 writer。
Writer append(CharSequence csq, int start, int end)
将指定字符序列的子序列添加到此 writer.Appendable。
abstract void close()
关闭此流,但要先刷新它。
abstract void flush()
刷新该流的缓冲。
void write(char[] cbuf)
写入字符数组。
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String str, int off, int len)
写入字符串的某一部分。
FileWriter类
FileOutputStream 用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑 使用 FileWriter。
构造方法摘要
FileWriter(File file)
根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(File file, boolean append)
根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(FileDescriptor fd)
构造与某个文件描述符相关联的 FileWriter 对象。
FileWriter(String fileName)
根据给定的文件名构造一个 FileWriter 对象。
FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
所有方法均继承父类
FileWriter 写入中文到文件中
写入字符到文件中,先进行流的刷新,再进行流的关闭。
示例代码
public class Test04 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileWriter fw = new FileWriter("002.txt");
fw.write("我爱你\r\n");
fw.flush();
fw.write("是真的");
fw.flush();
fw.close();
}
}
flush()和 close()的区别?
flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。
close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据, 然后在关闭流。流不可以使用。如果写入数据多,一定要一边写一边刷新,最后一次可 以不刷新,由 close 完成刷新并关闭。
字符流练习
示例代码
/**
* 复制文本文件
*/
public class Test05 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileReader fr = new FileReader("new4_12.txt");
FileWriter fw = new FileWriter("newnew4_12.txt");
int b = 0;
char[] ch = new char[10];
while((b=fr.read(ch))!=-1){
fw.write(ch, 0, b);
}
fr.close();
fw.close();
}
}
//复制成功
转换流
OutputStreamWriter
OutputStreamWriter 是字符流通向字节流的桥梁,可使用指定的字符编码表,将要写入流中 的字符编码成字节。
它的作用的就是,将字符串按照指定的编码表转成字节,再使用字节流将这些字节写出去。
示例代码
public class Test07 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//OutputStreamWriter 是字符流通向字节流的桥梁,可使用指定的字符编码表,将要写入流中 的字符编码成字节。
FileOutputStream fos = new FileOutputStream("4_12.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
osw.write("你好,你真的好");
osw.close();
}
}
InputStreamReader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的字符编码表读取字节并将其 解码为字符。
可以通过构造方法指定字符集
示例代码
public class Test08 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//InputStreamReader
InputStreamReader isr = new InputStreamReader(new FileInputStream("003.txt"));
int ch = 0;
while((ch = isr.read()) != -1){
System.out.print((char) ch);
}
}
isr.close;
}
缓冲流
在使用流读取数据量大的文件时,读取的速度会很慢,影响我们程序的效率,那么想提高速 度,怎么办?
Java 中提高了一套缓冲流,可以提高 IO 流的读写速度
缓冲流,根据流的分类分类字节缓冲流与字符缓冲流。
字符缓冲流
字节缓冲流根据流的方向,有 2 个:
- 写入数据到流中,字节缓冲输出流 BufferedOutputStream
- 读取流中的数据,字节缓冲输入流 BufferedInputStream
它们的内部都包含了一个缓冲区,通过缓冲区读写,就可以提高了 IO 流的读写速
字节缓冲输出流 BufferedOutputStream
通过字节缓冲流,进行文件的读写操作,写数据到文件中
构造方法摘要
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size)
创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
示例代码
public class Test10 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//BufferedOutputStream
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("002.txt"));
bos.write("真的爱你".getBytes());
bos.close();
}
}
字节缓冲输入流 BufferedInputStream
构造方法摘要
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
示例代码
public class Test09 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//写入数据到流中,字节缓冲输出流 BufferedOutputStream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("003.txt"));
int ch = 0;
while((ch= bis.read())!=-1){
System.out.print((char)ch);
}
}
}
字符缓冲流
- 字符缓冲输入流 BufferedReader
- 字符缓冲输出流 BufferedWriter
完成文本数据的高效的写入与读取的操作
字符缓冲输出流 BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
- 新增的方法
void flush() 刷新该流的缓冲
void newLine() 写入一个行分隔符
示例代码
public class Test12 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedWriter bw = new BufferedWriter(new FileWriter("002.txt",true));
bw.write("什么玩意");
bw.close();
}
}
字符缓冲输入流 BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
String readLine() 读取一个文本行
示例代码
public class Test11 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedReader br = new BufferedReader(new FileReader("003.txt"));
int ch = 0;
while((ch=br.read())!=-1){
System.out.print((char)ch);
}
br.close();
}
}
其他流
对象序列化流
序列化:是指将一个”对象(包含属性值)”存储到一个文件中,或者通过网络进行传输
类介绍:ObjectOutputStream
构造方法 ObjectOutputStream(OutputStreamout)
序列化的方法
voidwriteObject(Objectobj) :将指定的对象写入 ObjectOutputStream
注意:当某个对象需要被”序列化”时,此类必须实现:Serializable(接口)
在 Serializable 接口中,没有任何方法,这种接口叫:标记接口;它类似于一个标记,某个 类如果实现了这样的接口,表示这个类就具有了某种权限
反序列化:是指将一个文本文件转成一个 Java 对象
类介绍:ObjectInputStream
构造方法
ObjectInputStream(InputStreamin)
反序列化的方法
ObjectreadObject() :从 ObjectInputStream 读取对象。关于”序列号”: 当一个类实现了 Serializable 后,系统会自动为这个类添加一个属性–它表示”序列号”的 信息
这个”序列号”会被写入到文件中;
当我们修改这个类的结构时,系统会自动更改此”序列号”;所以这就导致了”类中的序 列号”和”文件中的序列号”不匹配的问题;
建议:每个需要被”序列化”的类,都要显示的指定”序列号”,并且由程序员自己维护序 列号的信息
使用 transient 关键字,声明不需要被序列化的成员变量
示例代码
public class Test13 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
Student stu = new Student("张三", 18);
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("002.txt"));
os.writeObject(stu);
os.flush();
os.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("002.txt"));
Student stu1 = (Student)ois.readObject();
System.out.println(stu1);
ois.close();
}
}
class Student implements Serializable {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name + age;
}
}
打印流
打印流是输出信息最方便的类,主要包含字节打印流(PrintStream) 和字符打印流 (PrintWriter). 打印流提供了非常方便的打印功能,可以打印任何的数据类型,例如: 小 数、整数、字符串等等。
示例代码
public class Test14 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
PrintWriter pw = new PrintWriter("003.txt");
pw.print("2435u");
pw.close();
}
}
流的使用
IO 流中对象很多,解决问题(处理设备上的数据时)到底该用哪个对象呢?
第一步:明确操作的数据类型,是文本还是其它二进制文件?
文本:Reader、Writer
其它二进制:InputStream、OutputStream
第二步:先明确要操作类型输入还是输出
源:InputStream Reader
目的:OutputStream Writer
第三步:明确是否需要额外的功能 是否需要高效率?
BufferedInputStream
BufferedOuputStream
BufferedWriter
BufferedReader