第五章 字符流
5.1 字符编码
文件叫 d.txt
**请问,计算机底层存储的时候 是存的 a 黑马 吗? **
不是,是 a 黑马的字节形式,7 个字节?
我们如果使用字节流去读取中文文件,按照字节形式读取到的内容如下,
很多同学百思不得其解,为啥呀!!
对着呢,我用字节流读取数据,读的就是字节呀!!!!!
计算机是不认识自然界中的字符的,它只认识 01 代码也就是所谓二进制(字节),怎么办呢? 美国人那边就想了一种方法,让自然界中的字符与二进制产生对应关系,就形成了编码表。
最初的编码表只有英文字符。
编码: 通过编码表,把我们看得懂的 字符--->字节 看不懂的。 解码:通过编码表,把我们看不懂的字节 ---->字符 我们看得懂的。
package com.itheima.charactor;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo01 {
public static void main(String[] args) throws IOException {
//读取 d.txt文件
FileInputStream fis = new FileInputStream("day11\\d.txt");
//使用字节读取
int b;
while((b=fis.read())!=-1){
System.out.println((byte)b);
}
fis.close();
}
}
5.2 常见编码介绍
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照 A 规则存储,同样按照 A 规则解析,那么就能显示正确的文本 f 符号。反之,按照 A 规则存储,再按照 B 规则解析,就会导致乱码现象。
编码: 字符转换为字节。
解码:字节转换为字符。
字符编码
Character Encoding
: 就是一套自然语言的字符与二进制数之间的对应规则。生活中的文字和计算机文字的对应关系 a-->97-->0110 0001
字符集
Charset
:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有 ASCII 字符集、GBK 字符集、Unicode 字符集等。
可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。
- ASCII 字符集 :
- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
- 基本的 ASCII 字符集,使用 7 位(bits)表示一个字符,共 128 字符。ASCII 的扩展字符集使用 8 位(bits)表示一个字符,共 256 字符,方便支持欧洲常用字符。
- ISO-8859-1 字符集:
- 拉丁码表,别名 Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
- ISO-8859-1 使用单字节编码,兼容 ASCII 编码。
- GBxxx 字符集:
- GB 就是国标的意思,是为了显示中文而设计的一套字符集。
- GB2312:简体中文码表。一个小于 127 的字符的意义与原来相同。但两个大于 127 的字符连在一起时,就表示一个汉字,这样大约可以组合了包含 7000 多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在 127 号以下的那些就叫"半角"字符了。
- GBK:最常用的中文码表。是在 GB2312 标准基础上的扩展规范,使用了双字节编码方案,共收录了 21003 个汉字,完全兼容 GB2312 标准,同时支持繁体汉字以及日韩汉字等。
- 中文版操作系统使用的编码表就是 GBK。
- 中文汉字在 2312 和 GBK 编码表中均为两个字节表示,第一个字节为负数,第二个字节可能是负数也可能是正数。
- GB18030:最新的中文码表。收录汉字 70244 个,采用多字节编码,每个字可以由 1 个、2 个或 4 个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。尚未正式启用。
- Unicode 字符集 :
- Unicode 编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
- 它最多使用 4 个字节的数字来表达每个字母、符号,或者文字。有三种编码实现方案,UTF-8、UTF-16 和 UTF-32。最为常用的 UTF-8 编码。
- UTF-8 编码,可以用来表示 Unicode 标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持 UTF-8 编码。所以,我们开发 Web 应用,也要使用 UTF-8 编码。它使用一至四个字节为每个字符编码,编码规则:
- 128 个 US-ASCII 字符,只需一个字节编码。
- 拉丁文等字符,需要二个字节编码。
- 大部分常用字(含中文),使用三个字节编码。
- 其他极少使用的 Unicode 辅助字符,使用四字节编码。
- UTF-8 是变长编码表,汉字在 UTF-8 中均为负数
5.3 字节流读取数据和字符流读取数据对比
字节流读取数据
package com.itheima.fileReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Arrays;
public class FileReaderDemo {
public static void main(String[] args) throws Exception {
/*
使用字节流 读取 a.txt
使用字节数组去读取
*/
//1:创建输入流
FileInputStream fis = new FileInputStream("day12\\a.txt");
//2: 执行读操作
byte[] buffer = new byte[10];
int len = fis.read(buffer);//数据读到了 buffer中 有效字节数
System.out.println(len);
//现在有效的字节数据 在哪?
System.out.println(Arrays.toString(buffer));//观察 有效字节数 是前六个
// 看不懂的数据 ---看得懂 需要做 解码
String s = new String(buffer, 0, len);
System.out.println("我们手动解码之后 字节-字符:"+s);
//3: 释放资源
fis.close();
}
}
字符流读取数据
package com.itheima.fileReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.util.Arrays;
/*
FileReader 字符输入流 可以实现 直接读取字符操作
不需要我们做解码
大胆的说
字符流 = 字节流 + 编码表
*/
public class FileReaderDemo2 {
public static void main(String[] args) throws Exception {
/*
使用字符流 读取 day12\\a.txt
*/
//1:创建输入流对象
FileReader fr = new FileReader("day12\\a.txt");
//2:读取
//定义字符数组
char[] buffer = new char[3];
//读
int len = fr.read(buffer);//使用字符数组接收 读取来的字符 读的有效字符个数 len个
System.out.println("有效字符"+len);
System.out.println("查看数组:"+Arrays.toString(buffer));
//3:释放资源
fr.close();
}
}
5.4 字符流的使用
字符流的出现,是让程序员与字节的交流中解脱出来,也就是 再读文件,这个解码的操作, 就教给流去完成,我们不用再关注与解码动作了。
你只关注读出来的字符是什么就行了。
简而言之 就是我们可以一个字符一个字符的读取。
我们没有做解码 谁做了呢?
字符流 = 字节流 + 编码表。
字符流的顶级父类
字符输入流 顶级父类 Reader--抽象的
常用的类 FileReader
常用方法:
int read() 一次读一个字符 使用int类型接收,返回值就是读取到字符int形式,如果结果是-1读到末尾了。
int read(char[] buffer)一次读取一个字符数组,使用int接收读取到有效字符个数。
如果个数是-1读到末尾了。
close()
字符输出流 顶级父类 Writer --抽象的
常用的类 FileWriter
常用方法
这里有五个写方法
* write(char c) 写一个字符
* write(char[] chs)写一个字符数组
* write(char[] chs,int off,int len)写字符数组一部分
* write(String s)写一个字符串
* write(String s,int off,int len)写一个字符串一部分
**字符输出流 **
FileWriter
FileWriter(String path)
FileWriter(File file)
FileWriter(String path,boolean append)
FileWriter(File file,boolean append)
package com.itheima.fileWriter;
import java.io.FileWriter;
public class FileWriterDemo01 {
public static void main(String[] args) throws Exception{
//使用字符输出流 写一个数据 詹姆斯 到 day12\\nba.txt 文件中
//1:创建字符输出流
FileWriter fw = new FileWriter("day12\\nba.txt");
//2: 写
fw.write("詹姆斯");
fw.write("\r\n");
fw.write("杜兰特");
//3: 释放资源
fw.close();
}
}
如果我不释放资源 文件中有没有数据
package com.itheima.fileWriter;
import java.io.FileWriter;
public class FileWriterDemo02 {
public static void main(String[] args) throws Exception{
//使用字符输出流 写一个数据 詹姆斯 到 day12\\nba.txt 文件中
//1:创建字符输出流
FileWriter fw = new FileWriter("day12\\nba.txt");
//2: 写
fw.write("詹姆斯");
//刷新
// fw.flush();
fw.write("杜兰特");
// fw.close(); close不能使用在 刷新之前 因为close包含了刷新同时也释放流资源
fw.flush();
//3: 释放资源
fw.close();
}
}
注意:字符串输出数据,必须要使用 flush()方法,否则数据在内存中,不会到达指定文件,close()方法在关闭前也会进行刷新,推荐写一次刷新一次,避免内存占用过多。
字符输入流
FileReader
一次读一个字符
一次读一个字符数组
package com.itheima.reader;
import java.io.FileReader;
import java.io.IOException;
public class ReaderDemo {
public static void main(String[] args) throws IOException {
//创建字符输入流 用于读取 文本文件 nba.txt
FileReader fr = new FileReader("day11\\nba.txt");
//读数据 采用字符数组形式
char[] chs = new char[24];
int len = fr.read(chs);
// System.out.println("读了几个字符:"+len);
for (int i = 0; i < len; i++) {
System.out.print(chs[i]);
}
fr.close();
}
}
5.4 字符高效流的使用
package com.itheima.buffer;
import java.io.*;
public class BufferDemo {
public static void main(String[] args) throws Exception {
//字符缓冲流
//写的操作
// write();
//读取四行数据
FileReader fr = new FileReader("day12\\cba.txt");
//你不能 你的装饰流 BufferReader
BufferedReader br = new BufferedReader(fr);
// String s1 = br.readLine();//一次读一行 读完指向下一行
// String s2 = br.readLine();//一次读一行 读完指向下一行
// String s3 = br.readLine();//一次读一行 读完指向下一行
// String s4 = br.readLine();//一次读一行 读完指向下一行
// String s5 = br.readLine();//一次读一行 读完指向下一行
//
// System.out.println(s1);
// System.out.println(s2);
// System.out.println(s3);
// System.out.println(s4);
// System.out.println(s5);
//适用循环优化
String s ;//s 用于接收读取到的每一行数据
while((s=br.readLine())!=null){//不等于null说明这一行 读到数据
System.out.println(s);
}
//释放资源
br.close();
fr.close();
}
public static void write() throws Exception {
//字符缓冲流
//写的操作
//使用字符缓冲输出流关联 基本的字符输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("day12\\cba.txt"));
//之前的方法都适用
bw.write("林书豪");
bw.newLine();//是可以匹配 不同的操作系统 增强适用性
bw.write("郭艾伦");
bw.newLine();
bw.write("原帅");
bw.newLine();
bw.write("艾弗森");
//释放资源
bw.close();
}
}
5.5 文本练习
请将文本信息恢复顺序。
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
案例分析
- 使用流对象逐行读取原文本信息,把读取的信息保存到集合中。
- 使用 Collections 集合工具类中的方法 sort,对集合中的元素按照自定义规则排序。
- 遍历集合,把集合中排序后的文本在写入到新的记事本中。
package com.itheima.test;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
public class Test01 {
/*
把 厨师表 中的 每一行数据 进行排序 --输出一个正确顺序的 出师表.txt
1:把出师表的每一行数据读取来。
遍历的时候使用一个集合 进行 收集每一行数据。
2:使用 Collections.sort(List<?> list)
一个参数代表 不改规则 排序比较的规则
两个参数的话 第二个参数是不是可以更改排序的规则
3:排完序了 集合中的内容是不是有顺序了
4:遍历 一行行的 使用字符输出流 输出到一个 出师表.txt文件中
*/
public static void main(String[] args) throws Exception {
//1: 创建字符输入流 --- 创建高效的字符流
FileReader fr = new FileReader("day12\\厨师表.txt");
BufferedReader br = new BufferedReader(fr);
//2: 读取的时候 进行收集
// 创建一个收集每一行的 集合
ArrayList<String> list = new ArrayList<>();
//3 读一行 add一行
String line ;
while((line=br.readLine())!=null){//读到每一行 line
//把line添加到list
list.add(line);
}
// 4 对集合list进行排序
Collections.sort(list);
// 5 排好顺序在 list集合 变遍历变 写
// 需要 一个字符输出流 ---换行 使用 字符缓冲流
FileWriter fw = new FileWriter("day12\\出师表.txt");
BufferedWriter bw = new BufferedWriter(fw);
//遍历
for (String s : list) {
bw.write(s);
bw.newLine();
}
//释放资源
bw.close();
fw.close();
br.close();
fr.close();
}
}
补充章节 IO 流中的异常处理
/*
IO流代码中的异常处理
1.throws: 声明抛出异常
2.try-catch: 捕获处理异常
try{
有可能产生异常的代码
} catch(异常类 对象名){
异常处理的代码
} finally{
释放资源的代码
}
还可以使用JDK7优化后的 try-with-resource 语句,该语句确保了每个资源在语句结束时关闭。所谓的资源(resource)是指在程序完成后,必须关闭的对象。
try(流对象的定义){
有可能产生异常的代码
}catch(异常类 对象名) {
异常处理的代码
}
*/
public class Demo05IOException {
public static void main(String[] args) {
//提升变量的作用域,进行初始化
//不初始化的话,在创建对象时,如果出异常,变量没有值,不能调用方法
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("day11_xw\\io\\from\\jzc.flv");
fos = new FileOutputStream("day11_xw\\io\\to\\jzc.flv");
byte[] bs = new byte[1024*8];
int len = 0;
while ((len = fis.read(bs)) != -1) {
fos.write(bs,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//必须分开处理
//如果try中new对象出了异常,变量的初始值都是null
//调用方法,报出空指针异常
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}