1.使用指定码表读写字符
FileReader是使用默认码表读取文件,如果需要使用指定码表读取,那么可以使用InputStreamReader(字节流,编码表)
FileWriter是使用默认码表写出文件,如果需要使用指定码表写出,那么可以使用OutputStreamReader(字节流,编码表)
public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("aaa.txt"), "utf-8")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("bbb.txt"),"gbk")); int c; while((c=br.read())!=-1) { bw.write(c); } br.close(); bw.close(); }
画图分析一下转换流
练习题:IO流和集合的配合使用
获取一个文本上每个字符的出现的次数,并将结果写在times.txt上
public class Test006 { /* * 获取一个文本上每个字符的出现的次数,并将结果写在times.txt上 * * 分析: * 1.创建输入流对象,带缓冲的效率会高一点 * 2.创建双列集合对象,如果想排序的话可以用TreeMap * 3.将读到的字符存储在双列集合中(字符作键,次数作值), * 存储的时候做判断,如果不包含这个字符,就将键和1存储; * 如果包含这个字符,就将键和值+1存储 * 4.关闭输入流 * 5.创建输出流对象 * 6.遍历集合,将集合中的内容写到times.txt中 * 7.关闭输出流 */ public static void main(String[] args) throws IOException { //1.创建输入流对象 BufferedReader br = new BufferedReader(new FileReader("aaa.txt")); //2.创建双列集合对象,如果想排序的话可以用TreeMap TreeMap<Character,Integer> tm = new TreeMap<>(); //3.将读到的字符存储在双列集合中(字符作键,次数作值),存储的时候做判断,如果不包含这个字符,就将键和1存储; //如果包含这个字符,就将键和值+1存储 int ch; while((ch=br.read())!=-1) { char c = (char)ch; if(!tm.containsKey(c)) { tm.put(c, 1); }else { tm.put(c, tm.get(c)+1); } } //4.关闭输入流 br.close(); //5.创建输出流对象 BufferedWriter bw = new BufferedWriter(new FileWriter("times.txt")); //6.遍历集合,将集合中的内容写到times.txt中 for (Character key : tm.keySet()) { switch (key) { case '\t': bw.write("\\t"+"="+tm.get(key)); break; case '\n': bw.write("\\n"+"="+tm.get(key)); break; case '\r': bw.write("\\r"+"="+tm.get(key)); break; default: bw.write(key+"="+tm.get(key)); //写出键和值 break; } bw.newLine(); } //7.关闭输出流 bw.close(); } }
2.补充前面的File
递归:简单说就是方法自己调用自己
弊端:不能调用次数过多,容易造成栈内存溢出
好处:不必知道循环次数
构造方法不能使用递归调用
练习:从键盘输入一个文件夹路径,打印出该文件夹下的所有.Java文件
package com.zixue.eclipse; import java.io.File; import java.util.Scanner; public class Test007 { /* * 从键盘输入一个文件夹路径,打印出该文件夹下的所有.Java文件 * 分析: * 从键盘接受一个文件夹路径 * 1.如果录入的路径不存在或者不是文件夹而是文件,提示并重新录入 * 2.如果是文件夹路径,返回该路径 * * 打印出该文件夹下的所有.Java文件 * 1.获取到该文件夹路径下的所有文件和文件夹,存储在File数组中 * 2.遍历数组,对每一个文件或文件夹做判断 * 3.如果是文件,并且以.Java结尾的就打印 * 4.如果是文件夹就递归调用 * */ public static void main(String[] args) { File dir = getDir(); printJava(dir); } /* * 获取从键盘输入一个文件夹路径 */ public static File getDir() { Scanner sc = new Scanner(System.in); while(true) { String line = sc.nextLine(); File dir = new File(line); if(!dir.exists()) { System.out.println("您录入的文件夹路径不存在,请重试"); }else if(dir.isFile()) { System.out.println("您录入的是一个文件路径,不是文件夹路径,请重试"); }else { return dir; } } } /* * 打印出该文件夹下的所有.Java文件 */ public static void printJava(File dir) { //获取到该文件夹路径下的所有文件和文件夹,存储在File数组中 File[] subFiles = dir.listFiles(); //遍历数组,对每一个文件或文件夹做判断 for (File file : subFiles) { //3.如果是文件,并且以.Java结尾的就打印 if(file.isFile()&&file.getName().endsWith(".java")) { System.out.println(file); }else if(file.isDirectory()){ printJava(file); } } } }
3.序列流
序列流可以把多个字节输入流整合成一个,从序列流中读取到数据时,将从被整合的第一个流开始,读完一个之后继续读取第二个,一次类推.
构造方法:暂时先学这个SequenceInputStream(InputStream s1, InputStream s2)
package com.zixue.eclipse; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; public class Test009 { public static void main(String[] args) throws IOException { FileInputStream fis1 = new FileInputStream("a.txt"); FileInputStream fis2 = new FileInputStream("b.txt"); SequenceInputStream sis = new SequenceInputStream(fis1, fis2);//对两个字节输入流进行包装 FileOutputStream fos = new FileOutputStream("c.txt"); int b; while((b = sis.read()) != -1) { fos.write(b); } sis.close(); //sis在关闭的时候,会将构造方法中传入的流对象都关闭 fos.close(); } }
上面的例子是整合两个输入流,那要是整合多个输入流如何做呢?
用到第二个构造方法SequenceInputStream(Enumeration<? extends InputStream> e)---接收一个枚举
在集合当中学Vector的遍历时接触过枚举,下面看代码
package com.zixue.eclipse; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.util.Enumeration; import java.util.Vector; public class Test010 { public static void main(String[] args) throws IOException { FileInputStream fis1 = new FileInputStream("a.txt"); FileInputStream fis2 = new FileInputStream("b.txt"); FileInputStream fis3 = new FileInputStream("c.txt"); //创建集合对象,将流对象加进来 Vector<FileInputStream> v = new Vector<>(); v.add(fis1); v.add(fis2); v.add(fis3); //得到枚举 Enumeration<FileInputStream> en = v.elements(); //创建序列流对象,将枚举中的输入流整合成一个 SequenceInputStream sis = new SequenceInputStream(en); FileOutputStream fos = new FileOutputStream("d.txt"); int b ; while((b=sis.read())!=-1) { fos.write(b); } sis.close(); fos.close(); } }
4.内存輸出流
该输出流可以向内存中写数据.把内存当作一个缓冲区,写出之后可以一次性获取出所有数据
使用方式:创建对象 new ByteArrayOutputStream()
写出数据 write(int) ,write(byte[])
获取数据 toByteArray()
package com.zixue.eclipse; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; public class Test011 { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("a.txt"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 在内存中创建了可以增长的内存数组 int b; while((b=fis.read())!=-1) { baos.write(b); //将读取到的数据逐个写到内存中 } byte [] arr = baos.toByteArray(); //将缓冲区的数据全部获取出来,并赋值给arr System.out.println(new String(arr)); // System.out.println(baos); 如果是默认的编码表,可以用这个简便的写法 fis.close(); } }
5.随机访问流
RandomAccessFile类不属于流,是Object的子类,但它融合了InputStream和OutputStream的功能.支持对随机访问文件的读取和写入
常用方法read()..write()..seek()
构造方法RandomAccessFile(File file,String mode)
RandomAccessFile(String name,String mode)
mode参数指定用以打开文件的访问模式
"r"---以只读方式打开,调用结果对象的任何write方法都将导致抛出IOException
"rw"---打开以便读取和写入,如果文件不存在,则尝试创建该文件
"rws"---打开以便读取和写入,对于"rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备
"rwd"---打开以便读取和写入,对于"rw",还要求对文件的内容的每个更新都同步写入到底层存储设备
package com.zixue.eclipse; import java.io.IOException; import java.io.RandomAccessFile; public class Test_RandomAccessFile { public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile("a.txt", "rw"); //raf.write(97); //int x = raf.read(); raf.seek(0); //在指定位置设置指针 raf.write(98); raf.close(); } }