Java语言基础-IO流(输入输出流) 字节流、转换流
IO流(输入输出流) 字节流
基本操作与字符流相同;
不仅可以操作字符,还可以操作其他媒体文件;
字节流不需要编解码
字节流基本操作示例:
package cn.itcast.p7.io.bytestream.demo; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ByteStreamDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // demo_write(); demo_read(); } public static void demo_read() throws IOException { // 1.创建一个读取流对象,和指定的文件相关联 FileInputStream fis = new FileInputStream("bytedemo.txt"); // byte[] buf=new byte[fis.available()];//慎用,大文件可能沾满内存 // fis.read(buf); // System.out.println(new String(buf)); // 建议使用该方法读取数据 byte[] buf = new byte[1024]; int len = 0; while ((len = fis.read(buf)) != -1) { System.out.println(new String(buf, 0, len)); } // 2.一次读取一个字节,无法读出中文 // int ch=fis.read(); // System.out.println(ch); // int ch = 0; // while ((ch = fis.read()) != -1) { // System.out.println((char)ch); // } fis.close(); } public static void demo_write() throws IOException { // 1.创建字节输出流对象,用于操作文件 FileOutputStream fos = new FileOutputStream("bytedemo.txt"); // 2.写数据。直接写入到了目的地中。 fos.write("abcdefg".getBytes()); fos.close();// 关闭资源动作需要完成 } }
复制一个文件示例:
package cn.itcast.p7.io.bytestream.test; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class CopyMp3Test { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // copy_1(); copy_2(); // copy_3(); // copy_4(); } // 读一个字节写一个字节,效率非常低。不要用 public static void copy_4() throws IOException { FileInputStream fis = new FileInputStream("C:\\0.mp3"); FileOutputStream fos = new FileOutputStream("C:\\4.mp3"); int ch = 0; while ((ch = fis.read()) != -1) { fos.write(ch); } fos.close(); fis.close(); } // 不建议使用 public static void copy_3() throws IOException { FileInputStream fis = new FileInputStream("C:\\0.mp3"); FileOutputStream fos = new FileOutputStream("C:\\3.mp3"); byte[] buf = new byte[fis.available()];// 文件过大时,会发生内存溢出 fis.read(buf); fos.write(buf); fos.close(); fis.close(); } public static void copy_2() throws IOException { FileInputStream fis = new FileInputStream("C:\\0.mp3"); BufferedInputStream bufis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream("C:\\2.mp3"); BufferedOutputStream bufos = new BufferedOutputStream(fos); int ch = 0; while ((ch = bufis.read()) != -1) {// 从缓冲区读取 bufos.write(ch); } bufos.close(); bufis.close(); } // 效率较高 public static void copy_1() throws IOException { FileInputStream fis = new FileInputStream("C:\\0.mp3"); FileOutputStream fos = new FileOutputStream("C:\\1.mp3"); byte[] buf = new byte[1024]; int len = 0; while ((len = fis.read(buf)) != -1) { fos.write(buf, 0, len); } fos.close(); fis.close(); } }
字符流复制文件※
读完字节数据后,并没有直接向目的地里写,需要查表。如果码表没有对应的字符,则会使用未知字符区代替未对应的字符。文件发生了变化,不是原文件的复制。
键盘录入示例:
package cn.itcast.p1.io.transtream.demo; import java.io.IOException; import java.io.InputStream; /* * 读取一个键盘录入的数据,并打印在控制台上 * * 键盘本身就是一个标准的输入设备,对于Java而言,对于这种输入设备都有对应的对象。 * */ public class ReadKey { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // readKey(); readKey2(); } public static void readKey2() throws IOException { /* * 获取用户键盘录入的数据, 并将数据变成大写显示在控制台上。 * 如果用户输入的是over,结束键盘录入。 * * 思路: * 1.因为键盘录入只读取一个字节,要判断是否是over,需要先将读到的字节拼成字符串; * 2.需要一个容器,StringBuilder; * 3.在用户回车之前,将录入的数据变成字符串即可。 */ // 1.创建容器 StringBuilder sb = new StringBuilder(); // 2.获取键盘读取流 InputStream in = System.in; // 3.定义变量记录读取到的字节,并循环获取 int ch = 0; while ((ch = in.read()) != -1) { // 在存储之前需要判断是否是换行标记,因为换行标记不存储 if (ch == '\r') continue; if (ch == '\n') { String temp = sb.toString(); if ("over".equals(temp)) break; System.out.println(temp.toUpperCase()); sb.delete(0, sb.length());//打印完后清空缓冲区 } else { // 不是回车、换行字符时,将该字符存储到StringBuilder中 sb.append((char) ch); } } } public static void readKey() throws IOException { InputStream in = System.in; // System.out.println((int)'\r');//13 // System.out.println((int)'\n');//10 int ch = in.read();// 阻塞式方法 System.out.println(ch); // in.close();//可以不关闭,随着系统的存在而存在;随着系统的关闭而关闭 } }
转换流
字节流+编解码表——>字符流
public class InputStreamReader extends Reader
※InputStreamReader 是字节流通向字符流的桥梁:
它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
public class OutputStreamWriter extends Writer
※OutputStreamWriter 是字符流通向字节流的桥梁:
可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
package cn.itcast.p1.io.transtream.demo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; public class TranStreamDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // 字节流 InputStream in = System.in; // 将字节流转成字符流的桥梁,转换流 InputStreamReader isr = new InputStreamReader(in); // 缓冲区 BufferedReader bufr = new BufferedReader(isr); //字符流 OutputStream out = System.out; //将字符流转换成字节流的桥梁 OutputStreamWriter osw = new OutputStreamWriter(out); //缓冲区 BufferedWriter bufw = new BufferedWriter(osw); String line = null; while ((line = bufr.readLine()) != null) {//读取到字符串数据 if ("over".equals(line)) break; // System.out.println(line.toUpperCase()); // osw.write(line.toUpperCase()+"\n"); // osw.flush(); bufw.write(line.toUpperCase());//将字符数据用缓冲区对象将数据写入缓冲区,目的地是osw bufw.newLine(); bufw.flush(); } } }
简化代码后:
package cn.itcast.p1.io.transtream.demo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class TranStreamDemo2 { public static void main(String[] args) throws IOException { BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while ((line = bufr.readLine()) != null) {//读取到字符串数据 if ("over".equals(line)) break; bufw.write(line.toUpperCase());//将字符数据用缓冲区对象将数据写入缓冲区,目的地是osw bufw.newLine(); bufw.flush(); } } }
※流的基本操作规律
InputStreamReader:字节到字符,解码。
OutputStreamWriter:字符到字节,编码。
流的操作规律:
原因:流对象太多,开发时不知用哪个合适。
想要知道开发时用到哪些对象,只要通过四个明确即可。
1.明确源和目的(汇);
源: InputStream、Reader
目的:OutputStream、Writer
2.明确数据是否是纯文本数据;
源: 是纯文本 Reader
不是纯文本 InputStream
目的: 是纯文本 Writer
不是纯文本 OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3.明确具体的设备;
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘: File
控制台:System.out
内存: 数组
网络: Socket流
4.是否需要其他额外功能
是否需要高效(缓冲区):
是:加上buffer
需求1:复制一个文本文件
1.明确源和目的
源: InputStream Reader
目的:OutputStream Writer
2.是否是纯文本?是
源: Reader
目的:Writer
3.具体设备
源: 硬盘 File
目的:硬盘 File
FileReader fr=new FileReader("a.txt");
FileWriter fw=new FileWriter("b.txt");
4.是否需要额外功能
需要高效,转换
BufferedReader bufr=new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw=new BufferedWriter(new FileWriter("b.txt"));
需求2:读取键盘录入信息,写入到一个文件中
1.明确源和目的
源: InputStream Reader
目的:OutputStream Writer
2.是否是纯文本?是
源: Reader
目的:Writer
3.设备:
源: 键盘 System.in
目的:硬盘 File
InputStream in=System.in;
FIleWriter fw=new FileWriter("b.txt");
将读取的字节数据转成字符串,再由字符流操作——>
4.是否需要额外功能?
需要,转换。将字节流转成字符流,因为明确的源是Reader,这样操作文本数据更便捷。所以,要将已有的字节流转成字符流。使用字节——>字符的对象
InputStreamReader inr=InputStreamReader(System.in);
FileWriter fw=new FileWriter("b.txt");
需要高效。
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.int));
BufferedWriter bufw=new BufferedWriter(new FileWriter("b.txt"));
需求3:将一个文本文件数据显示在控制台上
1.明确源和目的
源: InputStream Reader
目的:OutputStream Writer
2.是否是纯文本?是
源: Reader
目的:Writer
3.明确具体设备:
源: 硬盘 File
目的:控制台 System.out
FileReader fr=new FileReader("a.txt");
OutputStream out=new OutputStream(System.out);//PrintStream
4.需要额外功能:
需要转换
FileReader fr=new FileReader("a.txt");
OutputStreamWriter osw=new OutputStreamWriter(System.out);
需要高效
BufferedReader bufr=new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw-new BufferedWriter(new OutputStreamWriter(System.out)):
需求4:读取键盘录入数据,显示在控制台上
1.明确源和目的
源: InputStream Reader
目的:OutputStream Writer
2.是否是纯文本?是
源: Reader
目的:Writer
3.明确设备:
源: 键盘 System.in
目的:控制台 System.out
InputStream in=System.in;
OutputStream out=Systemo.out;
4.额外功能:
转换,因为都是字节流,操作的却是文本数据,所以使用字符流操作起来更为便捷。
InputStreamReader inr=new InputStreamReader(System.in);
OutputStreamWriter osw=new OutputStreamWriter(System.out);
高效
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
需求5:将一个中文字符串数据按照指定的编码表写入到一个文本文件中。
1.目的。 OutputStream,Writer
2.是纯文本。 Writer
3.设备:硬盘 File
FileWriter fw=new FileWriter("a.txt");
fw.write("你好");
注意:需求中明确了要指定编码表,就不能使用FileWriter,因为FileWriter内部使用默认的本地码表。
只能使用其父类OutputStreamWriter。
另外,OutputStreamWriter接收一个字节输出流对象,既然是操作文件,该对象即为FileOutputStream。
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);//charsetName,字符集名称
高效
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
转换流的使用场景:
1.源或者目的对应的设备是字节流,但是操作的是文本数据,可以使用转换流作为桥梁,提高对文本操作的便捷。
2.一旦操作文本涉及到具体的指定编码表时,必须使用转换流。
package cn.itcast.p1.io.transtream.demo; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class TranStreamDemo3 { public static void main(String[] args) throws IOException { // writeText(); readText_2(); } public static void readText_2() throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream( "u8_1.txt"), "UTF-8"); char[] buf = new char[10]; int len = isr.read(buf); String str = new String(buf, 0, len); System.out.println(str); isr.close(); } public static void readText_1() throws IOException { FileReader fr = new FileReader("u8_1.txt");// 默认gbk,读取utf-8字符对应错误 char[] buf = new char[10]; int len = fr.read(buf); String str = new String(buf, 0, len); System.out.println(str); fr.close(); } public static void writeText_3() throws IOException { OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream( "u8_1.txt"), "utf-8"); osw.write("你好"); osw.close(); } public static void writeText_2() throws IOException { OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream( "gbk_3.txt"), "GBK"); // FileWriter fw = new FileWriter("gbk_1.txt"); /* * 这两句代码的功能是等同的 * FileWriter 其实就是转换流指定了本机默认码表的体现,而且这个转换流的子类对象,可以方便操作文本文件 * 简单说,就是操作文件的字节流+本机默认的编码表 * 这是按照默认码表来操作文件的便捷类。 * 如果操作文本文件需要明确具体的编码,FileWriter就不行了,必须用转换流。 */ osw.write("你好"); osw.close(); } public static void writeText() throws IOException { FileWriter fw = new FileWriter("gbk_1.txt"); fw.write("你好"); fw.close(); } }
File对象-构造函数 字段
用来将文件或者文件夹封装成对象;
方便对文件与文件夹的属性信息进行操作;
File对象可以作为参数传递给流的构造函数;
package cn.itcast.p2.io.file.demo; import java.io.File; public class FileDemo { /** * @param args */ public static void main(String[] args) { constructorDemo(); } public static void constructorDemo() { // 可以将一个已存在的不存在的文件或目录封装成File对象 File f1 = new File("c:\\a.txt"); File f2 = new File("c:\\", "a.txt"); File f = new File("c:\\"); File f3 = new File(f, "a.txt"); // File f4=new // File("c:"+System.getProperty("file.separator")+"abc\\a.txt"); File f4 = new File("c:" + File.separator + "abc" + File.separator + "a.txt"); System.out.println(f4); } }
File对象的常见方法
1.获取
1.1 获取文件名称
1.2 获取文件路径
1.3 获取文件大小
1.4 获取文件修改时间
2.创建和删除
boolean createNewFile();//和输出流不一样,如果文件不存在,则创建;如果文件存在,则不创建。
3.判断
4.重命名
boolean renameTo(File dest)
重新命名此抽象路径名表示的文件。
package cn.itcast.p2.io.file.demo; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.util.Date; public class FileMethodDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // getDemo(); // createAndDeleteDemo(); // isDemo(); // renameToDemo(); // listRootsDemo(); } public static void listRootsDemo() { File[] files = File.listRoots(); for (File file : files) { System.out.println(file); } File file = new File("c:\\"); System.out.println(file.getFreeSpace()); System.out.println(file.getTotalSpace()); System.out.println(file.getUsableSpace()); } public static void renameToDemo() { File f1 = new File("c:\\0.mp3"); File f2 = new File("d:\\9.mp3"); f1.renameTo(f2); } public static void isDemo() throws IOException { File f = new File("a.txt"); boolean b = f.exists(); System.out.println("exists?:" + b); // 最好先判断是否存在 System.out.println("isFile?:" + f.isFile()); System.out.println("isDirectory?:" + f.isDirectory()); } public static void createAndDeleteDemo() throws IOException { File dir = new File("abc\\a\\b\\c\\d"); // boolean b=dir.mkdir();//创建单级目录 // boolean b=dir.mkdirs();//创建多级目录 // System.out.println("b:"+b); System.out.println("del:" + dir.delete()); // 文件的创建和删除 File file = new File("file.txt"); /* * 和输出流不一样,如果文件不存在,则创建;如果文件存在,则不创建。 */ // boolean b=file.createNewFile(); // System.out.println("b:"+b); // boolean b=file.delete(); // System.out.println("b:"+b); } public static void getDemo() { File file = new File("a.txt"); String name = file.getName(); String abspath = file.getAbsolutePath(); String path = file.getPath(); long len = file.length(); long time = file.lastModified(); Date date = new Date(time); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); String str_time = dateFormat.format(date); System.out.println("parent:" + file.getParent());// 文件对象中是绝对路径时,获取父目录;相对路径时,null System.out.println("name:" + name); System.out.println("path:" + path); System.out.println("abspath:" + abspath); System.out.println("len:" + len); System.out.println("time:" + time); System.out.println("str_time:" + str_time); } }
String[] list()
返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。
package cn.itcast.p2.io.file.demo; import java.io.File; public class FileListDemo { /** * @param args */ public static void main(String[] args) { listDemo(); } public static void listDemo() { File file=new File("c:\\"); /* * 获取当前目录下的文件及文件夹的名称,包括隐藏文件 * 调用list方法的File对象中,封装的必须是目录, * 否则会发生空指针异常NullPointerException * 如果访问的是系统级目录,也会发生空指针异常 * * 如果目录存在,但是没有内容,会返回一个数组,但是长度为0 */ String[] names=file.list(); for(String name:names) System.out.println(name); } }
过滤器的应用
String[] list(FilenameFilter filter)
返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。
boolean accept(File dir,String name)
测试指定文件是否应该包含在某一文件列表中。
参数:
dir - 被找到的文件所在的目录。
name - 文件的名称。
返回:
当且仅当该名称应该包含在文件列表中时返回 true;否则返回 false。
File[] listFiles(FileFilter filter)
返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
File[] listFiles(FilenameFilter filter)
返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
以上两个方法返回的是文件对象,可以对成员进行操作
package cn.itcast.p2.io.file.demo; import java.io.File; public class FileListDemo { /** * @param args */ public static void main(String[] args) { // listDemo(); listDemo_2(); // listDemo_3(); } public static void listDemo_3() { File dir=new File("c:\\"); File[] files=dir.listFiles(new FilterByHidden()); for(File file:files) System.out.println(file); } public static void listDemo_2() { File dir=new File("c:\\"); String[] names=dir.list(new SuffixFilter(".java"));//使用过滤器 for(String name:names)//调用了多次过滤器 System.out.println(name); } public static void listDemo() { File file=new File("c:\\"); /* * 获取当前目录下的文件及文件夹的名称,包括隐藏文件 * 调用list方法的File对象中,封装的必须是目录, * 否则会发生空指针异常NullPointerException * 如果访问的是系统级目录,也会发生空指针异常 * * 如果目录存在,但是没有内容,会返回一个数组,但是长度为0 */ String[] names=file.list(); for(String name:names) System.out.println(name); } }
package cn.itcast.p2.io.file.demo; import java.io.File; import java.io.FilenameFilter; public class SuffixFilter implements FilenameFilter { private String suffix;//用于传入的文件后缀名 public SuffixFilter(String suffix) { super(); this.suffix = suffix; } @Override public boolean accept(File dir, String name) { //如果含有指定后缀名则返回true;否则,false return name.endsWith(suffix); } }
package cn.itcast.p2.io.file.demo; import java.io.File; import java.io.FileFilter; //隐藏文件过滤器 public class FilterByHidden implements FileFilter { @Override public boolean accept(File pathname) { return !pathname.isHidden(); } }
深度遍历文件夹示例:
package cn.itcase.io.p1.file.test; import java.io.File; /* * 需求:对指定目录进行所有内容的列出(包含子目录中的内容) * 也可以理解为深度遍历 */ public class FileTest { /** * @param args */ public static void main(String[] args) { File dir = new File("e:\\PalInn"); listAll(dir,0); } public static void listAll(File dir,int level) {//level作为目录层数计数器,用于缩进 System.out.println(getSpace(level)+dir.getName()); // 获取指定目录下的所有文件夹或文件对象 level++; File[] files = dir.listFiles(); for (int x = 0; x < files.length; x++) { if (files[x].isDirectory()) { listAll(files[x],level); } else System.out.println(getSpace(level)+files[x].getName()); } } public static String getSpace(int level) { StringBuilder sb=new StringBuilder(); sb.append("|--"); for(int x=0;x<level;x++){ sb.insert(0," ");//在字符串最前面加入空格 } return sb.toString(); } }
递归:
函数自身直接或者间接地调用到了自身
使用场景:
一个功能在被重复使用,并在每次使用时,参与运算的结果与上次调用有关。这时可以使用递归来解决问题。
注意:
1.递归一定要明确条件,否则容易栈溢出;
2.注意递归的次数
package cn.itcase.io.p1.digui; public class DiGuiDemo { /** * @param args */ public static void main(String[] args) { // toBin(6); System.out.println(getSum(5)); } public static int getSum(int num) { if (num == 1) return 1; return num+getSum(num-1); } public static void toBin(int num) { if (num > 0) { toBin(num / 2); System.out.print(num % 2); } } }
删除带内容的目录示例:
原理:必须从里往外删除;
需要进行深度遍历。
package cn.itcase.io.p1.file.test; import java.io.File; public class RemoveDirTest { /** * @param args */ public static void main(String[] args) { File dir=new File("demodir"); removeDir(dir); } public static void removeDir(File dir) { File[] files=dir.listFiles(); for(File file:files){ if(file.isDirectory()){ removeDir(file); }else{ System.out.println(file+":"+file.delete()); } } System.out.println(dir+":"+dir.delete()); } }