Java I/O流 03
I/O流·字符流
字符流FileReader
* A:字符流是什么
* 字符流是可以直接读写字符的 IO流
* 字符流读取字符,就要先读取到字节数据,然后转换为字符;如果要写出字符,需要把字符转换为字节再写出
* B:FileReader
* FileReader类的read() 方法可以按照字符大小读取
package com.heima.chario; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class Demo1_FileReader { public static void main(String[] args) throws IOException { // demo1(); // demo2(); } public static void demo2() throws FileNotFoundException, IOException { FileReader fr = new FileReader("xxx.txt"); int c; // 默认返回的是int类型 while ((c = fr.read()) != -1) { // 通过项目默认的码表,一次读取一个字符 System.out.println((char) c); // 强转为char类型 } fr.close(); } public static void demo1() throws FileNotFoundException, IOException { FileReader fr = new FileReader("xxx.txt"); // 创建字符输入流 char c = (char) fr.read(); // 读取一个字符并强转为char类型 System.out.println(c); fr.close(); // 关流 } }
字符流FileWriter
* A:FileWriter类的 write()方法可以自动把字符转为字节写出
package com.heima.chario; import java.io.FileWriter; public class Demo2_FileWriter { public static void main(String[] args) throws Exception { FileWriter fw = new FileWriter("yyy.txt"); // 创建字符输出流 fw.write("大家好,基础班快接近尾声了,大家要努力啊!!"); // 写入字符串,底层通过编码表翻译后用字节写入 fw.write(97); fw.close(); // 关流 } }
什么情况下使用字符流
* 字符流也可以拷贝文本文件,但不推荐是同,因为读取时会把字节转为字符,写入时会把字符转为字节
* 程序如果只需要读取一段文本或者只需要写出一段文本的时候,可以使用字符流
* 因为读取的时候是按照字符的大小读取的,不会出现半个中文的情况
* 写入的是红可以直接将字符串写出,不用转换为字节
* 字符流不可以拷贝非纯文本文件
* 因为再读的时候会将字节转换为字符,在转换的过程中可能找不到对应的字符,就会用?代替
* 如果读取为?,写出时也是?的字节,这样文件的乱码了
用字符流拷贝文件
* A:普通字符流拷贝
* B:带缓冲的字符流
* BufferedReader的 read()方法读取字符时会一次读取若干个字符到缓冲区,然后逐个返回给程序,降低读取文件的次数,提高效率
* BufferedWriter的 write()方法写出字符时会先写到缓冲区,缓冲区写满后才会写到文件,降低写文件的次数,提高效率
package com.heima.chario; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Demo3_Copy { public static void main(String[] args) throws IOException { // demo1(); // demo2(); // demo3(); // demo4(); } public static void demo4() throws FileNotFoundException, IOException { BufferedReader br = new BufferedReader(new FileReader("xxx.txt")); // 创建带缓冲的字符输入流,关联普通字符输入流 BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt")); int c; while ((c = br.read())!= -1) { bw.write(c); } br.close(); bw.close(); } public static void demo3() throws FileNotFoundException, IOException { FileReader fr = new FileReader("xxx.txt"); // 创建字符输入流 FileWriter fw = new FileWriter("yyy.txt"); // 创建字符输出流 char[] arr = new char[1024 * 8]; // 创建字符数组接收数据 int len; while ((len = fr.read(arr)) != -1) { // 将文件上的数据读取到字符数组中 fw.write(arr, 0, len); // 将字符数组中的数据写到文件上 } fr.close(); fw.close(); } public static void demo2() throws FileNotFoundException, IOException { // 字符流不能拷贝非纯文本文件 FileReader fr = new FileReader("亲爱的小孩.mp3"); FileWriter fw = new FileWriter("copy1.mp3"); int c; while ((c = fr.read()) != -1) { fw.write(c); } fr.close(); fw.close(); } public static void demo1() throws FileNotFoundException, IOException { FileReader fr = new FileReader("xxx.txt"); // 创建字符输入流 FileWriter fw = new FileWriter("zzz.txt"); // 创建字符输出流 int c; // 接收每一个字符 while ((c = fr.read()) != -1) { fw.write(c); // 将读取到的内容写入文件 } fr.close(); // 关流 fw.close(); // Writer类中有一个2k的小缓冲区,程序会将内容写到缓冲区内,关流会将缓冲区内的内容刷新到硬盘上 } }
readLine() 和 newLine() 方法
* BufferedReader的 readLine()方法可以读取一行字符(不包含换行符)
* BufferedWriter的 newLine()方法可以输出一个跨平台的换行符
package com.heima.chario; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Demo4_Buffered { /* * 带缓冲区的流中的特殊方法: * readLine():逐行读取 * newLine():写出回车换行符 */ public static void main(String[] args) throws IOException { // demo1(); // demo2(); } public static void demo2() throws FileNotFoundException, IOException { BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); // 创建带缓冲的字符流 BufferedWriter bw = new BufferedWriter(new FileWriter("aaa.txt")); String lineString; while ((lineString = br.readLine()) != null) { bw.write(lineString); //bw.write("\r\n"); bw.newLine(); // newLine 和 \r\n 的区别是:前者是跨平台的,而后者仅在windows代表换行 } br.close(); // 关流 bw.close(); } public static void demo1() throws FileNotFoundException, IOException { BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); // 创建带缓冲的字符流 String lineString; while ((lineString = br.readLine()) != null) { // 逐行读取 System.out.println(lineString); } br.close(); // 关流 } }
将文本反转
* 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
package com.heima.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; public class Test1 { /* * 分析: * 1、创建输入输出流对象 * 2、创建集合对象,把数据进行临时存储 * 3、将读到的数据存储在集合中 * 4、倒着遍历集合,将数据写到文件上 * 5、关流 * * 注意事项:流对象尽量晚开早关 */ public static void main(String[] args) throws IOException { ArrayList<String> list = new ArrayList<String>(); // 创建空列表 String lineString; BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); // 创建带缓冲的字符流 while ((lineString = br.readLine()) != null) { // 从文件中逐行读取 list.add(lineString); // 逐行存入列表中 } br.close(); // 关流1 //Collections.reverse(list); // 也可以调用集合工具类先反转,再正着遍历 BufferedWriter bw = new BufferedWriter(new FileWriter("revzzz.txt")); for (int i = list.size() - 1; i >= 0; i--) { // 倒着遍历列表 bw.write(list.get(i)); // 通过索引获取列表元素并写入文件中 bw.newLine(); // 换行 } bw.close(); // 关流2 } }
LineNumberReader
* LineNumberReader是BufferedReader的子类,具有相同的功能,并且可以统计行号
* 调用 getLineNumber()方法可以获取当前行号
* 调用 setLineNumber()方法可以设置当前行号
package com.heima.chario; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; public class Demo5_LineNumberReader { public static void main(String[] args) throws IOException { LineNumberReader lnr = new LineNumberReader(new FileReader("zzz.txt")); String lineString; lnr.setLineNumber(100); // 设置初始的行号,默认为0,每readLine一次,行号就加一 while ((lineString = lnr.readLine())!=null) { System.out.println(lnr.getLineNumber() + ":" + lineString); } lnr.close(); } }
装饰设计模式
package com.heima.chario; public class Demo6_Wrap { /* * 装饰设计模式的好处是: * 耦合性低,被装饰类的变化与装饰类的变化无关 */ public static void main(String[] args) { HeiMaStudent hms = new HeiMaStudent(new Student()); hms.code(); } } interface Coder { public void code(); // 定义抽象方法 } class Student implements Coder { @Override public void code() { System.out.println("javase"); System.out.println("javaweb"); } } class HeiMaStudent implements Coder { // 目标是装饰Student类,对其进行强化 // 获取被装饰类的引用 private Student student; // 获取学生引用 // 在构造方法中传入被装饰类的对象 public HeiMaStudent(Student s) { this.student = s; } // 对原有的功能进行升级 @Override public void code() { student.code(); System.out.println("ssh"); System.out.println("数据库"); System.out.println("hadoop"); System.out.println("..."); } }
使用指定的码表读写字符
* FileReader是使用默认码表读取文件,如果需要使用指定码表读取,那么可以使用InputStreamReader(字节流,编码表)
* FileWriter是使用默认码表写文件,如果需要使用指定码表写出,那么可以使用OutputStreamReader(字节流,编码表)
package com.heima.chario; import java.io.*; public class Demo7_TransIO { public static void main(String[] args) throws IOException { // demo1(); // demo2(); // demo3(); } public static void demo3() throws UnsupportedEncodingException, FileNotFoundException, IOException { // 采用带缓冲的字符流,进行更高效的读写,装饰设计模式的思想 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 len; while ((len = br.read())!= -1) { // 使用内置的缓冲区读取数据 bw.write(len); } br.close(); // 关流 bw.close(); } public static void demo2() throws UnsupportedEncodingException, FileNotFoundException, IOException { // 指定码表读和写字符 InputStreamReader isr = new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8"); // 创建字符转换流,关联字节流 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk"); // 编码形式可以省略,省略默认为项目的编码表 int c; while ((c = isr.read())!= -1) { // 逐个字符读取 osw.write(c); } isr.close(); osw.close(); } public static void demo1() throws FileNotFoundException, IOException { // 用默认编码表读写,出现乱码 FileReader fr = new FileReader("utf-8.txt"); // 内容由utf-8编码 FileWriter fw = new FileWriter("gbk.txt"); // 内容由gbk编码 int c; while ((c = fr.read()) != -1) { fw.write(c); } fr.close(); fw.close(); } }
获取文本上字符出现的次数
* 获取文本上每个字符出现的次数,将结果写在 times.txt上
package com.heima.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.TreeMap; public class Test2 { /* * 分析: * 1、创建带缓冲的输入流对象 * 2、创建双列集合对象TreeMap * 3、将读到的字符存储在双列集合中,存储的时候要做判断,如果不包含这个键,就将键和1存储,如果包含这个键,就将键和值+1存储 * 4、关闭输入流,创建输出流对象 * 5、遍历集合,将集合的内容写到times.txt中 * 6、关闭输出流 */ public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("zzz.txt")); //创建带缓冲的输入流对象 TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>(); // 创建双列集合 int c; while ((c = br.read())!= -1) { // 从文件中逐个读取字符 char ch = (char)c; // 向下强转 /*if (!tm.containsKey(ch)) { tm.put(ch, 1); }else { tm.put(ch, tm.get(ch)+1); }*/ tm.put(ch, !tm.containsKey(ch)? 1 : tm.get(ch)+1); // 将字符及其出现次数存储到双列集合中 } br.close(); // 关闭输入流 BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt")); // 创建输出流对象 for (Character key : tm.keySet()) { // 遍历键的集合,获取每个键 /*if (key == '\r') { bw.write("\\r" + "=" + tm.get(key)); bw.newLine(); } else if (key == '\n') { bw.write("\\n" + "=" + tm.get(key)); bw.newLine(); }else if (key == '\t') { bw.write("\\t" + "=" + tm.get(key)); bw.newLine(); }else { bw.write(key + "=" + tm.get(key)); bw.newLine(); }*/ switch (key) { // 将集合中的内容写到文件中 case '\t': { bw.write("\\t" + "=" + tm.get(key)); break; } case '\r': { bw.write("\\r" + "=" + tm.get(key)); break; } case '\n': { bw.write("\\n" + "=" + tm.get(key)); break; } default: bw.write(key + "=" + tm.get(key)); } bw.newLine(); // 输入换行 } bw.close(); // 关闭输出流 } }
试用版软件
* 当我们下载一个试用版软件时,每执行一次就会提醒我们还有多少次使用的机会
* 用IO知识,模拟试用版软件,试用10次,每执行一次, 提示一次还剩几次机会,如果次数到了,就提示:请购买正版
package com.heima.test; import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Test3 { /* * 分析: * 1、创建待缓冲的输入流对象,使用readLine()方法,保证数据的原样性 * 2、将读到的字符串转换为int数 * 3、对int数进行判断,如果大于0,就将其减一,并写回源文件;如果小于等于0,就提示"请购买正版软件" * */ public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("config.txt")); String line = br.readLine(); // 整行读取 int c = Integer.parseInt(line); // 将字符串型数字转换为int型数字 br.close(); // 关闭输入流 FileWriter fw = new FileWriter("config.txt"); // 会清空config.txt文件 if (c > 0) { System.out.println("您还有 " + c-- + "次机会"); fw.write(c + ""); // 打印字符串 } else { System.out.println("试用次数以耗尽,请购买正版"); fw.write(0 + ""); // 将0写回文件,否则config.txt就是空的了 } fw.close(); // 关闭输出流,如果忘记关流,数据在缓冲区内不会被刷过去 } }
递归
* 递归的弊端:不能调用太多次,容易导致栈溢出
* 递归的好处:不用知道循环次数
* 练习:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的 .java文件
package com.heima.test; import java.io.File; import java.util.Scanner; public class Test4 { /* * 分析: * 1、从键盘接收一个文件夹路径 * 如果录入的是不存在的,给提示 * 如果录入的是文件路径,也给提示 * 如果是文件夹路径,直接返回 * * 2、获取到该文件夹下的所有文件和文件夹,存储在一个file数组中 * * 3、遍历数组,对每一个文件或文件夹做判断, * 如果是文件,并且后缀是.java就打印; * 如果是文件夹,就递归调用 * * 注意:构造方法不能递归调用 */ public static void main(String[] args) { File dir = getDir(); // 获取文件夹路径 printJavaFile(dir); // 调用递归方法 } /* * 获取键盘录入的文件夹路径 * 1、返回值类型,File * 2、不需要参数 */ public static File getDir() { Scanner sc = new Scanner(System.in); // 创建键盘录入对象 System.out.println("请输入一个文件夹路径"); // 提示输入 while (true) { // 定义无限循环 String liString = sc.nextLine(); // 将键盘录入的文件夹路径存储 File dir = new File(liString); // 将录入的内容封装成File对象 if (!dir.exists()) { // 对File对象进行一系列判断 System.out.println("您录入的文件夹路径不存在,请重新录入"); } else if (dir.isFile()) { System.out.println("您录入的是文件路径,请您录入文件夹路径"); } else { sc.close(); return dir; // 返回File对象 } } } /* * 获取文件夹路径下的所有.java文件 * 1、返回值为void * 2、参数列表为File dir */ public static void printJavaFile(File dir) { // 传入文件夹路径 File[] subFiles = dir.listFiles(); // 获取该路径下所有的文件和文件夹 for (File subfile : subFiles) { // 遍历File数组 if (subfile.isFile() && subfile.getName().endsWith(".java")) { // 判断是否符合条件 System.out.println(subfile.getName()); // 如果符合,就打印 } else if (subfile.isDirectory()) { // 如果是文件夹 printJavaFile(subfile); // 递归调用 } } } }