JAVA中级之I/O

创建一个文件对象

使用绝对路径或者相对路径创建File对象

package file;
  
import java.io.File;
  
public class TestFile {
  
    public static void main(String[] args) {
        // 绝对路径
        File f1 = new File("d:/LOLFolder");
        System.out.println("f1的绝对路径:" + f1.getAbsolutePath());
        // 相对路径,相对于工作目录,如果在eclipse中,就是项目目录
        File f2 = new File("LOL.exe");
        System.out.println("f2的绝对路径:" + f2.getAbsolutePath());
  
        // 把f1作为父目录创建文件对象
        File f3 = new File(f1, "LOL.exe");
  
        System.out.println("f3的绝对路径:" + f3.getAbsolutePath());
    }
}

文件常用方法1

注意1: 需要在D:\LOLFolder确实存在一个LOL.exe,才可以看到对应的文件长度、修改时间等信息

注意2: renameTo方法用于对物理文件名称进行修改,但是并不会修改File对象的name属性。

package file;
import java.io.File;
import java.util.Date;

import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

public class TestFile {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//绝对路径
		File f1 = new File("d:/LOLFolder");
		System.out.println("f1的绝对路径:"+f1.getAbsolutePath());
		//相对路径,相对于工作目录
		File f2 =  new File("LOL.exo");
		System.out.println("f2的绝对路径是:"+f2.getAbsolutePath());
		
		//把f1当作父目录创建文件对象
		File f3 = new File(f1,"LOL.exe");
		System.out.println("f3的绝对路径是:"+f3.getAbsolutePath());
		
		File f = new File("d:/LOLFolder/LOL.exe");
		System.out.println("当前文件是:"+f);
		
		//文件是否存在
		System.out.println("判断是否存在:"+f.exists());
		
		//是否是文件夹
		System.out.println("判断是否是文件夹:"+f.isDirectory());
		
		//是否是文件
		System.out.println("判断是否是文件:"+f.isFile());
		
		//文件长度
		System.out.println("获取文件的长度:"+f.length());
		
		//文件最后修改时间
		long time = f.lastModified();
		Date d = new Date(time);
		System.out.println("获取文件的最后修改时间:"+d);
		//设置文件修改时间是1970.1.1 08:00:00
		f.setLastModified(0);
		
		//文件重命名
		File f4 = new File("d:/LOLFolder/DOTA.exe");
		f.renameTo(f4);
		System.out.println("把LOL.exe改名成了DOTA.exe");
		System.out.println("注意:需要在D:\\LOLFolder确实存在一个LOL.exe,\r\n才可以看到对应的文件长度、修改时间等信息");

	}

}

文件常用方法2

package file;
  
import java.io.File;
import java.io.IOException;
  
public class TestFile {
  
    public static void main(String[] args) throws IOException {
  
        File f = new File("d:/LOLFolder/skin/garen.ski");
  
        // 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        f.list();
  
        // 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        File[]fs= f.listFiles();
  
        // 以字符串形式返回获取所在文件夹
        f.getParent();
  
        // 以文件形式返回获取所在文件夹
        f.getParentFile();
        // 创建文件夹,如果父文件夹skin不存在,创建就无效
        f.mkdir();
  
        // 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹
        f.mkdirs();
  
        // 创建一个空文件,如果父文件夹skin不存在,就会抛出异常
        f.createNewFile();
        // 所以创建一个空文件之前,通常都会创建父目录
        f.getParentFile().mkdirs();
  
        // 列出所有的盘符c: d: e: 等等
        f.listRoots();
  
        // 刪除文件
        f.delete();
  
        // JVM结束的时候,刪除文件,常用于临时文件的删除
        f.deleteOnExit();
  
    }
}

练习-遍历文件夹 

一般说来操作系统都会安装在C盘,所以会有一个 C:\WINDOWS目录。

遍历这个目录下所有的文件(不用遍历子目录)

找出这些文件里,最大的和最小(非0)的那个文件,打印出他们的文件名

同上的练习,要求遍历子文件夹

package file;
 
import java.io.File;
 
public class LookFile {
    public static void main (String[] args) {
        File directory = new File ("c:\\WINDOWS");
        File[] files = directory.listFiles ();
 
        File min_Size_File = files[0];
        File max_Size_File = files[0];
        for (File file : files) {
            if (file.length () < min_Size_File.length ()) min_Size_File = file;
            if(file.length () > max_Size_File.length ()) max_Size_File = file;
        }
 
        System.out.printf ("%s\n最大的文件为\t%s\t大小为\t%d字节\n最小的文件为\t%s\t大小为\t%d字节",directory,max_Size_File,max_Size_File.length (),min_Size_File,min_Size_File.length ());
 
    }
}
package IO;
 
import java.io.File;
 
public class TestFile2 {
    public static void main(String[] args) {
        File f=new File("C:\\WINDOWS");
        //数组文件的地址
        System.out.println(f.listFiles());
        //file文件的名称
        File[] cs=f.listFiles();
        for(File ecah:cs){
            System.out.println(ecah);
        }
    }
}

什么是流(Stream),流就是一系列的数据

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序
比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流
输入流: InputStream
输出流:OutputStream

什么是流

文件输入流

如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。

目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。

package stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestStream {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			File f = new File("f:/lol.txt");
			//创建基于文件的输入流,输出流
			FileInputStream fis = new FileInputStream(f);
			FileOutputStream fos = new FileOutputStream(f);
			//通过这个输入流,就可以把数据从硬盘读取到Java虚拟机中,也就是读到内存
		} catch (IOException e) {
			// TODO: handle exception
			e.printStackTrace();
		}

	}

}

InputStream字节输入流 
OutputStream字节输出流 
用于以字节的形式读取和写入数据

ASCII码 概念

所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放。
比如A就对应的数字65,a对应的数字97. 不同的字母和符号对应不同的数字,就是一张码表。
ASCII是这样的一种码表。 只包含简单的英文字母,符号,数字等等。 不包含中文,德文,俄语等复杂的。

以字节流的形式读取文件内容

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取

package stream;
  
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
  
public class TestStream {
  
    public static void main(String[] args) {
        try {
            //准备文件lol.txt其中的内容是AB,对应的ASCII分别是65 66
            File f =new File("d:/lol.txt");
            //创建基于文件的输入流
            FileInputStream fis =new FileInputStream(f);
            //创建字节数组,其长度就是文件的长度
            byte[] all =new byte[(int) f.length()];
            //以字节流的形式读取文件所有内容
            fis.read(all);
            for (byte b : all) {
                //打印出来是65 66
                System.out.println(b);
            }
             
            //每次使用完流,都应该进行关闭
            fis.close();
              
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
          
    }
}

以字节流的形式向文件写入数据

OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据

注: 如果文件d:/lol2.txt不存在,写出操作会自动创建该文件。 
但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常

以字节流的形式向文件写入数据

package stream;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class TestStream {
 
    public static void main(String[] args) {
        try {
            // 准备文件lol2.txt其中的内容是空的
            File f = new File("d:/lol2.txt");
            // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
            byte data[] = { 88, 89 };
 
            // 创建基于文件的输出流
            FileOutputStream fos = new FileOutputStream(f);
            // 把数据写入到输出流
            fos.write(data);
            // 关闭输出流
            fos.close();
             
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

 

package stream;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class TestStream {
 
    public static void main(String[] args) {
         
        /**
         * 练习-写入数据到文件
         * 
         * 以字节流的形式向文件写入数据 中的例子,当lol2.txt不存在的时候,是会自动创建lol2.txt文件的。
         * 但是,如果是写入数据到d:/xyz/lol2.txt,而目录xyz又不存在的话,就会抛出异常。
         * 那么怎么自动创建xyz目录?
         * 如果是多层目录 d:/xyz/abc/def/lol2.txt 呢?
         */
 
        try {
            // 准备文件lol2.txt其中的内容是空的
            File f = new File("e:/xyz/abc/def/lol2.txt");
             
            // 如果父文件夹不存在,则创建
            // File.mkdirs(); 创建文件夹,如果父文件夹不存在,就会创建父文件夹
            if (!f.getParentFile().exists()) {
                f.getParentFile().mkdirs();
            }
             
            // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
            byte data[] = { 88, 89 };
  
            // 创建基于文件的输出流
            FileOutputStream fos = new FileOutputStream(f);
            // 把数据写入到输出流
            fos.write(data);
            // 关闭输出流
            fos.close();
              
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

练习-拆分文件 

找到一个大于100k的文件,按照100k为单位,拆分成多个子文件,并且以编号作为文件名结束。
比如文件 eclipse.exe,大小是309k。
拆分之后,成为 
eclipse.exe-0
eclipse.exe-1
eclipse.exe-2
eclipse.exe-3

 

package IOoperation;
 
import java.io.*;
 
public class DetachFile {
    static File f = new File("d:\\xyz\\abc\\def\\lol2.txt");
    static int file_num = (int) (f.length() / 102400);
 
    public static void main(String[] args) throws Exception {
 
        FileInputStream fis = new FileInputStream(f);
 
        for (int i = 0; i <= file_num; i++) {
            byte[] temp;
            if (i < file_num) {// 判断是第几个文件
                temp = new byte[102400];
            } else {// 为了节省空间
                temp = new byte[(int) (f.length() - 102400 * (file_num))];
            }
            fis.read(temp);
            String file_name = "d:\\xyz\\abc\\def\\lol2_" + i + ".txt";
            File f_temp = new File(file_name);
            FileOutputStream fos = new FileOutputStream(file_name);
            fos.write(temp);
            fos.close();
            System.out.printf("输出子文件%s,其大小为%d字节\n", file_name, f_temp.length());
        }
        fis.close();
 
    }
}

 

所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。

 

在try中关闭

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

package stream;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
 
public class TestStream {
 
    public static void main(String[] args) {
        try {
            File f = new File("d:/lol.txt");
            FileInputStream fis = new FileInputStream(f);
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
            // 在try 里关闭流
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
}

在finally中关闭

这是标准的关闭流的方式
1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
2. 在finally关闭之前,要先判断该引用是否为空
3. 关闭的时候,需要再一次进行try catch处理

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~

package stream;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
 
public class TestStream {
 
    public static void main(String[] args) {
        File f = new File("d:/lol.txt");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(f);
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 在finally 里关闭流
            if (null != fis)
                try {
 
                    fis.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        }
 
    }
}

使用try()的方式

把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

package stream;
  
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
  
public class TestStream {
  
    public static void main(String[] args) {
        File f = new File("d:/lol.txt");
  
        //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
        try (FileInputStream fis = new FileInputStream(f)) {
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
  
    }
}

Reader字符输入流 
Writer字符输出流 
专门用于字符的形式读取和写入数据

使用字符流读取文件

FileReader 是Reader子类,以FileReader 为例进行文件读取

package stream;
 
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
 
public class TestStream {
 
    public static void main(String[] args) {
        // 准备文件lol.txt其中的内容是AB
        File f = new File("d:/lol.txt");
        // 创建基于文件的Reader
        try (FileReader fr = new FileReader(f)) {
            // 创建字符数组,其长度就是文件的长度
            char[] all = new char[(int) f.length()];
            // 以字符流的形式读取文件所有内容
            fr.read(all);
            for (char b : all) {
                // 打印出来是A B
                System.out.println(b);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

使用字符流把字符串写入到文件

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件

使用字符流把字符串写入到文件

package stream;
  
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
  
public class TestStream {
  
    public static void main(String[] args) {
        // 准备文件lol2.txt
        File f = new File("d:/lol2.txt");
        // 创建基于文件的Writer
        try (FileWriter fr = new FileWriter(f)) {
            // 以字符流的形式把数据写入到文件中
            String data="abcdefg1234567890";
            char[] cs = data.toCharArray();
            fr.write(cs);
  
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  
    }
}

练习-文件加密 和解密

准备一个文本文件(非二进制),其中包含ASCII码的字符和中文字符。
设计一个方法

public static void encodeFile(File encodingFile, File encodedFile);


在这个方法中把encodingFile的内容进行加密,然后保存到encodedFile文件中。
加密算法: 
数字:
如果不是9的数字,在原来的基础上加1,比如5变成6, 3变成4
如果是9的数字,变成0
字母字符:
如果是非z字符,向右移动一个,比如d变成e, G变成H
如果是z,z->a, Z-A。
字符需要保留大小写
非字母字符
比如',&^ 保留不变,中文也保留不变

解密相反

 

public class Homework02 {
    public static void main(String[] args) {
        File decodingFile=new File("D:\\example\\encodedFile.txt");
        File decodedFile=new File("D:\\example\\decodedFile.txt");
        decodeFile(decodingFile,decodedFile);
    }
    //解密函数,对加密函数进行解密
    public static void decodeFile(File decodingFile, File decodedFile){
        //读取加密文件字符的字符输入流
        FileReader fr=null;
        //将解密后的字符输出到指定文件的字符输出流
        FileWriter fw=null;
        try{
            fr=new FileReader(decodingFile);
            fw=new FileWriter(decodedFile);
            char[] buffer=new char[1]; //每次读取一个字符
            int len=0;//读取字符的长度
            while((len=fr.read(buffer))!=-1){
                char ch=buffer[0];
                if(ch=='0'){
                    ch='9';
                }else if(ch>'0'&&ch<='9'){
                    ch--;
                }else if(ch=='a'){
                    ch='z';
                }else if(ch=='A'){
                    ch='Z';
                }else if(ch>'a'&&ch<'z'){
                    ch--;
                }else if(ch>'A'&&ch<'Z'){
                    ch--;
                }
                fw.write(ch);
                fw.flush();
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            //关闭流
                try{
                    if(fr!=null) {
                        fr.close();
                    }
                    if(fw!=null) {
                        fw.close();
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
 
 
 
public class HomeWork01 {
    public static void main(String[] args) {
        File encodingFile=new File("d:/example/encodingFile.txt");
        File encodedFile=new File("d:/example/encodedFile.txt");
        encodeFile(encodingFile,encodedFile);
    }
    //对文件进行加密的方法
    public static void encodeFile(File encodingFile, File encodedFile){
        FileReader fr=null;  //读取未加密文件的字符输入流
        FileWriter fw=null;  //将加密的字符写入到指定路径的文件中
        try{
            fr=new FileReader(encodingFile);
            fw=new FileWriter(encodedFile);
            //字符缓冲数组
            char[] buffer=new char[1];
            int len=0; //读取字符的长度
            while((len=fr.read(buffer))!=-1){
                char ch=buffer[0];
                if(ch=='9'){
                    ch='0';
                }else if(ch>='0'&&ch<'9'){
                    ch++;
                }else if(ch=='z'){
                    ch='a';
                }else if(ch=='Z'){
                    ch='A';
                }else if(ch>='a'&&ch<'z'){
                    ch++;
                }else if(ch>='A'&&ch<='Z'){
                    ch++;
                }
                fw.write(ch);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally{
            if(fr!=null){
                try{
                    fr.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(fw!=null){
                try{
                    fw.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
 
}

 

编码概念

计算机存放数据只能存放数字,所有的字符都会被转换为不同的数字。
就像一个棋盘一样,不同的字,处于不同的位置,而不同的位置,有不同的数字编号。
有的棋盘很小,只能放数字和英文
有的大一点,还能放中文
有的“足够”大,能够放下世界人民所使用的所有文字和符号

如图所示,英文字符 A 能够放在所有的棋盘里,而且位置都差不多
中文字符, 中文字符 中 能够放在后两种棋盘里,并且位置不一样,而且在小的那个棋盘里,就放不下中文

编码概念

常见编码

工作后经常接触的编码方式有如下几种:
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)

其中
ISO-8859-1 包含 ASCII
GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中

UNICODE和UTF

根据前面的学习,我们了解到不同的编码方式对应不同的棋盘,而UNICODE因为要存放所有的数据,那么它的棋盘是最大的。 
不仅如此,棋盘里每个数字都是很长的(4个字节),因为不仅要表示字母,还要表示汉字等。

如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。
比如在ISO-8859-1中,a 字符对应的数字是0x61
而UNICODE中对应的数字是 0x00000061,倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

UTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 unicode-百度百科

UNICODE和UTF

以介质是硬盘为例,字节流和字符流的弊端: 
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。 

为了解决以上弊端,采用缓存流。 
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。 

就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲 

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作

使用缓存流读取数据

缓存字符输入流 BufferedReader 可以一次读取一行数据

package stream;
  
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
  
public class TestStream {
  
    public static void main(String[] args) {
        // 准备文件lol.txt其中的内容是
        // garen kill teemo
        // teemo revive after 1 minutes
        // teemo try to garen, but killed again
        File f = new File("d:/lol.txt");
        // 创建文件字符流
        // 缓存流必须建立在一个存在的流的基础上
        try (
                FileReader fr = new FileReader(f);
                BufferedReader br = new BufferedReader(fr);
            )
        {
            while (true) {
                // 一次读一行
                String line = br.readLine();
                if (null == line)
                    break;
                System.out.println(line);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  
    }
}

使用缓存流写出数据

PrintWriter 缓存字符输出流, 可以一次写出一行数据

package stream;
   
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
   
public class TestStream {
   
    public static void main(String[] args) {
        // 向文件lol2.txt中写入三行语句
        File f = new File("d:/lol2.txt");
          
        try (
                // 创建文件字符流
                FileWriter fw = new FileWriter(f);
                // 缓存流必须建立在一个存在的流的基础上              
                PrintWriter pw = new PrintWriter(fw);              
        ) {
            pw.println("garen kill teemo");
            pw.println("teemo revive after 1 minutes");
            pw.println("teemo try to garen, but killed again");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   
    }
}

flush

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush

package stream;
    
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
    public static void main(String[] args) {
        //向文件lol2.txt中写入三行语句
        File f =new File("d:/lol2.txt");
        //创建文件字符流
        //缓存流必须建立在一个存在的流的基础上
        try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {
            pw.println("garen kill teemo");
            //强制把缓存中的数据写入硬盘,无论缓存是否已满
                pw.flush();           
            pw.println("teemo revive after 1 minutes");
                pw.flush();
            pw.println("teemo try to garen, but killed again");
                pw.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
package IOoperation;
 
import java.io.*;
// 这是一个用于删除java中注释的程序
public class eliminatecomment {
    public static void main(String[] args) {
        File f = new File("d://comment.java");
        if (!f.exists()) {
            System.err.println("文件不存在");
        } else {
            removeComments(f);
        }
    }
    //初始化一个字符串数组,把读取的字符串写入
    public static void removeComments(File javaFile) {
        String[] temp = new String[100];
        int count = 0;
        try (FileReader fr = new FileReader(javaFile); BufferedReader br = new BufferedReader(fr)) {
            while (true) {
                String line = br.readLine();
                temp[count] = line;
                count++;
                if (line == null)
                    break;
                System.out.println(line);
 
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        }
        //把处理后的字符串重新写回文件
        try (FileWriter fw = new FileWriter(javaFile); PrintWriter pw = new PrintWriter(fw)) {
            processString(temp);
 
            for (int i = 0; i < temp.length; i++) {
                if (temp[i] != null) {
                    pw.println(temp[i]);
                }
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //处理文件,把//开头的变成null
    public static void processString(String[] s) {
        for (int i = 0; i < s.length; i++) {
            if (s[i] != null) {
                if (s[i].startsWith("//")) {
                    s[i] = null;
                }
            }
 
        }
    }
}

DataInputStream 数据输入流 
DataOutputStream 数据输出流

直接进行字符串的读写

使用数据流的writeUTF()和readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出 布尔值,整数和字符串。 然后再通过DataInputStream 顺序读入这些数据。

注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。

package stream;
      
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
      
public class TestStream {
      
    public static void main(String[] args) {
        write();
        read();
    }
 
    private static void read() {
        File f =new File("d:/lol.txt");
        try (
                FileInputStream fis  = new FileInputStream(f);
                DataInputStream dis =new DataInputStream(fis);
        ){
            boolean b= dis.readBoolean();
            int i = dis.readInt();
            String str = dis.readUTF();
             
            System.out.println("读取到布尔值:"+b);
            System.out.println("读取到整数:"+i);
            System.out.println("读取到字符串:"+str);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
 
    private static void write() {
        File f =new File("d:/lol.txt");
        try (
                FileOutputStream fos  = new FileOutputStream(f);
                DataOutputStream dos =new DataOutputStream(fos);
        ){
            dos.writeBoolean(true);
            dos.writeInt(300);
            dos.writeUTF("123 this is gareen");
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
}

练习-向文件中写入两个数字,然后把这两个数字分别读取出来 

要求
第一种方式: 使用缓存流把两个数字以字符串的形式写到文件里,再用缓存流以字符串的形式读取出来,然后转换为两个数字。 
注: 两个数字之间要有分隔符用于区分这两个数字。 比如数字是31和15,如果不使用分隔符,那么就是3115,读取出来就无法识别到底是哪两个数字。 使用分隔符31@15能解决这个问题。

第二种方式: 使用数据流DataOutputStream向文件连续写入两个数字,然后用DataInpuStream连续读取两个数字

package IOoperation;
 
import java.io.*;
//用缓存流与数据流分别读写数字
public class Datastreamprac {
    public static void main(String[] args) {
        File f = new File("d:/lol.txt");
        buffered(f);
        dstream(f);
    }
 
    public static void buffered(File f) {
        try (FileWriter fw = new FileWriter(f); PrintWriter pw = new PrintWriter(fw)) {
            String line = "31@15";
            pw.write(line);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (FileReader fw = new FileReader(f); BufferedReader br = new BufferedReader(fw)) {
            String line = br.readLine();
            String[] temp = line.split("@");
            for (int i = 0; i < temp.length; i++) {
                System.out.println(Integer.valueOf(temp[i]));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public static void dstream(File f) {
        try (FileOutputStream fw = new FileOutputStream(f); DataOutputStream dos = new DataOutputStream(fw)) {
            dos.writeInt(31);
            dos.writeInt(15);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (FileInputStream fis = new FileInputStream(f); DataInputStream dis = new DataInputStream(fis)) {
 
            System.out.println(dis.readInt());
            System.out.println(dis.readInt());
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘 

一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口

序列化一个对象

创建一个Hero对象,设置其名称为garen。 
把该对象序列化到一个文件garen.lol。
然后再通过序列化把该文件转换为一个Hero对象

注:把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口

package stream;
    
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
  
import charactor.Hero;
    
public class TestStream {
    
    public static void main(String[] args) {
        //创建一个Hero garen
        //要把Hero对象直接保存在文件上,务必让Hero类实现Serializable接口
        Hero h = new Hero();
        h.name = "garen";
        h.hp = 616;
          
        //准备一个文件用于保存该对象
        File f =new File("d:/garen.lol");
 
        try(
            //创建对象输出流
            FileOutputStream fos = new FileOutputStream(f);
            ObjectOutputStream oos =new ObjectOutputStream(fos);
            //创建对象输入流              
            FileInputStream fis = new FileInputStream(f);
            ObjectInputStream ois =new ObjectInputStream(fis);
        ) {
            oos.writeObject(h);
            Hero h2 = (Hero) ois.readObject();
            System.out.println(h2.name);
            System.out.println(h2.hp);
               
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            
    }
}




------------------------
package charactor;
 
import java.io.Serializable;
 
public class Hero implements Serializable {
    //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
    private static final long serialVersionUID = 1L;
    public String name;
    public float hp;
 
}

 

System.out 是常用的在控制台输出数据的 
System.in 可以从控制台输入数据

package stream;
 
import java.io.IOException;
import java.io.InputStream;
 
public class TestStream {
 
    public static void main(String[] args) {
        // 控制台输入
        try (InputStream is = System.in;) {
            while (true) {
                // 敲入a,然后敲回车可以看到
                // 97 13 10
                // 97是a的ASCII码
                // 13 10分别对应回车换行
                int i = is.read();
                System.out.println(i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Scanner读取字符串

使用System.in.read虽然可以读取数据,但是很不方便
使用Scanner就可以逐行读取了

package stream;
    
import java.util.Scanner;
    
public class TestStream {
    
    public static void main(String[] args) {
         
            Scanner s = new Scanner(System.in);
             
            while(true){
                String line = s.nextLine();
                System.out.println(line);
            }
         
    }
}

Scanner从控制台读取整数

使用Scanner从控制台读取整数

Scanner从控制台读取整数

package stream;
 
import java.util.Scanner;
 
public class TestStream {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int a = s.nextInt();
        System.out.println("第一个整数:"+a);
        int b = s.nextInt();
        System.out.println("第二个整数:"+b);
    }
}

练习-自动创建类 

自动创建有一个属性的类文件。
通过控制台,获取类名,属性名称,属性类型,根据一个模板文件,自动创建这个类文件,并且为属性提供setter和getter

练习-自动创建类

 

package stream;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;
 
/**
 * 练习-自动创建类
 * 
 * 自动创建有一个属性的类文件。
 * 通过控制台,获取类名,属性名称,属性类型,根据一个模板文件,自动创建这个类文件,并且为属性提供setter和getter
 * 
 * 模板文件名templateClass.java
 *  public class @class@ {
 *      public @type@ @property@;
 *      public @class@() {
 *      }
 *      public void set@Uproperty@(@type@  @property@){
 *          this.@property@ = @property@;
 *      }
 *    
 *      public @type@  get@Uproperty@(){
 *          return this.@property@;
 *      }
 *  }
 */
 
public class TestStream {
 
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("请输入类的名称:");
        String className = s.nextLine();
        System.out.println("请输入属性的类型:");
        String typeName = s.nextLine();
        System.out.println("请输入属性的名称:");
        String propertyName = s.nextLine();
 
        File templateFile = new File("E:/templateClass.java"); // 模板文件
        File classFile = new File("E:/" + className + ".java"); // 生成类文件
 
        FileReader fr = null;
        BufferedReader br = null;
        StringBuffer sb = new StringBuffer();
        FileWriter fw = null;
        PrintWriter pw = null;
        try {
            fr = new FileReader(templateFile);
            br = new BufferedReader(fr);
            String line = null;
            while ((line = br.readLine()) != null) {
                sb.append(line + "\n");
            }
 
            // 替换模板
            String classStr = sb.toString().replaceAll("@class@", className);
            classStr = classStr.replaceAll("@type@", typeName);
            classStr = classStr.replaceAll("@property@", propertyName);
            char first = Character.toUpperCase(propertyName.charAt(0)); // 首字母大写
            String sub = propertyName.substring(1); // 分割字符串
            String UpropertyName = first + sub; // 大写的字母和剩余字符串拼接
            classStr = classStr.replaceAll("@Uproperty@", UpropertyName);
 
            System.out.println("替换后的内容:");
            System.out.println(classStr);
            fw = new FileWriter(classFile);
            pw = new PrintWriter(fw);
            pw.print(classStr);
            System.out.println("文件保存在:" + classFile.getAbsolutePath());
        } catch(IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fr != null) {
                    fr.close();
                }
                if (br != null) {
                    br.close();
                }
                if (fw != null) {
                    fw.close();
                }
                if (pw != null) {
                    pw.close();
                }
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
 
}

练习-复制文件 

复制文件是常见的IO操作,设计如下方法,实现复制源文件srcFile到目标文件destFile

public static void copyFile(String srcFile, String destFile){

}

 

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class Main {
 
    public static void copyFile(String srcFile,String destFile) {
        File file=new File(srcFile);
        File newfile=new File(destFile);
        try(FileInputStream fReader=new FileInputStream(file);
            FileOutputStream fileWriter=new FileOutputStream(newfile);
                ){
            byte[]cs=new byte[(int)file.length()];
            fReader.read(cs);
            fileWriter.write(cs);
            System.out.println("复制的文件保存在:"+newfile.getAbsolutePath());
        }
        catch (IOException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        // TODO 自动生成的方法存根
        String beforefile="D:\\QRCode.png";
        String afterfile="G:\\QRCode.png";
        Main.copyFile(beforefile, afterfile);
    }

练习-复制文件夹 

复制文件夹,实现如下方法,把源文件夹下所有的文件 复制到目标文件夹下(包括子文件夹)

public static void copyFolder(String srcFolder, String destFolder){

}

package TestStream;
 
import java.io.*;
import java.util.*;
 
public class Stream_test_2 {
 
    public static void main(String[] args) {
         
        Scanner s = new Scanner(System.in);
        System.out.println("输入源文件:");
        String srcFolder = s.next();
        System.out.println("输入存入的目标文件:");
        String destFolder = s.next();
        copyFolder(srcFolder, destFolder);
    }
//  复制文件
    public static void copyFolder(String srcFolder, String destFolder){
         
        File sf = new File(srcFolder);
        File df = new File(destFolder);
//      创建文件引用集合
        File[] f = sf.listFiles();
//      创建文件名称集合
        String[] name = sf.list();
//      for读出
        for(int i=0;i<f.length;i++) {
//          如果不是目录则执行
            if(!f[i].isDirectory()) {
//              将源文件写入目标文件之中
                copyFile(f[i].getAbsolutePath(), new File(df+"/"+name[i]).getAbsolutePath());
            }else {
//              反之则先创建父目录,再在将源文件写入目标文件之中
                new File(df+"/"+name[i]).mkdirs();
                copyFolder(f[i].getAbsolutePath(), new File(df+"/"+name[i]).getAbsolutePath());
            }
        }
    }
//  文件写入
    public static void copyFile(String srcFolder, String destFolder) {
         
        try(BufferedReader br = new BufferedReader(new FileReader(srcFolder));
            PrintWriter pw = new PrintWriter(new FileWriter(destFolder))) {
            while(true) {
                String str = br.readLine();
                if(str==null)
                    break;
                pw.println(str);
            }
            System.out.printf("%n%s复制成功!%n",new File(srcFolder).getAbsoluteFile());
        } catch (IOException e) {
            System.out.printf("%n找不到文件!");
            e.getStackTrace();
        }
         
    }
     
}









----------------------------------------------------------
package file;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
/**
 *  复制文件夹,实现如下方法,把源文件夹下所有的文件 复制到目标文件夹下(包括子文件夹)
  
    public static void copyFolder(String srcFolder, String destFolder){
         
    }
 */
public class TestStream {
    public static void main(String[] args) {
         copyFolder("e:/LOLFolder","e:/LOLFolder2");
    }
    //复制文件和文件夹的方法
    private static void copyFolder(String srcFolder, String destFolder) {
        File src = new File(srcFolder);     //源文件
        File dest = new File(destFolder);   //目标文件
         
        if (!src.exists()) {    //源文件不能为空
            return;
        }
        if (src.isFile()) {     //源文件只能是是文件夹
            return ;
        }
        if (dest.isFile()) {    //目标文件只能是文件夹
            return;
        }
        if (!dest.exists()) {   //目标文件不存在,就创建文件夹爱
            dest.mkdirs();
        }
         
        //遍历文件夹
        File[] f = src.listFiles(); //得到文件夹下所有(文件/文件夹)的文件数组
        for (File file : f) {       
            if (file.isFile()) {    //筛选文件
                //在目标文件下,创建复制的文件名
                File newFile = new File(dest,file.getName());
                //然后传入遍历的文件绝对路径,和复制的文件绝对路径
                copyFile(file.getAbsolutePath(), newFile.getAbsolutePath());
            }
            if (file.isDirectory()) {//筛选文件夹
                //新建复制的文件夹名
                File newFolder = new File(dest,file.getName());
                //传入遍历的每一个文件夹路径,和复制的文件夹路径,一直循环整个方法,直到文件复制结束
                copyFolder(file.getAbsolutePath(), newFolder.getAbsolutePath());
            }
        }
    }
    //复制文件的方法
    public static void copyFile(String srcFile, String destFile){
        //文件路径
        File src= new File(srcFile);
        File dest = new File(destFile);
          
        //缓存区
        byte[] fileContent = new byte[1024];
          
        //输入输出
        try (
                FileInputStream fis = new FileInputStream(srcFile);
                FileOutputStream fos = new FileOutputStream(destFile);){
            while (true) {                 
                int i = fis.read(fileContent);
                if (i == -1) {            
                    break;
                }
                fos.write(fileContent,0,i);
            }
                  
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    }
}

练习-查找文件内容 

public static void search(File folder, String search);


假设你的项目目录是 e:/project,遍历这个目录下所有的java文件(包括子文件夹),找出文件内容包括 Magic的那些文件,并打印出来。

 

package file;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
 
public class search_mmagic {
    public static void main(String[] args){
        File f=new File("F:\\java\\demo\\example\\src");
        try{
            dfs(f);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    public static void dfs(File file)throws IOException {
        File[] files=file.listFiles();
        for(File f:files){
            if(f.isFile()){
                BufferedReader br=new BufferedReader(new FileReader(f));
                while(true){
                    String line=br.readLine();
                    if(line==null){
                        break;
                    }
                    if(line.contains("Magic")){
                        System.out.println("找到子目标字符串Magic,在文件:"+f.getAbsolutePath());
                        break;
                    }
                }
            }else if(f.isDirectory()){
                dfs(f);
            }
        }
    }
}

流关系图

这个图把本章节学到的流关系做了个简单整理
1. 流分为字节流和字符流
2. 字节流下面常用的又有数据流和对象流
3. 字符流下面常用的又有缓存流

流关系图

 步骤 2 : 

其他流

除了上图所接触的流之外,还有很多其他流,如图所示InputStream下面有很多的子类。 这些子类不需要立即掌握,他们大体上用法是差不多的,只是在一些特殊场合下用起来更方便,在工作中用到的时候再进行学习就行了。

其他流

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值