字符流总结--Day21
转换流出现的原因及思想
由于字节流操作中文不是特别方便,所以,java就提供了转换流(InputStreamReader,OutputStreamWriter)。
OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。
InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。
把字节流转换为字符流。
字符流=字节流+编码表。
究竟什么时候采用字节流,什么时候采用字符流?
能够用系统记事本打开并能阅读里面内容时,我们采用字符流,
否则,其他情况我们全部用字节流,注意,视频,应用软件等的复制吧不能使用字符流进行复制,因为字符流在提取字节码是进行编码,难免有所丢失,解码是也会发生状况,所以除了文本文件用字符流,其他文件全部用字节流
/*
* 字节流读取中文可能出现的小问题:
*/
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("a.txt");
// 读取数据
// int by = 0;
// while ((by = fis.read()) != -1) {
// System.out.print((char) by); //这里会输出单个字符,双字符的会变成乱码
// }
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));//这里不会出现乱码,因为他是整段读取,java会分析后判断属于字符还是字节,若像上面一样读取一个,还是会输出中文变乱码
}
// 释放资源
fis.close();
}
}
编码表
由字符及其对应的数值组成的一张表
常见编码表
计算机只能识别二进制数据,早期由来是电信号。
为了方便应用计算机,让它可以识别各个国家的文字。
就将各个国家的文字用数字来表示,并一一对应,形成一张表。
ASCII:美国标准信息交换码。
用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
GB18030:GBK的取代版本
BIG-5码 :通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。
Unicode:国际标准码,融合了多种文字。
所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:
它将Unicode编码为00000000-0000007F的字符,用单个字节来表示它将Unicode编码为00000080-000007FF的字符用两个字节表示?它将Unicode编码为00000800-0000FFFF的字符用3字节表示?
编码
把看得懂的变成看不懂的
解码
把看不懂的变成看得懂的
编码与解码例子,说明编码解码所用的编码表必须相同
public class Demo02 {
// 编码与解码问题
// String(byte[] bytes, String charsetName):通过指定的字符集解码字节数组
// byte[] getBytes(String charsetName):使用指定的字符集合把字符串编码为字节数组
//
// 编码问题很难,难在编码与解码不一致会存在解析异常,不能尊却读取数据
// 编码问题简单,只要编码解码的格式是一致的。
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "你好";
// 字符串到字节数组,称为编码,编码作用:更便于底层进行处理与存储
// 本Eclipse的文本编辑器默认为GBK,可以设置改变
// 下面进行编码:
// ->
byte[] by01 = str.getBytes();// 默认不写
byte[] by02 = str.getBytes("GBK");// 设置为GBK
byte[] by03 = str.getBytes("UTF-8");
System.out.println(Arrays.toString(by01));
System.out.println(Arrays.toString(by02));
System.out.println(Arrays.toString(by03));
// 以上结果输出
// [-60, -29, -70, -61]
// [-60, -29, -70, -61]
// [-28, -67, -96, -27, -91, -67]
// -----------------------------------------------
// 下面进行解码
// ->
System.out.println(new String(by01));// 默认不写
System.out.println(new String(by01, "GBK"));// GBK
System.out.println(new String(by01, "UTF-8"));// UTF-8
System.out.println();
System.out.println(new String(by02));// 默认不写
System.out.println(new String(by02, "GBK"));// GBK
System.out.println(new String(by02, "UTF-8"));// UTF-8
System.out.println();
System.out.println(new String(by03));// 默认不写
System.out.println(new String(by03, "GBK"));// GBK
System.out.println(new String(by03, "UTF-8"));// UTF-8
// 以上结果输出
// 你好
// 你好
// ???
//
// 你好
// 你好
// ???
//
// 浣犲ソ
// 浣犲ソ
// 你好
// -----------------------------------------------
}
}
字符(char)---获取对应字符整型(int)----按指定编码表编码-----得到编码表指向该字符的字节表示形式---保存该字节
/*
* OutputStreamWriter(OutputStream out):OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。
* OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流
* 把字节流转换为字符流。
* 字符流 = 字节流 +编码表。
*/
public static void main(String[] args) throws IOException {
// 默认文本字符集GBK
// OutputStreamWriter osw = new OutputStreamWriter(new
// FileOutputStream("123.txt"));
// osw.write("你好阳光,你好中国!");
// osw.close();
// 显式设定文本字符集:GBK
// OutputStreamWriter osw = new OutputStreamWriter(new
// FileOutputStream("123.txt"),"GBK");
// osw.write("你好阳光,你好中国!");
// osw.close();
// 设定文本字符集:UTF-8
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"123.txt"), "UTF-8");
osw.write("你好阳光,你好中国!");
osw.close();
}
获取文件中的字节码---通过编码表将字节码解码转换为特定的整型(int)形式---将整型形式通过强制转换获得字符
/*
* InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。
* InputStreamReader(InputStream is):用默认的编码读取数据
* InputStreamReader(InputStream is,String charsetName):用指定的编码读取数据
*/
public static void main(String[] args) throws IOException{
//这是默认字符集:GBK
InputStreamReader isr = new InputStreamReader(new FileInputStream("123.txt"));
int ch = 0;//此处获取的是字符编码的int值,与inputStream中每次获取的字节数据不一样,这里是字符,分清字节字符区别
while((ch=isr.read())!=-1){
System.out.print((char)ch);//这里还是要强转,因为返回的是字符的整型(int)形态
}
isr.close();
System.out.println("\n--------------------------\n");
//这是指定字符集:GBK
InputStreamReader isr02 = new InputStreamReader(new FileInputStream("123.txt"),"GBK");
int ch02 = 0;//此处获取的是字符编码的int值,与inputStream中每次获取的字节数据不一样,这里是字符,分清字节字符区别
while((ch02=isr02.read())!=-1){
System.out.println("ch:"+ch02);
//System.out.print((char)ch02);//这里还是要强转,因为返回的是字符的整型(int)形态
}
isr02.close();
System.out.println("\n--------------------------\n");
//这是指定字符集:GBK
InputStreamReader isr03 = new InputStreamReader(new FileInputStream("123.txt"),"UTF-8");
int ch03 = 0;//此处获取的是字符编码的int值,与inputStream中每次获取的字节数据不一样,这里是字符,分清字节字符区别
while((ch03=isr03.read())!=-1){
//System.out.println("ch:"+ch03);
System.out.print((char)ch03);//这里还是要强转,因为返回的是字符的整型(int)形态
}
isr03.close();
// 输出结果:注意,本来,该123.txt文本就是依照UTF-8的编码进行录入,所以前两个按照GBK形式阅读是没有办法解析的,因为UTF-8是三字节解析一个字符
// 浣犲ソ闃冲厜锛屼綘濂戒腑鍥斤紒
// --------------------------
//
// 浣犲ソ闃冲厜锛屼綘濂戒腑鍥斤紒
// --------------------------
//
// 你好阳光,你好中国!
//----------------------------------------------------------------------------
// 在第三段代码while位置中添加:System.out.println("ch:"+ch03);可得字符的UTF-8整型表示形式
// ch:20320
// ch:22909
// ch:38451
// ch:20809
// ch:65292
// ch:20320
// ch:22909
// ch:20013
// ch:22269
// ch:65281
}
public static void main(String[] args) {
// TODO Auto-generated method stub
char ch = '中';
System.out.println((int)ch); //输出结果:20013
}
/*
* OutputStreamWriter的方法:
* public void write(int c):写一个字符
* public void write(char[] cbuf):写一个字符数组
* public void write(char[] cbuf,int off,int len):写一个字符数组的一部分
* public void write(String str):写一个字符串
* public void write(String str,int off,int len):写一个字符串的一部分
*
* 注意以后操作字符流时候,由于字符流不是直接写道文件上,而是先放在缓冲区上缓冲,再执行刷新写入,而字节流文件看了会自动接收。
*
* 不执行close或flush操作时,字节依旧能写入到文件,因为他是不走缓冲的,而字符是双字节,先走缓冲再走写入,所以要刷新缓冲区,将字符码冲到文件中写入
*
* 面试题:close()和flush()的区别?
* A:close()关闭流对象,但是先刷新一次缓冲区。关闭之后,流对象不可以继续再使用了。
* B:flush()仅仅刷新缓冲区,刷新之后,流对象还可以继续使用。
*
* 最好不要每次写字符流都执行flush()方法,因为每一次写完字符,执行flush()方法,就会让方法从方法去送到栈去执行,然后执行完丢去,如果写入内容不是很多,但次数多,那么调用flush方法次数过多,方法整天进栈出栈,效率不高。最好是如果数据量不大,直接执行关闭方法就行,因为方法会自动刷新一次然后在关闭流。若文件真的很大,那么可以设定要一定的数据量时我们调用flush方法,减少调用flush方法次数。
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
// 创建对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"osw2.txt"));
// 写数据
// public void write(int c):写一个字符
// osw.write('a');
// osw.write(97);
// 为什么数据没有进去呢?
// 本次试验,我的关闭方法是没有写的,所以没有进去的第一个原因是没有没有关闭流,其实关闭操作也会进行一次刷新,将数据写道文件上。
// 原因是:字符 = 2字节
// 文件中数据存储的基本单位是字节。
// 文件发现是字符的存入,所以就不会自动操作,所以我们需要手动执行flush()方法进行刷新,把缓冲推到他们该去的地方
// void flush()
// public void write(char[] cbuf):写一个字符数组
// char[] chs = {'a','b','c','d','e'};
// osw.write(chs);
// public void write(char[] cbuf,int off,int len):写一个字符数组的一部分
// osw.write(chs,1,3);
// public void write(String str):写一个字符串
// osw.write("我爱林青霞");
// public void write(String str,int off,int len):写一个字符串的一部分
osw.write("我爱林青霞", 2, 3);
// 刷新缓冲区
osw.flush();
// osw.write("我爱林青霞", 2, 3);
// 释放资源
osw.close();
// java.io.IOException: Stream closed
// osw.write("我爱林青霞", 2, 3);
}
}
/*
* InputStreamReader的方法:
* int read():一次读取一个字符
* int read(char[] chs):一次读取一个字符数组
*/
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
// 创建对象
InputStreamReader isr = new InputStreamReader(new FileInputStream(
"StringDemo.java"));
// 一次读取一个字符
// int ch = 0;
// while ((ch = isr.read()) != -1) {
// System.out.print((char) ch);
// }
// 一次读取一个字符数组
char[] chs = new char[1024];
int len = 0;
while ((len = isr.read(chs)) != -1) {
System.out.print(new String(chs, 0, len));
}
// 释放资源
isr.close();
}
}
/*
* 需求:把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中
*
* 数据源:
* a.txt -- 读取数据 -- 字符转换流 -- InputStreamReader
* 目的地:
* b.txt -- 写出数据 -- 字符转换流 -- OutputStreamWriter
*/
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
// 封装数据源
InputStreamReader isr = new InputStreamReader(new FileInputStream(
"a.txt"));
// 封装目的地
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"b.txt"));
// 读写数据
// 方式1
// int ch = 0;
// while ((ch = isr.read()) != -1) {
// osw.write(ch);
// }
// 方式2
char[] chs = new char[1024];
int len = 0;
while ((len = isr.read(chs)) != -1) {
osw.write(chs, 0, len);
// osw.flush();
}
// 释放资源
osw.close();
isr.close();
}
}
/*
* 由于我们常见的操作都是使用本地默认编码,所以,不用指定编码。
* 而转换流的名称有点长,所以,Java就提供了其子类供我们使用。
* OutputStreamWriter = FileOutputStream + 编码表(GBK)
* FileWriter = FileOutputStream + 编码表(GBK) 用来写入字符文件的便捷类。默认字符集了
*
* InputStreamReader = FileInputStream + 编码表(GBK)
* FileReader = FileInputStream + 编码表(GBK) 用来读取字符文件的便捷类。默认字符集了
*
/*
* 需求:把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中
*
* 数据源:
* a.txt -- 读取数据 -- 字符转换流 -- InputStreamReader --名字太长了,包装了一下-- FileReader
* 目的地:
* b.txt -- 写出数据 -- 字符转换流 -- OutputStreamWriter --名字太长了,包装了一下-- FileWriter
*/
public class CopyFileDemo2 {
public static void main(String[] args) throws IOException {
// 封装数据源
FileReader fr = new FileReader("a.txt");
// 封装目的地
FileWriter fw = new FileWriter("b.txt");
// 一次一个字符
// int ch = 0;
// while ((ch = fr.read()) != -1) {
// fw.write(ch);
// }
// 一次一个字符数组
char[] chs = new char[1024];
int len = 0;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
fw.flush();
}
// 释放资源
fw.close();
fr.close();
}
}
/*
* BufferedReader
* 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
* 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
*
* BufferedReader(Reader in)
*/
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
// 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
// 方式1
// int ch = 0;
// while ((ch = br.read()) != -1) {
// System.out.print((char) ch);
// }
// 方式2
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
System.out.print(new String(chs, 0, len));
}
// 释放资源
br.close();
}
}
/*
* 字符流为了高效读写,也提供了对应的字符缓冲流。
* BufferedWriter:字符缓冲输出流
* BufferedReader:字符缓冲输入流
*
* BufferedWriter:字符缓冲输出流
* 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
* 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
*/
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
// BufferedWriter(Writer out)
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
// new FileOutputStream("bw.txt")));
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
bw.write("hello");
bw.write("world");
bw.write("java");
bw.flush();
bw.close();
}
}
/*
* 字符缓冲流的特殊方法:
* BufferedWriter:
* public void newLine():根据系统来决定换行符,不同的系统添加的换行都会不一样
* BufferedReader:
* public String readLine():一次读取一行数据
* 包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
*/
public class BufferedDemo {
public static void main(String[] args) throws IOException {
// write();
read();
}
private static void read() throws IOException {
// 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("bw2.txt"));
// public String readLine():一次读取一行数据
// String line = br.readLine();
// System.out.println(line);
// line = br.readLine();
// System.out.println(line);
// 最终版代码
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//释放资源
br.close();
}
private static void write() throws IOException {
// 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("bw2.txt"));
for (int x = 0; x < 10; x++) {
bw.write("hello" + x);
// bw.write("\r\n");
bw.newLine();
bw.flush();
}
bw.close();
}
}
九种读写文件方法:
字节流:四种
字符流:五种
-----------------------------------------------------------------------------------------------------
//字节流
/*
* 复制图片
*
* 分析:
* 复制数据,如果我们知道用记事本打开并能够读懂,就用字符流,否则用字节流。
* 通过该原理,我们知道我们应该采用字节流。
* 而字节流有4种方式,所以做这个题目我们有4种方式。推荐掌握第4种。
*
* 数据源:
* c:\\a.jpg -- FileInputStream -- BufferedInputStream
* 目的地:
* d:\\b.jpg -- FileOutputStream -- BufferedOutputStream
*/
public class CopyImageDemo {
public static void main(String[] args) throws IOException {
// 使用字符串作为路径
// String srcString = "c:\\a.jpg";
// String destString = "d:\\b.jpg";
// 使用File对象做为参数
File srcFile = new File("c:\\a.jpg");
File destFile = new File("d:\\b.jpg");
// method1(srcFile, destFile);
// method2(srcFile, destFile);
// method3(srcFile, destFile);
method4(srcFile, destFile);
}
// 字节缓冲流一次读写一个字节数组
private static void method4(File srcFile, File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
srcFile));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
bis.close();
}
// 字节缓冲流一次读写一个字节
private static void method3(File srcFile, File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
srcFile));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destFile));
int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
}
// 基本字节流一次读写一个字节数组
private static void method2(File srcFile, File destFile) throws IOException {
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
fos.close();
fis.close();
}
// 基本字节流一次读写一个字节
private static void method1(File srcFile, File destFile) throws IOException {
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
}
---------------------------------------------------------------------------------------------------------------
//字符流
/*
* 复制文本文件
*
* 分析:
* 复制数据,如果我们知道用记事本打开并能够读懂,就用字符流,否则用字节流。
* 通过该原理,我们知道我们应该采用字符流更方便一些。
* 而字符流有5种方式,所以做这个题目我们有5种方式。推荐掌握第5种。
* 数据源:
* c:\\a.txt -- FileReader -- BufferdReader
* 目的地:
* d:\\b.txt -- FileWriter -- BufferedWriter
*/
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
String srcString = "c:\\a.txt";
String destString = "d:\\b.txt";
// method1(srcString, destString);
// method2(srcString, destString);
// method3(srcString, destString);
// method4(srcString, destString);
method5(srcString, destString);
}
// 字符缓冲流一次读写一个字符串
private static void method5(String srcString, String destString)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(srcString));
BufferedWriter bw = new BufferedWriter(new FileWriter(destString));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
// 字符缓冲流一次读写一个字符数组
private static void method4(String srcString, String destString)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(srcString));
BufferedWriter bw = new BufferedWriter(new FileWriter(destString));
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
}
bw.close();
br.close();
}
// 字符缓冲流一次读写一个字符
private static void method3(String srcString, String destString)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(srcString));
BufferedWriter bw = new BufferedWriter(new FileWriter(destString));
int ch = 0;
while ((ch = br.read()) != -1) {
bw.write(ch);
}
bw.close();
br.close();
}
// 基本字符流一次读写一个字符数组
private static void method2(String srcString, String destString)
throws IOException {
FileReader fr = new FileReader(srcString);
FileWriter fw = new FileWriter(destString);
char[] chs = new char[1024];
int len = 0;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
fw.close();
fr.close();
}
// 基本字符流一次读写一个字符
private static void method1(String srcString, String destString)
throws IOException {
FileReader fr = new FileReader(srcString);
FileWriter fw = new FileWriter(destString);
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
fw.close();
fr.close();
}
}