Java I/O流

来源:互联网 发布:python自动化运维系统 编辑:程序博客网 时间:2024/06/05 19:35

首先列出参考资料:

  1. Java中字符与字节的编码关系

  2. java开发之io流

  3. 文件传输基础——Java IO流

由于在实际应用中,字节流远比字符流频率高,所以,本篇文章详细讲解字节流,字符流只是一笔带过。

零.要明确:

  • 输入流里的参数(通常是文件名)是流的起点;

  • 输出流里的参数(同上)是流的终点;

一.文件的编码

  1. Java采用unicode,C语言采用ASCII;

  2. 一些编码方式对字符的处理:

    • unicode中,一个中文或英文字符都占2个字节;

    • gbk中,一个英文字符占1个字节,一个汉字字符占2个字节;

    • utf-8中,一个英文字符占1个字节,一个汉字字符占3个字节(unicode扩展区的一些汉字存储需要4个字节);

    • utf-16中,一个英文字符占2个字节,一个汉字字符占3个字节(unicode扩展区的一些汉字存储需要4个字节);

    • utf-32中,任何字符的存储都要4个字节

通过一个案例帮助理解:

public class ByteTest {    public static void main(String[] args) throws UnsupportedEncodingException {        String str = "浩创Hultron";        //gbk        int byte_len = str.getBytes().length;//byte_len值为11        int len = str.length();//len值为9        System.out.println("***gbk编码方式***");        System.out.println("字节长度为:" + byte_len);        System.out.println("字符长度为:" + len);        System.out.println("系统默认编码方式: " + System.getProperty("file.encoding"));        System.out.println();        //utf-8        int byte_len2 = str.getBytes("utf-8").length;//byte_len值为13        int len2 = str.length();//len值为9        System.out.println("***utf-8编码方式***");        System.out.println("字节长度为:" + byte_len2);        System.out.println("字符长度为:" + len2);        System.out.println();        //utf-16        int byte_len3 = str.getBytes("utf-16").length;//byte_len值为20        int len3 = str.length();//len值为9        System.out.println("***utf-16编码方式***");        System.out.println("字节长度为:" + byte_len3);        System.out.println("字符长度为:" + len3);        System.out.println();        //utf-32        int byte_len4 = str.getBytes("utf-32").length;//byte_len值为36        int len4 = str.length();//len值为9        System.out.println("***utf-32编码方式***");        System.out.println("字节长度为:" + byte_len4);        System.out.println("字符长度为:" + len4);    }}

运行结果:

这里写图片描述

当你的字节序列是某种编码时,这个时候想把字节序列变成字符串,也需要采用同样的编码,即字节序列的编码方式要和字符序列的编码方式相同,否则会出现乱码。

eclipse每个项目指定了编码方式后,它将只认识这种编码方式,但是如果是把其他的文本文件中的内容复制粘贴到项目中的文本文件中,项目会对其进行自动转换。

二.File类的使用

参考资料

以下引自《Java编程思想》第4版

File类这个名字具有误导性;我们可能认为它指代的是文件,实际上,它既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称(我把这理解为文件夹名)。如果它指的是一个文件集,我们就可以对此调用list()方法,这个方法会返回一个字符数组。
我们很容易就可以理解返回的是一个数组而不是某个更具灵活性的类容器,因为元素的个数是固定的,所以如果我们想取得不同的目录列表,只需要在创建一个不同的File对象就可以了。
实际上,FilePath(文件路径)对这个类来说是个更好的名字。

java.io.File类用于表示文件(目录)

File类只用于文件(目录)的信息(名称、大小等),不能用于文件内容的访问

2.1 File类常用 API 介绍

直接在代码里看吧:

public class FileDemo {    public static void main(String[] args) {        //表示文件目录时用两个反斜线,第一个是转义字符        File file =                 new File("C:\\Users\\Think\\Desktop\\博客素材\\Java IO流");        //判断文件是否存在,如果存在,输出true,反之,输出false        System.out.println(file.exists());        //这是一个不存在的文件夹        File file2 = new File("C:\\Users\\Think\\Desktop\\博客素材\\徐正良");        //判断文件是否存在,如果不存在,创建一个        if(!file2.exists()) {            file2.mkdirs();//创建新文件夹        } else {            file2.delete();//删除文件夹        }        //判断file是否是一个目录  如果是目录返回true,如果不是目录or目录不存在返回false        System.out.println(file.isDirectory());        //判断file是否是一个文件        System.out.println(file.isFile());        //这是一个不存在的文件        File file3 = new File("C:\\Users\\Think\\Desktop\\博客素材\\日记1.txt");        if (!file3.exists()) {            try {            //创建一个日记1.txt                file3.createNewFile();            } catch (IOException e) {                e.printStackTrace();            }        } else {            file3.delete();        }        File file4 = new File("C:\\Users\\Think\\Desktop\\博客素材","日记1.txt");        if (!file4.exists()) {            try {                file4.createNewFile();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }else{            file4.delete();        }        //常用的File对象的API        //直接打印File对象,打印的是File对象的目录;file.toString()的内容        System.out.println(file);        //获取File对象的绝对目录        System.out.println(file.getAbsolutePath());        //获取File对象的名字(目录名)        System.out.println(file.getName());        //获取File对象的名字(文件名)        System.out.println(file3.getName());        //获取File对象的父目录        System.out.println(file3.getParent());        System.out.println(file3.getParentFile().getAbsolutePath());    }}

2.2 遍历目录

先构造一个File工具类

//构造一个File工具类,列出File类的一些常用操作,比如过滤,遍历等操作public class FileUtils {    /**     * 列出指定目录下(包括其子目录)的所有文件     * @param dir     * @throws IOException     */    public static void listDirectory(File dir) throws IOException {        if(!dir.exists()) {            throw new IllegalArgumentException("目录: "+ dir + "不存在");        }        if(!dir.isDirectory()){            throw new IllegalArgumentException(dir + "不是目录");        }//      遍历不包含子目录//        String[] filenames = dir.list();//不包含子目录的内容//        for (String string:filenames) {//            //输出文件名//            System.out.println(string);//            //输出文件全名(加目录)//            System.out.println(dir+"\\"+string);//        }        //如果要遍历子目录下的内容就要构造成File对象做递归操作,        //File提供了直接返回File对象的API:listFiles()        File[] files = dir.listFiles();//返回的是直接子目录(文件)的抽象        if(files != null && files.length > 0) {            for (File file:files) {                if (file.isDirectory()) {                    //递归                    listDirectory(file);                }else{                    System.out.println(file);                }            }        }else{            System.out.println(dir.getAbsolutePath());        }    }}

三. RandomAccessFile类的使用

RandomAccessFile类是Java提供的对文件内容的访问类,既可以读文件,也可以写文件

RandomAccessFile类可以随机访问文件,可以访问文件的任意位置

一些要点:

  • Java文件模型:在硬盘上的文件是以“byte byte byte”形式存储的,是数据的集合

  • 打开文件:有两种模式rw(读写)和r(只读)

    RandomAccessFile raf = new RandomAccessFile(file,"rw");
    文件指针:打开文件时,指针在开头,pointer=0;进行读写操作时,指针会往后移动

  • 写方法
    raf.write(int)—>只写一个字节,要写入一个int型数据,则要写四次,写完后指针会指向下一个位置,准备再次写入

  • 读方法
    int b = raf.read()—>读一个字节

  • 文件读写完成以后一定要关闭

3.1 RanddomAccessFile类的基本操作

public class RafDemo {    public static void main(String[] args) throws IOException{        //创建一个目录,不指定绝对路径时,该目录会创建在项目下        File demo = new File("demo");        if(!demo.exists()) {            demo.mkdir();            }        File file = new File(demo,"raf.dat");//以demo为父目录,创建一个raf.dat文件        if(!file.exists())            file.createNewFile();        RandomAccessFile raf = new RandomAccessFile(file,"rw");        //指针的位置        System.out.println(raf.getFilePointer());//初始时为0        raf.write('A');//只写了一个字节,pointer为1        System.out.println(raf.getFilePointer());        raf.write('B');//pointer为2        System.out.println(raf.getFilePointer());        int i = 0x7fffffff;        //用write方法每次只能写一个字节,如果要把i写进去,就要写4次        raf.write(i>>>24);//pointer为3        raf.write(i>>>16);//pointer为4        raf.write(i>>>8);//pointer为5        raf.write(i);//pointer为6        System.out.println(raf.getFilePointer());        //可以直接写一个int,writeInt()的源码实现了上述操作        raf.writeInt(i);//一次性写入了4个字节,pointer为10        System.out.println(raf.getFilePointer());        String s = "中";        byte[] gbk = s.getBytes("gbk");        //直接写入一个字节数组        raf.write(gbk);//pointer为12        System.out.println(raf.getFilePointer());        System.out.println(raf.length());        //读文件,必须把指针移到头部        raf.seek(0);        System.out.println(raf.getFilePointer());        //一次性读取,把文件中的内容都读到字节数组中        byte[] buf = new byte[(int)raf.length()];        raf.read(buf);//将raf读到字节数组buf里        System.out.print(Arrays.toString(buf));        System.out.println();        //以16进制的方式输出        for(byte b:buf) {        System.out.print(Integer.toHexString(b&0xff)+" ");        }        raf.close();    }}

四.字节流

与输入有关的所有类都应该从InputStream继承;InputStream 的作用是用来表示那些从不同数据源产生输入的类

与输出有关的所有类都应该从OutputStream继承;该类别的类决定了输出所要去往的目标

EOF=End Of File,读到-1就读到结尾

4.1 字节流之文件输入流(FileInputStream)

A FileInputStream obtains input bytes from a file in a file system.What files are available depends on the host environment.

FileInputStream is meant for reading streams of raw bytes such as image data.For reading streams of characters,consider using FileReader.

  • 具体实现了在文件上读取数据

FileInputStream 的常用方法:

//in是一个输入流对象int b = in.read();//读一个字节存储到int中(b的低8位)in.read(byte[] buf);//读取数据直接填充到字节数组bufin.read(byte[] buf,int start,int size);//读取数据到字节数组buf,从buf的start位置开始存放size长度的数据

下面的内容引自官方文档。
read

int read()
Reads a byte of data from this input stream. This method blocks if no input is yet available.

Returns int the next byte of data, or -1 if the end of the file is reached. Throws IOException if an I/O error occurs.

int read(byte[] b)

Reads up to b.length bytes of data from this input stream into an array of bytes. This method blocks until some input is available.

Returns int the total number of bytes read into the buffer,or -1 if there is no more data because the end of the file has been reached. Throws IOException if an I/O error occurs.

int read(byte[] b, int off, int len)

Reads up to len bytes of data from this input stream into an array of bytes. If len is not zero, the method blocks until some input is available; Otherwise,no bytes are read and 0 is returned.

Returns int the total number of bytes read into the buffer, or -1 if there is no more data because the end of the file has been reached. Throws NullPointerException If b is null. IndexOutOfBoundsException If off is negative, len is negative, or len is greater than b.lenth-off IOException if an I/O error occurs.

下述工具类中实现了单字节读取批量读取

public class FileInputUtil {    /**     *第一种,单字节读取     * 读取指定文件内容,按照16进制输出到控制台     * 并且每输出10个字节换行     * */    public static void printHex(String fileName) throws IOException {        //把文件作为一个字节流进行读操作        FileInputStream in = new FileInputStream(fileName);        int b;//读出的数据赋值给b        int i = 1;        while((b = in.read()) != -1) {            if(b<=0xf){                //单位数前面补0                System.out.print("0");            }            //以16进制方式输出            System.out.print(Integer.toHexString(b)+" ");            if(i++ % 10 == 0){                System.out.println();            }        }        //IO处理一定要做关闭操作        in.close();    }    /**     * 第二种,批量读取     * 对大文件而言,效率高,也是最常用的读文件的方式     * */    public static void printHexByByteArray(String fileName) throws IOException {        FileInputStream in = new FileInputStream(fileName);        byte[] buf = new byte[8*1024];         //从in中批量读取字节,放入到buf这个字节数组中,         //从第0个位置开始放,最多放buf.length个         //返回的是读到的字节的个数//        当字节数组足够大时,可以一次性读取//        int bytes = in.read(buf,0,buf.length);//        int j = 1;//        for (int i = 0; i<bytes;i++){//            if(buf[i]<=0xf){//                System.out.print("0");//            }//            System.out.print(Integer.toHexString(buf[i])+" ");//            if(j++%10==0){//                System.out.println();//            }//        }        int bytes = 0;        int j = 1;        //当字节数组的大小可能不够时,采用循环方式读取        while((bytes=in.read(buf,0,buf.length))!=-1) {            for(int i = 0;i<bytes;i++){                System.out.print(Integer.toHexString(buf[i]&0xff)+" ");                //byte类型8位,int类型32位,通过 &0xff 将高24位清零                if(j++%10==0){                    System.out.println();                }            }        }        in.close();    }}

4.2 字节流之文件输出流(FileOutputStream)

FileOutputStream—>具体实现了在文件上写出数据

FileOutputStream提供了三个接收不同参数的write()方法,用法与FileInputStream中的read()方法类似:

//out是一个输出流对象out.write(int b);//写一个字节到流(b的低8位)out.write(byte[] buf);//将buf字节数组都写入到流out.write(byte[] buf,int start,int size);//字节数组buf从start位置开始写size长度的字节到流
public class FileOutUtil {    public static void main(String[] args) throws IOException {        //如果该文件不存在,则直接创建;如果存在,则删除后创建        FileOutputStream out =                new FileOutputStream("E:\\demo.dat");        //如果该文件不存在,则直接创建;如果存在,则在文件中追加内容        FileOutputStream out2 =                 new FileOutputStream("E:\\demo.dat",true);        out.write('A');//写出了'A'字符的低8位        //write只能写8位,每写一个int需要写4次每次8位        int a = 10;        out.write(a>>>24);        out.write(a>>>16);        out.write(a>>>8);        out.close();    }}

下面是一个复制文件的方法:

/***一个文件复制(copy)的方法*/    //参数一个是源文件,一个是目标文件    public static void copyFile(File srcFile,File destFile) throws IOException {        if(!srcFile.exists()) {            throw new IllegalArgumentException("文件:"+srcFile+"不存在");        }        if(!srcFile.isFile()) {            throw new IllegalArgumentException(srcFile+"不是文件");        }        FileInputStream in = new FileInputStream(srcFile);        FileOutputStream out = new FileOutputStream(destFile);        byte[] buf = new byte[8*1024];//批量读写        int b;        while ((b = in.read(buf,0,buf.length))!=-1) {            //将读取的字节放到buf里            out.write(buf, 0, b);            out.flush();//最好加上        }        in.close();        out.close();    }

4.3 DataOutputStream&DataInputStream

  • 对”流”功能的扩展,可以更加方便的读取int,long,字符等类型数据

  • DataInputStream: readInt()/readDouble()/readUTF()

  • DataOutputStream: writeInt()/writeDouble()/writeUTF()

看下面的案例:

/** *将数据写出到磁盘上的file:`E:\\data.dat`*/public class DosDemo {    public static void main(String[] args) throws IOException {        String file = "E:\\data.dat";        //DataOutputStream由FileOutputStream嵌套得出        DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));        dos.writeInt(10);        dos.writeLong(10l);        //utf-8编码        dos.writeUTF("中国");        //采用utf-16be编码写出        dos.writeChars("中国");        dos.close();    }}/** *将数据从磁盘上的file:`E:\\data.dat`读入程序*/public class DisDemo {    public static void main(String[] args) throws IOException {        String file = "E:\\data.dat";        DataInputStream dis = new DataInputStream(new FileInputStream(file));        int i = dis.readInt();        System.out.println(i);        long l = dis.readLong();        System.out.println(l);        String s = dis.readUTF();        System.out.println(s);        Char c = dis.readChar();        System.out.println(c);        dis.close();    }}

4.4 BufferedInputStream&BufferedOutputStream

这两个流类为IO流提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能

从应用程序中把数据放入文件,相当于将一缸水倒入到另一个缸中:

FileOutputStream—>write方法相当于一滴一滴的把水”转移”过去

DataOutputStream—>writeXxx()方法相当于一瓢一瓢的转移

BufferedOutputStream—>write()方法更方便,相当于一瓢一瓢先放入桶中,再从桶中倒入缸中

/***利用缓冲区复制(copy)文件*/public static void copyFileByBuffer(File srcFile,File destFile) throws IOException {        if(!srcFile.exists()){            throw new IllegalArgumentException("文件: " + srcFile + "不存在");        }        if(!srcFile.isFile()){            throw new IllegalArgumentException(srcFile+"不是文件");        }        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));        int c;        while((c=bis.read())!=-1){            bos.write(c);            bos.flush();//刷新缓冲区        }        bis.close();        bos.close();    }

五. 字符流

认识文本和文本文件

java中的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)。

文件是byte byte byte ...的数据序列。

文本文件是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储结构。

Reader, Writer都为抽象类。

字符的底层仍然是基本的字节序列。

字符流的输入输出

字符流部分请看我的这篇文章:Java IO 流之字符流

0 0
原创粉丝点击