如果要进行文件内容的操作,必须依靠数据流完成,而数据流分为两种:

字节流:InpuStream(字节输入流)、OutputStream(字节输出流)

字符流:Reader(字符输入流)、Writer(字符输出流)

字符比字节处理的多,但使用哪个,基本流程都一样

范例:

创建File类对象,主要是指明要操作的文件路径

通过字节流或字符流的子类为父类实例化

进行文件的读写操作

关闭数据流(close())

 

字节输出流:OutputStream

字节输出流主要以byte数据为主

 

输出单个字节:public abstract void write(int b) throws IOException

输出全部字节数组:public void write(byte[] b) throws IOException

输出部分字节数组:public void write(byte[] b, int off, int len) throws IOException  //重点

OutputStream是抽象类,文件的输出操作需要子类FileOutputStream,此类有两个常用构造

构造方法:public FileOutputStream(File file) throws FileNotFoundException  新内容覆盖文件

构造方法:public FileOutputStream(File file, boolean append) throws FileNotFoundException  追加文件内容

范例:实现文件的输出(往文件里写内容)

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class Hello{
    public static void main(String[] args) throws Exception {
        //第一步:定义要输出文件的File类对象
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        //输出信息的时候文件可以不存在,目录必须存在
        if(!file.getParentFile().exists()) { //父路径不存在
            file.getParentFile().mkdirs(); //创建父路径
        }
        //第二部:利用OutputStream的子类为父类实例化
        OutputStream output = new FileOutputStream(file);
        //第三步:输出文字信息
        String msg = "******"; //字符串
        //为了方便输出,需要将字符串变为字节数组
        byte data[] = msg.getBytes(); // 变为了字节数组
        output.write(data); //输出数据 , 创建了test.TXT,并写入了*****数据
        output.close();
    }
}

实现只输出部分内容:

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class Hello{
    public static void main(String[] args) throws Exception {
        //第一步:定义要输出文件的File类对象
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        //输出信息的时候文件可以不存在,目录必须存在
        if(!file.getParentFile().exists()) { //父路径不存在
            file.getParentFile().mkdirs(); //创建父路径
        }
        //第二部:利用OutputStream的子类为父类实例化
        OutputStream output = new FileOutputStream(file);
        //第三步:输出文字信息
        String msg = "adfasdfadsfasdfasdfsadfasdf"; //字符串
        //为了方便输出,需要将字符串变为字节数组
        byte data[] = msg.getBytes(); // 变为了字节数组
        output.write(data, 0, 10); //输出数据 , 从0开始输出10个
        output.close();
    }

使用循环的方式单个字节的输出

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class Hello{
    public static void main(String[] args) throws Exception {
        //第一步:定义要输出文件的File类对象
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        //输出信息的时候文件可以不存在,目录必须存在
        if(!file.getParentFile().exists()) { //父路径不存在
            file.getParentFile().mkdirs(); //创建父路径
        }
        //第二部:利用OutputStream的子类为父类实例化
        OutputStream output = new FileOutputStream(file);
        //第三步:输出文字信息
        String msg = "adfasdfadsfasdfasdfsadfasdf"; //字符串
        //为了方便输出,需要将字符串变为字节数组
        byte data[] = msg.getBytes(); // 变为了字节数组
        for(int x = 0; x < data.length; x++) {
            output.write(data[x]); //输出数据 

        }
        output.close();
    }
}

 现在发现每当执行完成后之前的内容都被覆盖了,所以也可以进行数据的追加操作:

范例:追加数据(加入新数据 之前的数据不变 ),第二部加个true

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class Hello{
    public static void main(String[] args) throws Exception {
        //第一步:定义要输出文件的File类对象
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        //输出信息的时候文件可以不存在,目录必须存在
        if(!file.getParentFile().exists()) { //父路径不存在
            file.getParentFile().mkdirs(); //创建父路径
        }
        //第二部:利用OutputStream的子类为父类实例化
        OutputStream output = new FileOutputStream(file, true); //此处为true,表示追加操作
        //第三步:输出文字信息
        String msg = "111adfasdfadsfasdfasdfsadfasdf"; //字符串
        //为了方便输出,需要将字符串变为字节数组
        byte data[] = msg.getBytes(); // 变为了字节数组
        output.write(data); //输出数据 
        output.close();
    }
}

进行换行操作使用“\r\n”

 

 

字节输入流InputStream

可以实现数据读取

三个数据读取方法:

1. 读取单个字节:public abstract int read() throws IOException  //每次执行此方法将读取单个字节的数据,如果已经去取完成了,没数据了,那么最后返回的是-1
2. 读取数据到字节数组中:public int read(byte[]b) throws IOException  //每次讲数据读取到数组中,那么会返回一个读取长度的数据,如果没有数据,返回长度为-1,要考虑两种情况:1.要读取的内容大于开辟数组的内容,长度就是整个数组的长度  2.要读取的内容小于开辟的数组内容,长度就是全部最后的内容长度,数组装不满

3. 读取 部分内容到字节数组:public int read(byte[]b,int off, int len) throws IOException  //每次读取内容到部分字节数组,只允许读取满限制的数组的字节个数,此方法依然返回读取的长度。

InputStream是抽象类,所以要进行文件的读取使用FileInputStream子类,子类定义的构造方法:public FileInputStream(File file) throws FileNotFoundException

范例:实现从txt的数据读取

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;


public class Hello{
    public static void main(String[] args) throws Exception {
        //第一步:定义要输出文件的File类对象
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        //第二步:实例化InputStream类对象
        InputStream input = new FileInputStream(file);
        //第三步:实现数据读取操作
        byte data[] = new byte[1024];
        int len = input.read(data); //将数据读取到数组中
        System.out.println("读取的内容:【" + new String(data,0,len) +"");
        //第四步:关闭输入流
        input.close();
    }
}

read()方法可以实现单个字节数据读取操作,用此方法实现单个字节数据的读取

范例 :读取单个字节”

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;


public class Hello{
    public static void main(String[] args) throws Exception {
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        InputStream input = new FileInputStream(file);
        byte data[] = new byte[1024];
        int foot = 0; //控制保存的角标索引
        int temp =  0; //接收每次保存的数据
        do {
            temp = (byte)input.read(); //读取出来的数据保存到字节数组中
            if(temp != -1) { //现在有数据
                data[foot ++] = (byte) temp;
            }
        } while(temp != -1); //表示后面可能还有数据
        System.out.println("读取的内容:【" + new String(data,0,foot) +"");
        input.close();
    }
}

以上使用了do...while,实际开发中都用while:

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;


public class Hello{
    public static void main(String[] args) throws Exception {
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        InputStream input = new FileInputStream(file);
        byte data[] = new byte[1024];
        int foot = 0; //控制保存的角标索引
        int temp =  0; //接收每次保存的数据
        while((temp = input.read())!= -1 ) {
            data[foot ++] = (byte)temp;
        }
        System.out.println("读取的内容:【" + new String(data,0,foot) +"");
        input.close();
    }
}

 

字符输出流:Writer

Writer是进行字符输出操作的抽象类

之所以提供一个Writer类,是因为这个类的输出方法有一个特别好用的:

输出字符串:public void write(String str) throws IOException   //重点

 

范例:使用Writer输出数据

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;


public class Hello{
    public static void main(String[] args) throws Exception {
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        if(file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        Writer out = new FileWriter(file);
        String data = "测试数据测试数据测试数据测试数据测试数据" ;
        out.write(data); //直接输出字符串
        out.close();
    }
}

若要追加增加数据,之前的不变:

Writer out = new FileWriter(file, true);

虽然Wirter类提供字符数组的输出操作能力,但本质上讲使用Writer类就意味着要执行字符串的直接输出。

 

字符输入流:Reader

数据读取:public int read(char[] cbuf) throws IOException

范例:读取数据

import java.io.File;
import java.io.FileReader;
import java.io.Reader;


public class Hello{
    public static void main(String[] args) throws Exception {
        File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); 
        if(file.exists()) {
            Reader in = new FileReader(file);
            char data[] = new char[1024];
            int len = in.read(data); //向字符数组中保存数据,返回长度
            System.out.println(new String(data, 0, len));
            in.close();
        }
    }
}

Reader与InputStream类相比除了数据类型的差别之外,操作上没有优势

 

 

字节流与字符流的区别

这两种流的区别就好比数据库中的BLOB与CLOB区别

CLOB保存大文本数据,都是字符数据

BLOB保存二进制数据,例如:电影、图片、文字,都是字节数据

通过任何终端(网络、文件)读取或者输出的数据都一定是字节,但字符是通过内存处理后的数据。

字符输入:字符输入的时候字节(磁盘)自动转换为字符(内存)

字符输出:字符(内存)自动转换为字节(磁盘)

在利用字符流输出的时候,所有的内容实际上都只是输出到了缓冲区中(内存)。在使用close()关闭的时候会将缓冲区的数据输出,如果不关闭就不发进行输出,此时可以利用flush()进行强制刷新

字符使用到了缓冲区,而字节没有使用到缓冲区。如果处理中文使用字符流,其他任何数据都是用字节流。

 

综合案例:文件拷贝

是模拟dos系统中的copy命令完成

编写一个文件拷贝程序,可实现任意文件拷贝操作,通过初始化参数输入拷贝的源文件以及拷贝的目标文件路径,本程序暂不考虑类的设计。

dos拷贝命令:“copy 路径1 路径2”

两种实现思路:

  思路一:开辟一个数组,将需拷贝的内容读取到数组之中,而后一次性输出到目标路径中

  思路二:采用边读边写的方式进行拷贝,不是一次性读取

第一种方式如果文件小没问题,5M左右。如果文件大,基本内存就被沾满了,

范例:初期实现

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class CopyDemo {
    public static void main(String[] args) throws Exception {
        if(args.length != 2) { //参数内容必须是两个,一个源文件路径一个目标文件路径
            System.out.println("错误的命令,格式为:CopyDemo 源文件路径 目标文件路径。");
            System.exit(1); //退出程序
        }
        //接下来验证源文件是否存在
        File inFile = new File(args[0]);
        if(!inFile.exists()) {
            System.out.println("路径错误,请确定源文件路径正确。");
            System.exit(1);
        }
        //如果拷贝的目标文件存在,则也不应该进行拷贝
        File outFile = new File(args[1]);
        if(outFile.exists()) { //目标文件已经存在
            System.out.println("拷贝的路径文件已经存在,请更换路径。");
            System.exit(1);
        }    
        long start = System.currentTimeMillis();
        InputStream in = new FileInputStream(inFile);
        OutputStream out = new FileOutputStream(outFile);
        copy(in,out); //开始文件拷贝
        in.close();
        out.close();
        long end = System.currentTimeMillis();
        System.out.println("花费的时间:" + (end - start) );
    }
    public static void copy(InputStream input, OutputStream output) throws Exception {
        int temp = 0; //保存每次读取的字节量
        while((temp = input.read()) != -1 ) { //每次读取一个字节
            output.write(temp);            
        }
    }
}

要先执行以下再输入路径。此种方法只能复制很小的文件。

用数组来提升拷贝性能,可以将数据读取到数组中,而后一次性将数组输出。

修改拷贝方法:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class CopyDemo {
    public static void main(String[] args) throws Exception {
        if(args.length != 2) { //参数内容必须是两个,一个源文件路径一个目标文件路径
            System.out.println("错误的命令,格式为:CopyDemo 源文件路径 目标文件路径。");
            System.exit(1); //退出程序
        }
        //接下来验证源文件是否存在
        File inFile = new File(args[0]);
        if(!inFile.exists()) {
            System.out.println("路径错误,请确定源文件路径正确。");
            System.exit(1);
        }
        //如果拷贝的目标文件存在,则也不应该进行拷贝
        File outFile = new File(args[1]);
        if(outFile.exists()) { //目标文件已经存在
            System.out.println("拷贝的路径文件已经存在,请更换路径。");
            System.exit(1);
        }    
        long start = System.currentTimeMillis();
        InputStream in = new FileInputStream(inFile);
        OutputStream out = new FileOutputStream(outFile);
        copy(in,out); //开始文件拷贝
        in.close();
        out.close();
        long end = System.currentTimeMillis();
        System.out.println("花费的时间:" + (end - start) );
    }
    public static void copy(InputStream input, OutputStream output) throws Exception {
        int temp = 0; //保存每次读取的字节量
        byte data[] = new byte[2048];
        //数据向数组中读取
        while((temp = input.read(data)) != -1 ) { //每次读取一个字节
            output.write(data, 0 , temp); //输出数组            
        }
    }
}

特别快。

对于File、InputStream、OutputStream最直接操作就体现在本程序中

 

posted on 2018-04-16 23:14  lonske  阅读(1306)  评论(0编辑  收藏  举报