一、为什么会出现字符流
由于字节流操作中文不是特别方便,所以java就提供了字符流
字符流=字节流+编码表
用字节流赋值文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
二、编码表
1、基础知识
计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
按照某种规则,将字符存储到计算机中,称为编码,反之,将存储在计算机中二进制数按照某种规则解析显示出来,称为解码。这里强调一下:按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象。
字符编码:就是一套自然语言的字符与二进制数之间的对应规格(A,65)
2、字符集:
是一个系统支持的所有字符的集合,包括各国文字、标点符号、图形符号、数字等
计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少由一套字符编码。
常见字符集由ASCll字符集,GBxxx字符集,Unicode字符集等
ASCll字符集
ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符
GBXXX字符集
GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换,通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。
基本集共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位。每个区位上只有一个字符,因此可用所在的区和位来对汉字进行编码,称为区位码。
把换算成十六进制的区位码加上2020H,就得到国标码。国标码加上8080H,就得到常用的计算机机内码。1995年又颁布了《汉字编码扩展规范》(GBK)。GBK与GB 2312—1980国家标准所对应的内码标准兼容,同时在字汇一级支持ISO/IEC10646—1和GB 13000—1的全部中、日、韩(CJK)汉字,共计20902字。
GBK 向下与 GB 2312 编码兼容,向上支持 ISO 10646.1国际标准,是前者向后者过渡过程中的一个承上启下的产物。ISO 10646 是国际标准化组织 ISO 公布的一个编码标准,即 Universal Multilpe-Octet Coded Character Set(简称UCS),大陆译为《通用多八位编码字符集》,台湾译为《广用多八位元编码字元集》,它与 Unicode 组织的 Unicode 编码完全兼容。ISO 10646.1 是该标准的第一部分《体系结构与基本多文种平面》。我国 1993 年以 GB 13000.1 国家标准的形式予以认可(即 GB 13000.1 等同于 ISO 10646.1)。
GBK编码,是在GB2312-80标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年10月制定, 1995年12月正式发布,中文版的WIN95、WIN98、WINDOWS NT以及WINDOWS 2000、WINDOWS XP、WIN 7等都支持GBK编码方案。
GB 18030,全称《信息技术 中文编码字符集》,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB 2312-1980完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB 18030-2005共收录汉字70,244个。
Unicode字符集
统一码,也叫万国码、单一码(Unicode)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式发布1.0版本,2020年发布13.0版本。
UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
三、字符串中的编码解码问题
1、编码
byte[ ] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[ ] getBytes(String charsetName):使用指定的字符集将String编码为一系列字符,将结果存储到新的字节数组中
2、解码
String(byte[ ] bytes):通过使用平台默认字符集解码指定的字节数组来构造新的String
String(byte[ ] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
四、字符流中的编码解码问题
1、字符流抽象基类
Reader:字符输入流的抽象类
Writer:字符输出流的抽象类
2、字符流中和编码解码问题相关的两个类
InputStreamReader
OutputStreamWriter
public static void main(String[] args) throws IOException {
// 分两步创建输出流
FileOutputStream fileOut=new FileOutputStream("D:\\demo.txt");
OutputStreamWriter out0=new OutputStreamWriter(fileOut);
//使用默认的字符编码
OutputStreamWriter out1=new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"));
out1.write("中国");
out1.close();
//使用指定的字符编码 UTF-8
OutputStreamWriter out=new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"),"UTF-8");
out.write("中国");
out.close();
//采用指定字符编码读取数据
InputStreamReader in1=new InputStreamReader(new FileInputStream("D:\\demo.txt"),"UTF-8");
//一次读取一个字节数据
int ch;
while ((ch=in1.read())!=-1){
System.out.println((char)ch);
}
in1.close();
}
五、字符流写数据的五种方式
/**
1、写一个字符
*/
public void write(int c) throws IOException {
se.write(c);
}
/**
2、写一组字符
*/
public void write(char cbuf[]) throws IOException {
write(cbuf, 0, cbuf.length);
}
/**
3、写字符数组的一部分
*/
public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}
/**
4、写一个字符串
*/
public void write(String str) throws IOException {
write(str, 0, str.length());
}
/**
5、写字符串的一部分
*/
public void write(String str, int off, int len) throws IOException {
se.write(str, off, len);
}
OutputStreamWriter out1=new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"));
//写入数据方式一
out1.write("97");
out1.flush();
out1.write("98");
out1.close();//关闭流的时候,会先自动flush
//写入数据方式二
char[] chs = {'a','b','c'};
out1.write(chs);
out1.flush();
//写入数据方式三
out1.write(chs,0,chs.length);//abc
out1.write(chs,1,2);//ab
out1.flush();
//写入数据方式四
out1.write("abcde");
out1.flush();
//写入数据方式五
out1.write("abcde",0,"abced".length());
out1.flush();
六、字符流读数据的两种方式
/**
1、一次读一个字符
*/
public int read() throws IOException {
return sd.read();
}
/**
2、一次读取一个字符组
*/
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
InputStreamReader in1=new InputStreamReader(new FileInputStream("D:\\demo.txt"),"UTF-8");
//一次读取一个字节数据
int ch;
while ((ch=in1.read())!=-1){
System.out.println((char)ch);
}
//一次读一组数据
char[] chars=new char[1024];
int len;
while((len=in1.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
in1.close();
七、FileReader和FileWriter
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流提供了对应的子类
FileReader:用于读取字符文件的便捷类
FileWriter:用于写入字符文件的便捷类
八、字符缓冲流
BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接收默认大小。默认值足够大,可用于大多数用途。
BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途。
1、构造方法
public BufferedWriter(Writer var1) {
this(var1, defaultCharBufferSize);
}
public BufferedReader(Reader var1) {
this(var1, defaultCharBufferSize);
}
2、使用
public static void main(String[] args) throws IOException {
BufferedWriter rd=new BufferedWriter(new FileWriter("D:\\demo.txt"));
rd.write("hello");
rd.write("hello");
rd.close();
BufferedReader br=new BufferedReader(new FileReader("D:\\demo.txt"));
//一次读一个字符数据
int len;
while ((len=br.read())!=-1){
System.out.println((char)len);
}
br.close();
//一次读取一组字节数据
char[] chs=new char[1024];
while ((len=br.read(chs))!=-1){
System.out.println(new String(chs,0,len));
}
}
3、字符缓冲流的特有功能
BufferedWriter
//写一行行分隔符,行分隔符字符串由系统属性定义
public void newLine() throws IOException {
this.write(this.lineSeparator);
}
BufferedReader
//读一行文字。结果包含行的内容的字符串,不包括任何终止字符,如果流的结果已经达到,则为null
public String readLine() throws IOException {
return this.readLine(false);
}
public static void main(String[] args) throws IOException {
BufferedWriter rd=new BufferedWriter(new FileWriter("D:\\demo.txt"));
rd.write("hello");
rd.newLine();//换行
rd.write("hello");
rd.newLine();//换行
rd.close();
BufferedReader br=new BufferedReader(new FileReader("D:\\demo.txt"));
//读取一行数据
String line;
while ((line=br.readLine())!=null){
System.out.println(line);//因为读取出来不包括换行符,需要手动换行
}
br.close();
}