I/O的学习之字符流
今天的学习内容
字符流FileReader
字符流FileWriter
字符流的拷贝
-
带缓冲的字符流
BufferedReader中有一个特有的方法:readLine();返回类型为String
BufferedWriter中有一个特有的方法:newLine();换行
-
IO流(将文本反转)
- 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
IO流(LineNumberReader)
IO流(装饰设计模式)
一、IO流(字符流FileReader)
-
字符流是什么
字符流是可以直接读写字符的IO流
字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
-
FileReader
- FileReader类的read()方法可以按照字符大小读取
-
程序
public static void main(String[] args) throws IOException{ FileReader fReader = null; try{ fReader = new FileReader(new File("E:/test/test2.txt")); int len = 0; //char[] cArr = new char[1024]; while((len = fReader.read()) != -1) { //通过码表一次读取一个字符 System.out.print((char)len); } }finally { if(fReader != null) { fReader.close(); } } } /* 输出test2.txt中内容:我爱你 */
二、IO流(字符流FileWriter)
FileWriter类的write()方法可以自动把字符转为字节写出
-
程序
public static void main(String[] args) throws IOException{ FileWriter fWriter = null; try { //创建流对象 fWriter = new FileWriter("E:/test/test2.txt",true); //将内容吸写入文件 fWriter.write("我爱你"); }finally { if(fWriter != null) { fWriter.close(); } } }
三、IO流(字符流的拷贝)
利用FileReader和FileWriter进行文件拷贝
-
程序
public static void main(String[] args) throws IOException{ FileReader fReader = null; FileWriter fWriter = null; try { fReader = new FileReader("E:/test/test2.txt"); fWriter = new FileWriter("E:/test/test3.txt"); int len = 0; //char[] cArr = new char[1024 * 8]; while((len = fReader.read()) != -1) { fWriter.write(len); //Writer类中有一个2k的缓冲区,不关流,则有内容滞留在缓冲区 } }finally { try { if(fReader != null) { fReader.close(); } }finally { fWriter.close(); //关流将缓冲区中内容写入文件 } } }
四、IO流(什么情况下使用字符流)
字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
-
程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
- 只读和只写的时候推荐使用字符流
读取的时候是按照字符的大小读取的,不会出现半个中文
写出的时候可以直接将字符串写出,不用转换为字节数组
-
字节流和字符流在拷贝文件时候的区别
五、IO流(字符流是否可以拷贝非纯文本的文件)
不可以拷贝非纯文本的文件
因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就
会用?代替,写出的时候会将字符转换成字节写出去(此时其实是字符流的工作原理的理解:读的时候将字节转换为字符,写的时候将字符转换为字节写出去)如果是?,直接写出,这样写出之后的文件就乱了,看不了了
六、IO流(自定义字符数组的拷贝)
-
程序
public static void main(String[] args) throws IOException{ FileReader fReader = null; FileWriter fWriter = null; try { fReader = new FileReader("E:/test/test2.txt"); fWriter = new FileWriter("E:/test/test3.txt"); //进行文件拷贝 int len = 0; char[] cArr = new char[1024 * 3]; while((len = fReader.read(cArr)) != -1) { //将文件中数据读到字符数组中 fWriter.write(cArr,0,len); //将字符数组中内容写出到文件 } }finally { try { if(fReader != null) { fReader.close(); } }finally { if(fWriter != null) { fWriter.close(); } } } }
七、IO流(带缓冲的字符流)
BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率
-
程序
public static void main(String[] args) throws IOException{ try( //创建流对象 BufferedReader bReader = new BufferedReader(new FileReader("E:/test/test2.txt")); BufferedWriter bWriter = new BufferedWriter(new FileWriter("E:/test/test3.txt")); ){ //进行文件拷贝 int len = 0; while((len = bReader.read()) != -1) { //将文件中内容写到缓冲区 bWriter.write(len); //将缓冲区内容写入到文件 } } }
八、IO流(readLine()和newLine()方法)——BufferedReader和BufferedWriter特有方法
BufferedReader的readLine()方法可以读取一行字符(不包含换行符号)
BufferedWriter的newLine()可以输出一个跨平台的换行符号"\r\n"
-
readLine方法测试
public static void main(String[] args) throws IOException{ BufferedReader bReader = null; try { bReader = new BufferedReader(new FileReader("E:/test/test2.txt")); //按行读文件并输出 String line; while((line = bReader.readLine()) != null) { System.out.println(line); } }finally { //关闭流 if(bReader != null) { bReader.close(); } } }
-
newLine()方法测试
public static void main(String[] args) throws IOException{ BufferedWriter bWriter = null; try { bWriter = new BufferedWriter(new FileWriter("E:/test/test.txt")); //测试nextLine()方法 bWriter.write("哈哈哈,我要换行了"); bWriter.newLine(); bWriter.write("换行成功了"); }finally { if(bWriter != null) { bWriter.close(); } } }
九、IO流(将文本反转)
将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
-
分析:
创建文件输入输出流对象BufferedReader和BufferedWriter
创建LinkedList等待存放每一行
BufferedReader获取每一行
将每一行存入Linkedlist集合中
将每一行从LinkedList中倒序取出,写入文件中
-
程序
public static void main(String[] args) throws IOException{ //1. 创建文件输入输出流对象BufferedReader和BufferedWriter BufferedReader bReader = null; BufferedWriter bWriter = null; try{ bReader = new BufferedReader(new FileReader("E:/test/test2.txt")); //2. 创建LinkedList等待存放每一行 ArrayList<String> list = new ArrayList<>(); //3. BufferedReader获取每一行 String line; while((line = bReader.readLine()) != null) { //4. 将每一行存入Linkedlist集合中 list.add(line); } bReader.close(); bWriter = new BufferedWriter(new FileWriter("E:/test/test3.txt")); //5. 将每一行从LinkedList中倒序取出,写入文件中 for(int i = list.size() - 1;i >= 0;i--) { bWriter.write(list.get(i)); bWriter.newLine(); } }finally { try { if(bReader != null) { bReader.close(); } }finally { if(bWriter != null) { bWriter.close(); } } } }
十、IO流(LineNumberReader)
-
LineNumberReader简述
LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号,LineNumberReader属性域中有一个成员变量lineNumber,初始化为0,调用一次readLine方法就+1一次。
This class defines methods setLineNumber(int) and getLineNumber() for setting and getting the current line number respectively.
By default, line numbering begins at 0. This number increments at every line terminator as the data is read, and can be changed with a call to setLineNumber(int).(默认行号从0开始,每read一次行号就增加1)
-
调用getLineNumber()方法可以获取当前行号
- public int getLineNumber()
Get the current line number.
- public int getLineNumber()
-
调用setLineNumber()方法可以设置当前行号
- public void setLineNumber(int lineNumber)
Set the current line number.
- public void setLineNumber(int lineNumber)
-
程序
public class TestLineNumberReader { public static void main(String[] args) throws IOException{ LineNumberReader lNumberReader = null; try { lNumberReader = new LineNumberReader(new FileReader("E:/test/test2.txt")); String line; lNumberReader.setLineNumber(100); while((line = lNumberReader.readLine()) != null) { //read一次行号就+1 System.out.println(lNumberReader.getLineNumber() + " : " + line); } }finally { if(lNumberReader != null) { lNumberReader.close(); } } } } /* 在JDK1.8中输出结果为: ----------------- 101 : 哈哈哈我爱你 102 : 嘻嘻嘻 103 : 嘻嘻哈哈 104 : 哈哈嘻嘻 105 : 我在写几行 106 : 啊哈哈哈哈哈哈哈 ----------------- * */
十一、IO流(装饰设计模式)
-
定义一个超类/接口Coder
public interface Coder { void code(); }
-
定义被装饰类学生——继承接口Coder
public class Student implements Coder{ @Override public void code() { System.out.println("学生会敲代码"); } }
-
定义装饰者类GoodStudent——继承接口Coder并接受学生类引用
public class GoodStudent implements Coder{ private Student s; //获取被装饰类的引用 GoodStudent(Student s){ //在构造方法中传入被装饰类的引用 this.s = s; } @Override public void code() { //对原有功能进行升级 s.code(); System.out.println("好学生敲好代码"); } }
-
继承于装饰者模式的关系
装饰者模式中用到了继承,但是这里装饰者模式用继承是为了保证装饰者和被装饰者有共同的超类,而不是为了给被装饰的类扩展新的特性
而装饰者模式中新的特性是通过类与类的组合(has-a的关系)而得到的,所以把装饰者模式和继承看做同一类设计思想是不恰当的。
装饰者模式减少了类与类之间的耦合性,当改动被装饰类时不影响装饰者类
一个被装饰类可选择多了装饰者,而继承的耦合性太高
十二、IO流(使用指定的码表读写字符)
-
基础概念
FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)
FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)
-
程序
//高效的用指定的编码表读 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8")); //高效的用指定的编码表写 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK")); int ch; while((ch = br.read()) != -1) { bw.write(ch); } br.close(); bw.close();
字节流来读?。悍乐孤衣?/p>
用指定的编码表是为了将字节正常的转换为字符
-
在实际开发中编码方式
GBK 国家编码 包含简繁体中文
GB2312 国家编码 只包含简体中文
ISO-8859-1 国际编码 世界上大多数国家使用的是字母 只包含字母
Unicode 十六进制的编码 描述所有的文字信息
UTF 象形文字 字母采用ISO-8859-1的编码
十三、IO流(转换流图解)
-
两个转换流
InputStreamReader
OutputStreamWriter
转换流可指定编码类型,避免读取和写入时的乱码
-
转换流图解
-
InputStreamReader(字节流,编码表)字节通向字符的桥梁
- 通过指定的编码表将字节转化为字符
-
OutputStreamWriter(字节流,编码表)字节通向字符的桥梁
- 通过指定的编码表将字符转化为字节
-
十四、IO流(获取文本上字符出现的次数)
获取一个文本上每个字符出现的次数,将结果写在times.txt上
-
分析
创建BufferedReader输入流读取文本中的内容
将读取到的每一个字符存到HashMap中
注意:若key重复,则增加value;若不重复,则value = 1;
遍历输出HashMap键值对
将键值对结果写入到times.txt中
关闭流
注意异常的处理
-
代码
public static void main(String[] args) throws IOException{ //1. 创建BufferedReader输入流读取文本中的内容 BufferedReader bReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("E:/test/test2.txt")),"GBK")); HashMap<Character, Integer> hashMap = new HashMap<>(); /*2. 将读取到的每一个字符存到HashMap中 3. 注意:若key重复,则增加value;若不重复,则value = 1;*/ int b = 0; while((b = bReader.read()) != -1) { char ch = ((char)b); if(hashMap.containsKey(ch)) { hashMap.put(ch, hashMap.get(ch) + 1); }else { hashMap.put(ch, 1); } } bReader.close(); //创建输出流 BufferedWriter bWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("E:/test/times.txt")))); //4. 遍历输出HashMap键值对 //5. 将键值对结果写入到times.txt中 for(Character character : hashMap.keySet()){ char key = character; switch (key) { case '\n': bWriter.write("\\n" + " = " + hashMap.get(key)); break; case '\t': bWriter.write("\\t" + " = " + hashMap.get(key)); break; case '\r': bWriter.write("\\r" + " = " + hashMap.get(key)); break; default: bWriter.write(key + " = " + hashMap.get(key)); break; } bWriter.newLine(); } bWriter.close(); }
十五、IO流(试用版软件)
当我们下载一个试用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少次使用机会用学过的IO流知识,模拟试用版软件,试用10次机会,执行一次就提示一次您还有几次机会,如果次数到了提示请购买正版
-
分析
-
由于读取数字的原因,不可hi借用字节流和字符流,在这儿我们选择BufferedReader中的特殊方法readLine()读取整行
创建带缓冲的输入流对象
将读到的字符串转化为int
若int值大于0,则返回int-1,若不大于0,则提示购买正版
if判断中要将--结果打印,并输出到文件中
-
-
代码
public static void main(String[] args) throws IOException{ //1. 创建带缓冲的输入流对象 BufferedReader bReader = new BufferedReader(new FileReader(new File("Conf.txt"))); String line = bReader.readLine(); bReader.close(); //2. 将读到的字符串转化为int int times = Integer.parseInt(line); //3. 若int值大于0,则返回int-1,若不大于0,则提示购买正版 if(times > 0) { System.out.println("您还有" + times-- + "次机会"); FileWriter fWriter = new FileWriter("Conf.txt"); //会先清空Conf.txt中的内容 fWriter.write(times + ""); fWriter.close(); //一定要关流,内容在缓冲区里,并没有写入到文件 }else { System.out.println("您的试用次数已到,请购买正版"); } }
十六、(递归)
-
5的阶乘
public class TestRecursion { public static void main(String[] args) { System.out.println(fun(5)); } private static int fun(int i) { // TODO Auto-generated method stub if(i == 1){ return 1; }else { return i * fun(i-1); } } }
-
递归的弊端
- 不能调用太多次,容易导致栈溢出
-
递归与循坏相比的好处
- 不用知道循坏次数
递归调用是否必须有返回值
十七、File类(练习)
需求:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.md文件名
-
程序
public static void main(String[] args) { //获取键盘输入 Scanner input = new Scanner(System.in); System.out.println("请输入文件夹路径"); String string; File file; while(true) { string = input.nextLine(); file = new File(string); if(file.isDirectory()) break; else System.out.println("您输入的目录不存在,请重新输入"); } //递归遍历所有的.md文件 doFind(file); } private static void doFind(File file) { // TODO Auto-generated method stub if(file.isDirectory()) { File[] files = file.listFiles(); if(files != null) { for(int i = 0;i < files.length;i++) doFind(files[i]); } }else { if(file.getName().contains(".md")) { System.out.println(file); } } }
十八、File类(练习)
需求:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名
-
程序
public static void main(String[] args) { File file = getDir(); doFind(file); } private static void doFind(File dir) { // TODO Auto-generated method stub File[] files = dir.listFiles(); for(File subFile : files) { if(subFile.isFile() && subFile.getName().endsWith(".md")) { System.out.println(subFile); }else if(subFile.isDirectory()){ doFind(subFile); } } } private static File getDir() { // TODO Auto-generated method stub //判断输入的合法性 Scanner input = new Scanner(System.in); System.out.println("请输入文件夹路径:"); String string = null; File file = null; while(true) { string = input.nextLine(); file = new File(string); if(!file.exists()) { System.out.println("您输入的路径不存在,请重新输入"); }else if(file.isFile()) { System.out.println("您输入的是文件路径,请重新输入文件夹路径"); }else { return file; } } }