话接上回,继续java IO部分的学习。上一次说完了字节流的读写数据,这次介绍一下字符流的读写数据。
一、字符流及其读/写数据
1、字符流
1.1 概述
1)背景
由于字节流操作中文不是特别方便,所以java就提供了字符流。
字符流=字节流+编码表(即字符流的底层还是字节流)
2)问题:用字节流复制文本文件,文本文件中也有中文,但是不会出现编码问题的原因?如何识别是中文?
最终底层操作会自动进行字节拼接成中文。
识别中文:汉字在存储时无论选择哪种编码存储,第一个字节都是负数。
3)一个汉字存储(不同编码占用字节数不同)
1 2 3 4 5 6 7
| - 采用GBK编码,占用2个字节 - UTF-8编码,占用3个字节
注:getBytes()方法:得到字符对应的字节数组,如: String s="abc"; byte[] bys=s.getBytes(); System.out.println(Arrays.toString(bys));
|
1.2 字符编码(简单了解)
1)概述
1 2 3 4
| ISO8859-1:属于单字节编码,最多只能表示 0~255 的字符范围。 GBK/GB2312:中文的国标编码,用来表示汉字,属于双字节编码。GBK 可以表示简体中文和繁体中文,而 GB2312 只能表示简体中文(GBK 兼容 GB2312) Unicode:是一种编码规范,是为解决全球字符通用编码而设计的。UTF-8 和 UTF-16 是这种规范的一种实现,该编码不兼容 ISO8859-1 编码。Java 内部采用此编码。 UTF:UTF 编码兼容了 ISO8859-1 编码,同时也可以用来表示所有的语言字符,但 UTF 编码是不定长编码,每一个字符的长度为 1~6 个字节不等(一般在中文网页中使用此编码,可以节省空间)
|
2)字符串中的编码解码
注:==按哪种编码存储(编码),就必须按该种编码解析(解码),否则会乱码==
编码(按某种规则,将字符存储到计算机中)
1 2
| byte[] getBytes():使用平台默认字符集将该String编码为一系列字节,并将结果存储到新的字节数组中。 byte[] getBytes(String charsetName):通过指定的字符集将该String编码为一系列字节,并将结果存储到新的字节数组中
|
解码(将储存在计算机中的二进制数按照某种规则解析显示)
1 2
| String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String String(bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
|
3)字符流中的编码解码
字符流抽象基类(父类)
1 2
| Reader:字符输入流的抽象类 Writer:字符输出流的抽象类
|
字符流中与编码解码相关的两个类
转换流:将字节流转换为字符流
1 2 3 4 5 6 7
| InputStreamReader(常用构造方法:2个) - InputStreamReader(InputStream in) - InputStreamReader(InputStream in,String charsetName)
OutputStreamWriter(常用构造方法:2个) - OutputStreamWriter(OutputStream out) - OutputStreamWriter(OutputStream out,String charsetName)
|
示例1(字符串中的编码解码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import java.io.UnsupportedEncodingException; import java.util.Arrays;
public class StringDemo { public static void main(String[] args) throws UnsupportedEncodingException { String s="中国";
byte[] bys = s.getBytes(); System.out.println(Arrays.toString(bys)); byte[] bys1 = s.getBytes("UTF-8"); System.out.println(Arrays.toString(bys1)); byte[] bys2 = s.getBytes("GBK"); System.out.println(Arrays.toString(bys2));
String ss= new String(bys); String ss1=new String(bys,"UTF-8"); String ss2=new String(bys,"GBK"); String ss3=new String(bys2,"GBK"); System.out.println(ss); System.out.println(ss1); System.out.println(ss2); System.out.println(ss3); } }
|
运行结果
1 2 3 4 5 6 7
| [-28, -72, -83, -27, -101, -67] [-28, -72, -83, -27, -101, -67] [-42, -48, -71, -6] 中国 中国 涓 浗 中国
|
示例2(字符流中的编码解码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import java.io.*;
public class ConversionStreamDemo { public static void main(String[] args) throws IOException {
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\osw.txt"),"GBK"); osw.write("中国"); osw.close();
InputStreamReader isr=new InputStreamReader(new FileInputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\osw.txt"),"GBK"); int ch; while ((ch= isr.read())!=-1){ System.out.print((char) ch); } isr.close(); } }
|
运行结果
1.3 字符流读数据
层级关系(父–>子):Reader(抽象类)—->InputStreamReader—->FileReader(都在java.io包下)
1)Reader类常用子类
1 2 3 4 5 6 7
| Reader 类是所有字符流输入类的父类,常用子类:(参考JDK帮助文档) - CharArrayReader 类:将字符数组转换为字符输入流,从中读取字符。 - StringReader 类:将字符串转换为字符输入流,从中读取字符。 - BufferedReader 类:为其他字符输入流提供读缓冲区。 - PipedReader 类:连接到一个 PipedWriter。 - InputStreamReader 类:将字节输入流转换为字符输入流,可以指定字符编码。 - 与 InputStream 类相同,在 Reader 类中也包含 close()、mark()、skip() 和 reset() 等方法,可参考 InputStream 类的方法
|
2)Reader类中的read()方法(重载–3个)
1 2 3 4
| int read() 从输入流中读取一个字符,并把它转换为 0~65535 的整数。如果返回 -1, 则表示已经到了输入流的末尾。 为了提高 I/O 操作的效率,通常使用以下两种 read()方法 int read(char[] cbuf) 从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。 该方法返回读取的字符数,如果返回 -1,则表示已经到了输入流的末尾 int read(char[] cbuf,int off,int len) 从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。其中,off 指定在字符数组中开始保存数据的起始下标,len 指定读取的字符数。该方法返回实际读取的字符数,如果返回 -1,则表示已经到了输入流的末尾
|
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader;
public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { InputStreamReader isr=new InputStreamReader(new FileInputStream("D:\\Ultimate JavaCode\\src\\test6\\文本复制案例.txt"));
char[] chs=new char[1024]; int len; while ((len=isr.read(chs))!=-1){ System.out.print(new String(chs,0,len)); } isr.close(); } }
|
3)字符文件输入流
FileReader类(构造方法–2个重载)
1 2 3 4
| FileReader(File file):在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中,file 表示要从中读取数据的文件。 FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中,fileName 表示要从中读取数据的文件的名称,表示的是一个文件的完整路径。
注:在创建 FileReader 对象时若引发 FileNotFoundException 异常,需要使用 try catch 语句捕获该异常。
|
示例(使用字符流复制java文件)
用转换流InputStreamReader和OutputStreamWriter实现字符流复制java文件
转换流作用:将字节流转换为字符流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.io.*;
public class FileReaderDemo { public static void main(String[] args) throws IOException { InputStreamReader isr=new InputStreamReader(new FileInputStream("D:\\Ultimate JavaCode\\ConversionStreamDemo.java")); OutputStreamWriter isw=new OutputStreamWriter(new FileOutputStream("D:\\Ultimate JavaCode\\Copy.java"));
char[] chs=new char[1024]; int len; while((len=isr.read(chs))!=-1){ isw.write(chs,0,len); } isw.close(); isr.close(); } }
|
由于转换流创建对象代码较冗长,可以使用其子类FileReader和FileWriter实现
但若要想解决编码问题,一定要用转换流,因为其在创建时可以指定编码类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.io.FileReader; import java.io.FileWriter; import java.io.IOException;
public class FileReaderDemo1 { public static void main(String[] args) throws IOException { FileReader fr=new FileReader("D:\\Ultimate JavaCode\\ConversionStreamDemo.java"); FileWriter fw=new FileWriter("D:\\Ultimate JavaCode\\Copy1.java");
char[] chs=new char[1024]; int len; while((len=fr.read(chs))!=-1){ fw.write(chs,0,len); } fw.close(); fr.close(); } }
|
4)字符缓冲区输入流
BufferedReader 类
主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换,可提高数据的读取效率。
构造方法(重载–2个)
1 2
| BufferedReader(Reader in):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流 BufferedReader(Reader in,int size):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流,参数 size 则用于指定缓冲区的大小,单位为字符。
|
示例(字符缓冲流)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.io.*;
public class BufferedStreamDemo { public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("D:\\Ultimate JavaCode\\src\\test6\\bw.txt"));
char[] chs=new char[1024]; int len; while((len=br.read(chs))!=-1){ System.out.println(new String(chs,0,len)); } br.close(); } }
|
运行结果
1.4 字符流写数据
层级关系(父–>子):Writer(抽象类)—->OutputStreamWriter—->FileWriter(都在java.io包下)
1)Writer类常用子类
1 2 3 4 5 6 7
| Writer 类是所有字符输出流的父类,常用子类: - CharArrayWriter 类:向内存缓冲区的字符数组写数据。 - StringWriter 类:向内存缓冲区的字符串(StringBuffer)写数据。 - BufferedWriter 类:为其他字符输出流提供写缓冲区。 - PipedWriter 类:连接到一个 PipedReader。 - OutputStreamWriter 类:将字节输出流转换为字符输出流,可以指定字符编码。 - 与 OutputStream 类相同,Writer 类也包含 close()、flush() 等方法,可参考 OutputStream 类的方法
|
2)FileWriter类(用于写入字符文件,重载–4个)
1 2 3 4
| FileWriter(File file):在指定 File 对象的情况下构造一个 FileWriter 对象。 FileWriter(File file,boolean append):在指定 File 对象的情况下构造一个 FileWriter 对象,如果 append 的值为 true,则将字节写入文件末尾,而不是写入文件开始处。 FileWriter(String fileName):在指定文件名的情况下构造一个 FileWriter 对象。其中,fileName 表示要写入字符的文件名,表示的是完整路径。 FileWriter(String fileName,boolean append):在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中,append 是一个 boolean 值,如果为 true,则将数据写入文件末尾,而不是文件开始处
|
3)字符缓冲输出流
BufferedWriter 类
主要用于辅助其他字符输出流,同样带有缓冲区,可以先将一批数据写入缓冲区,当缓冲区满了后再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。
构造方法(重载–2个)
1 2 3
| BufferedWriter(Writer out):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,默认大小。 BufferedWriter(Writer out,int size):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,参数 size 则用于指定缓冲区的大小,单位为字符。 - BufferedWriter 类的使用与 FileWriter 类相同,不再重述。
|
4)字符流写数据的五种方式
1 2 3 4 5
| write(int c) 写一个字符 write(char[] cbuf) 写入一个字符数组 write(char[] cbuf,int off,int len) 写入字符数组的一部分 write(String str) 写一个字符串 write(String str,int off,int len) 写一个字符串的一部
|
注:关于flush()和 close()方法
1 2
| flush():刷新流,还能继续写数据 close():关闭流之前先刷新流,一旦关闭后不能再写数据
|
示例(字符流写数据)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter;
public class OutputStreamWriteDemo { public static void main(String[] args) throws IOException { OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("D:\\Ultimate JavaCode\\src\\test6\\osw_2_12"));
osw.write("abcdef",1,3); osw.close(); } }
|