IO学习笔记之字符流
一、 字符流
字符流框架如图1所示。
图1
1.概述:字符流就是字节流+编码表。
2.编码表:
由字符及其对应的数值组成的一张表
常见编码表
ASCII:用7位表示一个数据
ISO-8859-1:拉丁码表,用8位表示一个数据
GB2312:简体中文,2个字节表示一个汉字,表示的汉字有限
GBK:简体中文,GB2312的升级版
GB18030:简体中文,代替GBK
BIG5:繁体中文,港澳台使用
Unicode:国际码,用2个字节表示一个数据.
UTF-8:替代Unicode码表,国际码,统一码。表示一个数据,所使用的字节数是变化的,使用1-4位字节来表示一个数据, 一个汉字使用3个字节。
3.字符串中的编码问题
编码:把看懂的数据转换成看不懂的数据
解码:把看不懂的数据转换成看懂的数据
String类中的解码方法:
public String(byte[] bytes)用系统平台默认的字符集,进行解码指定的 byte 数组
public String(byte[] bytes, StringcharsetName) 通过指定的编码表,进行解码指定的 byte 数组
String类中的编码方法
publicbyte[] getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
public byte[] getBytes(String charsetName)使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的byte 数组中。
测试案例:
public class StringCoding {
public static void main(String[] args) throwsUnsupportedEncodingException {
byte[]bys = "你好".getBytes();//编码,默认使用GBK编码
byte[]bys2 = "你好".getBytes("utf-8");//编码,规定使用UTF-8编码
System.out.println(Arrays.toString(bys));//打印编码后的值
System.out.println(Arrays.toString(bys2));//打印编码后的值
String result = new String(bys);//默认使用GBK解码
String result2 = new String(bys2,"utf-8");//规定使用utf-8解码
System.out.println(result);//打印解码后的值
System.out.println(result2);//打印解码后的值
}
}
输出结果:
[-60,-29, -70, -61]
[-28,-67, -96, -27, -91, -67]
你好
你好
注意:数据的编解码操作要的字符编码表要统一。
二、OutputStreamWriter类
简介:编码转换流,也叫字符输出流,继承Writer类。把基本的字节流使用给定的编码表,进行数据的编码。即把字节流转换成字符流。
构造方法:
publicOutputStreamWriter(OutputStream out)
//创建使用默认字符编码(gbk)的OutputStreamWriter。
publicOutputStreamWriter(OutputStream out,String charsetName)
//创建使用指定字符集的OutputStreamWriter。
常用方法:
public void close()//先刷新该流的缓冲,再关闭流。
public void flush()//只刷新该流的缓冲,不关闭流
public void write(int c)//写入一个字符
public void write(char[] cbuf)//写入一个字符数组,父类Writer中的方法
public void write(String str)//写入一个字符串,父类Writer中的方法
public void write(char[] cbuf,intoff,int len)//写入字符数组一部分
public void write(String str,int off,int len)//写入字符串一部分
public String getEncoding()//返回此流使用的字符编码的名称
测试案例:字符流的5中写数据方法
public classOutputStreamWriterMethod {
public static void main(String[] args) throws IOException {
//1、创建字符输出流对象
OutputStreamWriterosw =new OutputStreamWriter(new FileOutputStream("d:\\lee\\222.txt"));
//2、写数据
//2.1写一个字符
//osw.write('a');
//osw.write(98);
//osw.flush();
//2.2写一个字符数组
//osw.write("你好".toCharArray());//把字符串转换成字符数组
//osw.flush();
//2.3写一个字符数组的一部分
//char[] ch = "世界".toCharArray();//把字符串转换成字符数组
//注意:2个汉字占4个字节,
//System.out.println(Arrays.toString(ch));//[世,界]数组中有两个字节
//osw.write(ch, 0, 1);//0表示数组的起始位置,1表示元素个数。世
//osw.write(ch, 0, 2);//世界
//osw.flush();
//2.4写一个字符串
//osw.write("云计算");
//osw.flush();
//2.5写一个字符串的一部分
String string = "大数据";
osw.write(string,0, 2);
//3、关闭流
osw.close();
}
}
注意:后一次的写操作会覆盖掉前一次的数据。
flush()和close()的区别?
flush()后,流没有关闭,可以继续读写数据
close()后,流关闭了,不能够继续读写数据
三、InputStreamReader类
简介:解码转换流,也叫字符输入流。继承Reader类,把基本的字节流,使用给定的编码表,进行数据的解码。
构造方法:
public InputStreamReader(InputStreamin)//使用平台默认的字符集, 进行数据的解码
public InputStreamReader(InputStreamin,String charsetName) 使用给定的字符集,进行数据的解码
这两个构造方法是将InputStream 字节输入流,使用字符流进行解码操作,返回的结果是InputStreamReader字符输入流
常用方法:
public void close()//关闭该流并释放与之关联的所有资源。
public int read()//读取一个字符
public int read(char[]buf)//读取一个字符数组,父类Reader中的方法
测试案例1:字符流的2种读数据的常用方法
public classInputStreamReaderMethod {
public static void main(String[] args) throws IOException {
//1、创建流对象
InputStreamReaderisr = new InputStreamReader(new FileInputStream("d:\\lee\\1111.java"),"utf-8");
//2、读数据
//2.1读一个字符
/*
int ch= -1;
while ((ch= isr.read()) != -1) {
System.out.print((char)ch);//把读到的数据转成字符
}
*/
//2.2读一个字符数组
char[]chs = newchar[1024]; //1个字符对应着2个字节, 2048字节
int len = -1;//保存读取到字符数据的个数,默认为-1,表示没有读到数据,文件为空。
while ((len =isr.read(chs)) != -1) {
System.out.print(new String(chs, 0 ,len) );
}
// 关闭流
isr.close();
}
}
测试案例2:使用转换流完成文本文件的复制
public class copyTextFile {
public static voidmain(String[] args) throws IOException {
//1、数据源,把基本字节输入流转成字符输入流对象
InputStreamReaderisr =new InputStreamReader(new FileInputStream("d:\\lee\\1111.java"),"utf-8");
//2、目的地,把基本字节输出流转成字符输出流对象
OutputStreamWriterosw =new OutputStreamWriter(new FileOutputStream("c:\\lee\\1.java"),"utf-8");
//3、读数据,读一个字符数组数据
//定义一个字符数组
char[]buffer = newchar[1024];
int ch = -1;//保存读到字符数据的个数,如果读到流末尾,返回-1,默认文件为空
while ((ch =isr.read(buffer)) != -1) {
//4、写数据,写一个字符数组一部分
osw.write(buffer, 0,ch);
}
//关闭流
isr.close();
osw.close();
}
}
四、FileReader类
简介:继承InputStreamReader类,用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。从JDK1.1开始。
构造方法:
public FileReader(Filefile)
//在给定从中读取数据的 File 的情况下创建一个新FileReader。
public FileReader(StringfileName)
//在给定从中读取数据的文件名的情况下创建一个新FileReader。
五、FileWriter类
简介:继承OutputStreamWriter类,用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。从JDK1.1开始。
构造方法:
public FileWriter(Filefile)
//根据给定deFile对象构造一个FileWriter对象
public FileWriter(StringfileName)
//根据给定的文件名构造一个 FileWriter对象
FileReader和FileWriter测试案例:拷贝文件
public class copyTextFile2 {
public static void main(String[] args) throws IOException {
//1、数据源,平台默认的编码(GBK编码)
FileReader fr = new FileReader("d:\\lee\\1111.java");
//2、目的地,平台默认的编码(GBK编码)
FileWriter fw = new FileWriter("c:\\lee\\abc.java");
//3、读数据
int ch = -1;//保存读到的数据
while ((ch =fr.read()) != -1) {
//4、写数据
fw.write(ch);
}
//5、关闭流
fr.close();
fw.close();
}
}
注意:如果使用平台的默认字节集进行的编解码操作(GBK编码),可以使用更简化的流FileReader和FileWriter如果平台使用其他编码,就只能使用InputStreamReader和OutputStreamWriter转换流。
六、BufferedWriter类
简介:高效字符缓冲输出流,继承Writer类。将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。从JDK1.1开始。
构造方法:
public BufferedWriter(Writerout)
//创建一个使用默认大小输出缓冲区的缓冲字符输出流
public BufferedWriter(Writerout,int sz)
//创建一个使用给定大小输出缓冲区的新缓冲字符输出流
常用方法:
newLine()//写入一个行分隔符,换行符。
public void write(int c) //写入单个字符
public void write(char[] cbuf,int off,int len) //写入字符数组的某一部分
public void write(Strings,int off,int len)//写入字符串的某一部分
测试案例:
public classBufferedWriterDemo {
public static void main(String[] args) throws IOException {
//1、创建流对象
//先创建基本的字符流
//Writer w = newFileWriter("d:\\lee\\1234.java");
//再创建高效字符流,把基本的字符流包装到高效的字符输出流中
//BufferedWriter bw = new BufferedWriter(w);
//和字节流类似,把基本的字符流包装成高效的流
BufferedWriterbw = new BufferedWriter(new FileWriter("d:\\lee\\1234.java"));
//注意:写数据时,即使指定的文件不存在,系统会自动创建指定的文件。读文件时,必须保证指定的文件是已存在的。
//2、写数据
bw.write("javaEE,云计算,大数据");
//3、刷新,写数据时,最好刷新一下
bw.flush();
//4、关闭流
bw.close();
}
}
七、BufferedReader类
简介:高效字符缓冲输入流,继承Reader类。从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。从JDK1.1开始。
构造方法:
public BufferedReader(Readerin)//创建一个使用默认大小输入缓冲区的缓冲字符输入流
public BufferedReader(Readerin,int sz)//创建一个使用指定大小输入缓冲区的缓冲字符输入流
常用方法:
public int read()//读取单个字符,如果已到达流末尾,则返回 -1
public int read(char[] cbuf,int off,int len)//读取字符数组的一部分
public StringreadLine()//读取一个文本行
测试案例:
public classBufferedReaderDemo {
public static void main(String[] args) throws IOException {
//1、创建高效输入流对象
//基本字符流
//Reader reader = newFileReader("d:\\lee\\111.java");
//把基本字符流包装成高效的字符输入流
//BufferedReader br = new BufferedReader(reader);
BufferedReader br =new BufferedReader(new FileReader("d:\\lee\\111.java"));
//注意:读文件时,不能自动创建不存在的文件,所以必须保证先有要读的文件。写文件时可以自动创建不存在的文件。
//2、读数据
char[]ch = newchar[1024];//一次读取一个数组的字符
intlen = -1;//保存读取到的字符个数
while ((len =br.read(ch)) != -1) {
//3、写数据
System.out.println(new String(ch, 0,len));
}
//4、关闭流
br.close();
}
}
综合案例1:用高效字符流拷贝文件
public class copyTextFile {
public static void main(String[] args) throws IOException {
//1、数据源
BufferedReaderbr = new BufferedReader(new FileReader("d:\\lee\\1234.txt"));
//2、目的地
BufferedWriterbw = new BufferedWriter(new
FileWriter("c:\\lee\\1.txt"));
//3、读,一次读一个数组的字符
char[]buffer = newchar[1024];
int len = -1;
while ((len =br.read(buffer)) != -1) {
//4、写,一次写一个数组一部分
bw.write(buffer, 0,len);
}
//5、关闭
br.close();
bw.close();
}
}
综合案例2:字符缓冲的特有方法复制文件。
public class CopyTextFile {
public static void main(String[] args) throws IOException {
//1、数据源
BufferedReaderbr = new BufferedReader(new FileReader("面试题.txt"));
//2、目的地
BufferedWriterbw = new BufferedWriter(new FileWriter("copyText.txt"));
//3、读一行字符
String line = null;//记录每次读的字符数据
while ((line =br.readLine()) != null) {
//4、写一行字符
bw.write(line);
bw.newLine();//换行
bw.flush();//刷新
}
//5、关闭流
br.close();
bw.close();
}
}
八、字节流与字符流
复制文本文件方法
字符流5种,字符流只能复制文本文件
基本的流,一次一个字符
基础的流,一次一个字符数组
高效的流,一次一个字符
高效的流,一次一个字符数组(推荐)
高效的流,一次一行字符串 (推荐)
字节流4种,字节流可以复制任意类型的文件,万能流
基本的流,一次一个字节
基本的流,一次一个字节数组
高效的流,一个一次字节
高效的流,一次一个字节数组(推荐)
九、综合测试
案例1:把ArrayList集合中的字符串数据存储到文本文件中。
分析:
1、创建ArrayList集合对象,
2、向集合中添加字符数据
3、创建一个字符输出流对象BufferedWriter
4、遍历获取集合中每一个元素
5、把每个元素写到文件中
6、关闭流
public class CollectionToFile{
public static void main(String[] args) throws IOException {
//1、创建集合对象
ArrayList<String>list =new ArrayList<String>();
//2、向集合中添加数据
list.add("javaEE");
list.add("云计算");
list.add("大数据");
//3、创建字符输出流对象
BufferedWriterbw = new BufferedWriter(new FileWriter("d:\\lee\\111.java"));
//4、遍历集合
Iterator<String> it = list.iterator();
while (it.hasNext()) {
//5、获取每一个元素,把元素写到文件中
String str = it.next();
bw.write(str);//写到文件中
bw.newLine();//换行
bw.flush();//刷新
}
//6、关闭流
bw.close();
}
}
案例2:把文本文件中的数据存储到Arraylist集合中。
分析:
1:创建字符输入流BufferedReader,用来读取数据
2:创建ArrayList集合,用来存储数据
3:读取File中的数据
4:把数据添加到集合
5:遍历集合中的数据
6:关闭流
public class FileToCollection{
public static void main(String[] args) throws IOException {
//1:创建字符输入流BufferedReader,用来读取数据
BufferedReaderbr = new BufferedReader(new FileReader("arraylist.txt"));
//2:创建ArrayList集合,用来存储数据
ArrayList<String>list =new ArrayList<String>();
//3:读取File中的数据
//先定义一个变量用来保存读取到的数据,默认为null表示文件为空。
String line = null;
while ((line =br.readLine()) != null) {
//4:把读到的一行数据添加到集合
list.add(line);
}
//5:遍历集合中的数据
for (Strings : list) {
System.out.println(s);
}
//6:关闭流
br.close();
}
}
案例3:复制指定目录下指定后缀名的文件并修改名称
public class CopyFlterFile {
public static void main(String[] args) throws IOException {
//1:封装数据源,指定数据源路径
File srcPath = new File("d:\\lee\\");
//2:封装目的地,指定目的地路径
File destPath = new File("c:\\lee1\\");
if (!destPath.exists()) {//如果目的地文件夹不存在
//创建目的地文件夹
destPath.mkdir();
}
//3: 获取数据源中到所有的.java文件,
//文件名过滤器,先判断是否是文件,再判断是否是以".java"结尾的文件
File[] files = srcPath.listFiles(new FilenameFilter(){
@Override//accept承认,接受
public boolean accept(File dir, Stringname) {
//File file = new File(dir, name);//创建file对象
//boolean flag = file.isFile();//判断是否是文件
//boolean flag2 = name.endsWith(".java");
//判断是否是java文件。
//return flag && flag2;
return new File(dir, name).isFile() && name.endsWith(".java");
}
});
//4:遍历,得到每一个java文件
for (Filefile : files) {
//5:重命名
String name = file.getName().replaceAll("\\.java$", ".txt");//调用String类中的方法
//6:复制文件
//先创建目的地对象
File dest = new File(destPath,name);//目的地路径,文件名
copyFile(file,dest);//复制文件
}
}
//文件的复制
public static void copyFile(File src, File dest) throws IOException {
//数据源
BufferedInputStreambis =new BufferedInputStream(new FileInputStream(src));
//目的地
BufferedOutputStreambos =new BufferedOutputStream(new FileOutputStream(dest));
//读
byte[]buffer = newbyte[1024];
int len = -1;
while ((len =bis.read(buffer)) != -1) {
//写
bos.write(buffer, 0,len);
}
//关闭流
bis.close();
bos.close();
}
}
案例4:复制多层文件夹,用递归方法实现。
分析:
1:封装数据源
2:封装目的地
3:创建目的地文件夹
4:获取到数据源中所有的File对象
5:遍历,获取到每一个File对象
6:判断当前File对象是否为文件夹
是:
文件夹:递归操作,封装好目的地,回到步骤3
否:
文件:得到新文件的名称[File对象],开始复制文件
public class CopyDirs {
public static void main(String[] args) throws IOException {
//1:封装数据源
File srcPath = new File("d:\\lee");
//2:封装目的地
File destPath = new File("c:\\lee");
//3:复制文件夹
copyDirectorys(srcPath,destPath);//数据源,目的地
}
//复制文件夹
public static voidcopyDirectorys(FilesrcPath, File destPath)
throws IOException {
//3:创建目的地文件夹
destPath.mkdir();
//4:获取到数据源中所有的File对象
File[] files = srcPath.listFiles();
//5:遍历,获取到每一个File对象
for (Filefile : files) {
//6:判断当前File对象是否为文件夹
if (file.isDirectory()) {
//文件夹
Stringname = file.getName();
File dest = new File(destPath,name);
//递归,封装好目的地,回到步骤3
copyDirectorys(file,dest);
} else {
//文件
//得到新文件的名称[File对象]
Stringname = file.getName();
File dest = new File(destPath,name);
//开始复制文件
copyFile(file,dest);
}
}
}
//复制文件
public static void copyFile(File src, File dest) throws IOException {
//数据源
BufferedInputStreambis =new BufferedInputStream( new FileInputStream(src));
//目的地
BufferedOutputStreambos =new BufferedOutputStream( new FileOutputStream(dest));
//读
byte[]buffer = newbyte[1024];
int len = -1;
while ((len =bis.read(buffer)) != -1) {
//写
bos.write(buffer, 0,len);
}
//关闭流
bis.close();
bos.close();
}
}
十、LineNumberReader类
简介:高效字符输入流。跟踪行号的缓冲字符输入流。从JDK1.1开始
构造方法:
public LineNumberReader(Reader in)
//使用默认输入缓冲区的大小创建新的行编号 reader。
public LineNumberReader(Reader in, int sz)
//创建新的行编号reader,将字符读入给定大小的缓冲区。
常用方法:
public int getLineNumber()获得当前行号。
public void setLineNumber(int lineNumber)设置当前行号。
测试案例:
public classLineNumberReaderDemo {
public static void main(String[] args) throws IOException {
//创建流对象
LineNumberReaderlnr = new LineNumberReader( new FileReader("d:\\lee\\1111.java"));
//lnr.setLineNumber(0);//从指定值+1开始
//读数据
String line = null;
while ((line =lnr.readLine()) != null) {
intlineNumber = lnr.getLineNumber();
System.out.println(lineNumber +": "+line);//在控制塔打印输出结果
}
//关闭流
lnr.close();
}
}