3.字符流
3.1、为什么会出现字符流
由于字节流操作中文不是特别方便,所以Java就提供字符流
- 字符流=字节流+编码表
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中,如何实现拼接的呢?
- 汉字在存储时,无论选择哪种编码存储,第一个字节都是负数
package itheima06;import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;/*
一个汉字如果是GBK编码,占用2个字节 开头的字节都是负数如果是UTF-8编码,占用3个字节 开头的字节都是负数
*/
public class Demo01 {public static void main(String[] args) throws IOException {
/*// FileInputStream fis=new FileInputStream("D:\\itcast\\java.txt");FileInputStream fis=new FileInputStream("myByteStream\\a.txt");int len;
// byte [] bys=new byte[1024];while((len=fis.read())!=-1){
// System.out.println(new String(bys,0,len));System.out.print((char) len);}fis.close();*/
// String s="abc";输出//[97, 98, 99]String s="中国";
// byte[] bytes = s.getBytes();//一个汉字对应了三个UTF-8编码[-28, -72, -83, -27, -101, -67]
// 说明默认是UTF-8编码
//当我们指定GBK编码是,一个汉字对应的就是两个字节
// byte[] bytes = s.getBytes("UTF-8");等价于 byte[] bytes = s.getBytes();byte[] bytes = s.getBytes("GBK");//[-42, -48, -71, -6]System.out.println(Arrays.toString(bytes));}
}
3.2、编码表
基础知识:
用什么字符集存储(编码),就用对用的字符集解码
字符集:系统支持的所有字符的集合,包括各国文字,标点、图形符号、数字
常见的字符集有
ASCII:
ASCII 码使用指定的 7 位或 8 位二进制数组合来表示 128 或 256 种可能的字符。标准 ASCII 码也叫基础ASCII码,使用 7 位二进制数来表示所有的大写和小写字母,数字 0 到 9、标点符号, 以及在美式英语中使用的特殊控制字符。其中:
0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(振铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为 8、9、10 和 13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。
32~126(共95个)是字符(32sp是空格),其中48~57为0到9十个阿拉伯数字;
65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
同时还要注意,在标准ASCII中,其最高位(b7)用作奇偶校验位。所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。
后128个称为扩展ASCII码,目前许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展 ASCII 码允许将每个字符的第 8 位用于确定附加的 128 个特殊符号字符、外来语字母和图形符号。
GBXXX字符集
- GB2312:简体中文码表,ASCII中有的127个称为半角字符,127以上的称为全角字符
- GBK: 最常用的中文编码表,使用了双字节编码表方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
Unicode字符集:
为了表达任意语言字符而设计,是业界的一种标准,也成为统一码,万国码。它最多使用4个字节的数字来表达每个字母、符号、或文字。有三种编码方案:UTF-8、UTF-16、UTF32. UTF-8最常用
UTF-8:
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要两个字节编码
大部分常用语(含中文),使用三个字解码编码
其他极少数使用Unicode辅助字符,使用四字节编码
注:采用何种规则编码,就要采用对应的规则解码,否则就会出现乱码。
3.3、字符串中的编码解码问题
编码:
byte[] getBytes() 使用平台的默认字符集(UTF-8)将该String编码为一系列字节,将结果存储到字节数组中
byte[] getBytes(String CharStream) 使用指定的字符集,将该String编码为一系列字节,将结果存储到字节数组中
解码:
String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(byte[] bytes,String CharStream) 通过使用指定的字符集解码指定的字节数组来构造新的String
package com.itheima01;/*
编码:byte[] getBytes() 使用平台的默认字符集(UTF-8)将该String编码为一系列字节,将结果存储到字节数组中byte[] getBytes(String CharStream) 使用指定的字符集,将该String编码为一系列字节,将结果存储到字节数组中
解码:String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的StringString(byte[] bytes,String CharStream) 通过使用指定的字符集解码指定的字节数组来构造新的String
*/import java.io.UnsupportedEncodingException;
import java.util.Arrays;public class StringDemo {public static void main(String[] args) throws UnsupportedEncodingException {
/*
// 默认UTF—8编码:
// byte[] getBytes() 使用平台的默认字符集(UTF-8)将该String编码为一系列字节,将结果存储到字节数组中String s="我爱你";byte[] bytes = s.getBytes();System.out.println(Arrays.toString(bytes));//默认UTF-8每个字用三个数字表示[-26, -120, -111, -25, -120, -79, -28, -67, -96]// 默认UTF-8解码:
// String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的String
// String ss=new String(bytes);
// System.out.println(ss);//解码的结果为: 我爱你String sss=new String(bytes,"GBK");System.out.println(sss);//编码解码用的字节符不同时 的结果乱码: 鎴戠埍浣�
*/// GBK编码:
// byte[] getBytes(String CharStream) 使用指定的字符集,将该String编码为一系列字节,将结果存储到字节数组中 byte[] getBytes() 使用平台的默认字符集(UTF-8)将该String编码为一系列字节,将结果存储到字节数组中String s="我爱你";byte[] bytes = s.getBytes("GBK");System.out.println(Arrays.toString(bytes));//GBK每个字用2个数字表示[-50, -46, -80, -82, -60, -29]// GBK解码:
// String(byte[] bytes,String CharStream) 通过使用指定的字符集解码指定的字节数组来构造新的String String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的StringString sss=new String(bytes,"GBK");System.out.println(sss);//解码结果乱码: 我爱你�}
}
3.4、字符流中的编码解码问题
字符流抽象基类
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
字符流中和编码解码问题相关的两个类
- InputStreamReader 从字节流到字符流的桥梁
- OutputStreamWriter
package com.itheima02;/*
InputStreamReader 是从字节流到字符流的桥梁他读取字节,并使用指定的编码将其解码为字符它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
OutputStreamWriter 是从字符流到字节流的桥梁使用指定的编码将写入的字符编码为字符它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
*/import java.io.*;
import java.util.Arrays;public class ConversionStreamDemo {public static void main(String[] args) throws IOException {// OutputStreamWriter(OutputStream out)创建一个默认字符编码的OutputStreamWriter
// OutputStreamWriter(OutputStream out,String charsetName)创建一个指定字符编码的OutputStreamWriterOutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"),"GBK");
// 当我们指用GBK编码字符串"中国" 写数据osw.write("中国");//编码的结果就是 �й�osw.close();
// InputStreamReader(InputStream in) 创建一个默认字符集的InputStreamReader
// InputStreamReader(InputStream in,Stirng CharsetName) 创建一个默认字符集的InputStreamReaderInputStreamReader isr=new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"),"GBK");
// 一次读取一个字符,需要读两次,第一次读"中","国"/* int ch= isr.read();System.out.println((char) ch);ch= isr.read();System.out.println((char) ch);*/
// 一次读取字符数组int ch;while ((ch=isr.read())!=-1){System.out.print((char) ch);//�й�}
//优化改进一次读一个数组/* int len;byte[] bys=new byte[1024];while((len=isr.read(bys))!=-1){
// System.out.println(len);System.out.println(new String(bys,0,len));}*//*int len;byte [] bys=new byte[1024];while((len= bis.read(bys))!=-1){System.out.print(new String(bys,0,len));}
*/isr.close();}
}
3.5、字符流写数据的5种方式
void writer (int c) 写一个字符 void writer (char[] cbuf) 写一个字符数字 void writer (char[] cbuf,int off,int len) 写入一个字符数组的一部分 void writer (String str) 写一个字符串 void writer (String str,int off,int len) 写入一个字符串的一部分
package com.itheima03;
/*构造方法:InputStreamReader(InputStream in) 创建一个试用默认字符集的InputStreamReader
读数据的2种方式int read()一次读一个字符数据int read(char[] cbuf)一次读一个字符数组数据*/import java.io.*;public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {
// 先写一个GBK编码的文件,然后再用InpuStreamReader来读数据OutputStreamWriter osw2=new OutputStreamWriter(new FileOutputStream("myCharStream\\osw2.txt"),"GBK");
// void writer (int c) 写一个字符String s1="我";String s2="爱";String s3="你";osw2.write(s1);osw2.write(s2);osw2.write(s3);osw2.close();/* InputStreamReader isr=new InputStreamReader(new FileInputStream("myCharStream\\osw2.txt"),"GBK");
// int read()一次读一个字符数据*//* int ch;while((ch= isr.read())!=-1){System.out.print((char) ch);}*//*
// int read(char[] cbuf)一次读一个字符数组数据
// String[] s=new String[1024];char [] chs=new char[1024];int len;while((len= isr.read(chs))!=-1){
// 把一个字符串数组转换成字符System.out.print(new String(chs,0,len));}*/// 重新问一个文件读取数据InputStreamReader isr=new InputStreamReader(new FileInputStream("myCharStream\\ConversionStreamDemo.java"));
// int read()一次读一个字符数据/* int ch;while((ch= isr.read())!=-1){System.out.print((char) ch);}*/
// int read(char[] cbuf)一次读一个字符数组数据
// String[] s=new String[1024];/*char [] chs=new char[1024];int len;while((len= isr.read(chs))!=-1){
// 把一个字符串数组转换成字符System.out.print(new String(chs,0,len));}*/copy();}
//创建一个方法用来copy ConversionStreamDemo.java 并将改文件复制到copy.java中public static void copy() throws IOException {
// 先用InputStreamReader读取ConversionStreamDemo.java文件InputStreamReader isr=new InputStreamReader(new FileInputStream("myCharStream\\ConversionStreamDemo.java"));
// 然后用OutputStreamWriter将ConversionStreamDemo.java文件字节流写入copy.java中OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("myCharStream\\copy.java"));char[] chs=new char[1024];int len;while((len= isr.read(chs))!=-1){System.out.println(new String(chs,0,len));osw.write(chs);}isr.close();osw.close();}}
案例:
需求:把目录下的ConversionStreamDemo.java复制到模块目录下的copy.java
用了两个子类简化创建对象
FileReader 替代InputStreamReader 但是有缺点就是不能用来指定字符集
FileWriter 替代OutputStreamWriter 但是有缺点就是不能用来指定字符集
package com.itheima03;import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
需求:把目录下的ConversionStreamDemo.java复制到模块目录下的copy.java用了两个子类简化创建对象FileReader 替代InputStreamReader 但是有缺点就是不是能用来指定字符集FileWriter 替代OutputStreamWriter 但是有缺点就是不是能用来指定字符集*/public class copyDemo {public static void main(String[] args) throws IOException {FileReader fr=new FileReader("myCharStream\\ConversionStreamDemo.java");FileWriter fw=new FileWriter("myCharStream\\copy.java");char[] chs=new char[1024];int len;while((len=fr.read(chs))!=-1){fw.write(chs,0,len);}fr.close();fw.close();}
}
3.7、字符缓冲流
BufferedWriter 将文本写入字符输出流,缓冲字符,以提供单个字符,
数组和字符串的高效写入,可以制定缓冲区大小,或者接收默认大小(默认足够大)
BufferedReader 从字符输入流读取文本,缓冲字符,以提供字符,
数组和行字符串的高效读取,可以制定缓冲区大小,或者接收默认大小(默认足够大)
构造方法:
BufferedWriter(Writer out)
BufferedReader(Reader in)
案例需求:把目录下的ConversionStreamDemo.java复制到模块目录下的copy.java
package com.itheima04;
/*
需求:把目录下的ConversionStreamDemo.java复制到模块目录下的copy.java
BufferedWriter 将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以制定缓冲区大小,或者接收默认大小(默认足够大)
BufferedReader 从字符输入流读取文本,缓冲字符,以提供字符,数组和行字符串的高效读取,可以制定缓冲区大小,或者接收默认大小(默认足够大)
构造方法:BufferedWriter(Writer out)BufferedReader(Reader in)
*/import java.io.*;public class BufferedDemo {public static void main(String[] args) throws IOException {long starTime=System.currentTimeMillis();buffered();long endTime=System.currentTimeMillis();System.out.println("用字符缓冲流写的程序耗时:"+(endTime-starTime)+"毫秒");}public static void buffered() throws IOException {BufferedWriter bw=new BufferedWriter(new FileWriter("myCharStream\\copy.java"));BufferedReader br=new BufferedReader(new FileReader("myCharStream\\ConversionStreamDemo.java"));
// 一次读取一个字符数据/* int ch;while((ch= br.read())!=-1){
// System.out.println((char) ch);bw.write(ch);}*/char[] chs=new char[1024];int len;while((len= br.read(chs))!=-1){bw.write(chs,0,len);}
// 一次读取一个字符串数据bw.close();br.close();}
}
3.8、字符缓冲流特有功能
字节串缓冲流的特有功能 BufferedWritervoid newLine() 写一行行分隔符,行分隔符字符串由系统属性定义BufferedReaderpublic String readLine() 读一行文字结果包括行的内容的字符串,不包括任何行终止符,如果流已到达结尾,则为null
package com.itheima04;
/*字节串缓冲流的特有功能BufferedWritervoid newLine() 写一行 行分隔符,行分隔符字符串由系统属性定义BufferedReaderpublic String readLine() 读一行文字结果包括行的内容的字符串,不包括任何行终止符,如果流已到达结尾,则为null
*/import java.io.*;public class BufferedStreamDemo {public static void main(String[] args) throws IOException {/* BufferedWriter bw=new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));for (int i=0;i<3;i++){bw.write("hello"+i);
// bw.write("\r\n");
// void newLine() 写一行行分隔符,行分隔符字符串由系统属性定义
// bw.newLine();bw.flush();}bw.close();*/// BufferedReaderBufferedReader br=new BufferedReader(new FileReader("myCharStream\\bw.txt"));
// public String readLine() 读一行文字
// 结果包括行的内容的字符串,不包括任何行终止符,如果流已到达结尾,则为null
/*// 第一次读取数据---应该读的是第一行的数据String line1 = br.readLine();System.out.println(line1);
// 第二次读取数据---应该读的是第2行的数据String line2 = br.readLine();System.out.println(line2);
// 第3次读取数据---应该读的是第3行的数据String line3 = br.readLine();System.out.println(line3);
// 第4次读取数据---没有内容是输出nullString line4 = br.readLine();System.out.println(line4);*/
//循环改进String line;while((line=br.readLine())!=null){// System.out.print(line); 该方法不会读取换行符所以需要手动换行System.out.println(line);}br.close();}
}
案例:复制单极文件夹
package com.itheima09;
/*需求:把"D:\\itcast"这个文件夹复制到模块目录下
思路:创建数据源目录File对象,路径是D:\\itcast获取数据源目录File对象的名称(itcast)创建目的地目录File对象,路径名是模块名+itcast组成(myCharStream\\itcast)判断目的地目录对应的File是否存在,如果不存在,就创建获取数据源目录下所有文件的File数组遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件数据源文件:D:\\itcast\\lala.jpg获取数据源文件File对象的名称(lala.jpg)创建目的地文件File对象,路径名是目的地目录+lala.jpg组成(myCharStream\\itcast\\lala.jpg)复制文件由于文件不仅仅是文本文件,还有图片、视频,所以采用字节流复制文件
*/import java.io.*;public class CopyFolderDemo {public static void main(String[] args) throws IOException {
// 创建数据源目录File对象,路径是D:\\itcastFile srcFolder=new File("D:\\itcast");
// 获取数据源目录File对象的名称(itcast)String srcFolderName=srcFolder.getName();
// System.out.println("源目录名:"+srcFolderName);
// 创建目的地目录File对象,路径名是模块名+itcast组成(myCharStream\\itcast)File destFolder=new File("myCharStream",srcFolderName);
// 判断目的地目录对应的File是否存在,如果不存在,就创建if(!destFolder.exists()){destFolder.mkdir();}
// 获取数据源目录下所有文件的File数组File [] listFile=srcFolder.listFiles();
// 遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件for(File srcFile :listFile){
// 数据源文件:D:\\itcast\\lala.jpg
// 获取数据源文件File对象的名称(lala.jpg)String srcFileName=srcFile.getName();
// System.out.println(srcFileName);
// 创建目的地文件File对象,路径名是目的地目录+lala.jpg组成(myCharStream\\itcast\\lala.jpg)File destFile=new File(destFolder,srcFileName);// 复制文件copyFile(srcFile,destFile);}
// 由于文件不仅仅是文本文件,还有图片、视频,所以采用字节流复制文件}public static void copyFile(File srcFile,File destFile) throws IOException {
// 使用字节缓冲流写文件BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile));
// 使用字节缓冲流读取数据BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));int len;byte[] bys=new byte[1024];while((len= bis.read(bys))!=-1){bos.write(bys);}bis.close();bos.close();}
}
案例:复制多级文件
package com.itheima09;
/*需求:复制多级目录
思路:
①创建数据源File对象,路径是D:\\itcast
②创建目的地File对象,路径C:
③写方法实现文件夹的复制,参数为数据File对象和目的地Fiie对象
④判断数据源File是否是目录是目录:在目的地下创建和数据源File名称一样的目录获取数据源File下所有数组,得到每一个File对象遍历File数组,得到每一个File对象把该File作为数据源File对象,递归调用复制文件夹的方法不是目录说明File对象是文件,直接调用方法复制,用字节流*/
import java.io.*;
public class CopyMultiFolderDemo {public static void main(String[] args) throws IOException {
//创建数据源File对象,路径是D:\\itcastFile srcFolder=new File("D:\\itcast");
//创建目的地File对象,路径C:\\File destFolder=new File("C:\\");
//写方法实现文件夹的复制,参数为数据File对象和目的地Fiie对象copyFolder(srcFolder,destFolder);}
//复制文件夹private static void copyFolder(File srcFolder,File destFolder) throws IOException {//判断数据源File是否是目录if (srcFolder.isDirectory()){
// 是目录:
// 在目的地下创建和数据源File名称一样的目录String srcFolderName = srcFolder.getName();
// 获取D盘相同的文件名File newFileName=new File(destFolder,srcFolderName);// itcastSystem.out.println("从D盘复制过来的目录名:"+newFileName);
// 再次判断一下C盘有没有itcast这个文件,确保c盘有File newFileName=new File(destFolder,srcFolderName);// itcast文件/* 从D盘复制过来的目录名:C:\itcast从D盘复制过来的目录名:C:\itcast\JavaSE从D盘复制过来的目录名:C:\itcast\JavaWEB从D盘复制过来的目录名:C:\itcast\JavaWEB\HTML*/if (!newFileName.exists()){newFileName.mkdir();}
// 获取数据源File下 所有数组,得到每一个File对象File[] fileArray = srcFolder.listFiles();//C:\\itcast
// 遍历File数组,得到每一个File对象for (File file:fileArray){System.out.println("数据源File下所有:"+file);
// 需要将遍历获取的File对象封装起来
// String.valueOf(file)
// 把该File作为数据源File对象,递归调用复制文件夹的方法 下一层级如果还是目录还是继续递归copyFolder(file,newFileName);
// File destFile=new File(newFileName, file);}}else{
// 不是目录
// 说明File对象是文件,直接调用方法复制,用字节流
// copyFile(源文件,目的地文件);这里的文件名每一此次都会变,所以需要封装起来File newFile=new File(destFolder,srcFolder.getName());copyFile(srcFolder,newFile);}}
//复制文件public static void copyFile(File srcFile,File destFile) throws IOException{
// 使用字节缓冲流写文件BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile));
// 使用字节缓冲流读取数据BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));int len;byte[] bys=new byte[1024];while((len= bis.read(bys))!=-1){bos.write(bys);}bis.close();bos.close();}
}
3.9、字节流报错处理
package com.itheima10;/*
try{可能出现异常的代码}catch{异常的处理代码}finally{执行所有清楚操作}
*/import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;public class CopyFileDemo {public static void main(String[] args)throws IOException {method1();method2();}// 抛出异常的标准写法JDK9 会自动释放资源public static void method4() throws IOException{FileReader fr=new FileReader("fr.txt");FileWriter fw=new FileWriter("fw.txt");try(fr; fw;){char[] chs = new char[1024];int len;while ((len = fr.read(chs)) != -1){fw.write(chs, 0, len);}}catch(IOException e){e.printStackTrace();}}// 抛出异常的标准写法JDK7 会自动释放资源public static void method3() {try(FileReader fr=new FileReader("fr.txt");FileWriter fw=new FileWriter("fw.txt");){char[] chs = new char[1024];int len;while ((len = fr.read(chs)) != -1){fw.write(chs, 0, len);}}catch(IOException e){e.printStackTrace();}}/* fr.close();fw.close();*/
// 抛出异常的标准写法try{..}catch(){...}finallypublic static void method2() {FileReader fr=null;FileWriter fw=null;try{fr = new FileReader("fr.txt");fw = new FileWriter("fw.txt");char[] chs = new char[1024];int len;while ((len = fr.read(chs)) != -1) {fw.write(chs, 0, len);}}catch(IOException e){e.printStackTrace();}finally{if (fr!=null){try{fr.close();}catch(IOException e){e.printStackTrace();}}if(fw!=null){try{fw.close();}catch(IOException e){e.printStackTrace();}}}}
// 抛出异常,调用方法时还需要抛出一次异常public static void method1()throws IOException {FileReader fr = new FileReader("fr.txt");FileWriter fw = new FileWriter("fw.txt");char[] chs = new char[1024];int len;while ((len = fr.read(chs)) != -1) {fw.write(chs, 0, len);}fr.close();fw.close();}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
