IO总结2-字节流及流转换

------- android培训java培训、期待与您交流! ----------

说完了字符流的主要相关类,现在总结一下字节流的主要类:

FileOutputStream

三个主要的方法:

void write(int b) 写入一个int数字对应的byte,这点很奇怪:write(65535)和write(255)得到的结果是一致的,write方法只写入int b的最后面8位(一个byte)。需要注意一下。

void write(byte[] b) 写入一个byte数组。因为是字节流,所以在想用这个方法写入字符串到文件的时候,传入的参数应该是调用String类的getBytes方法将字符串转成byte数组再传入。

void write(byte[] b, int off, int len) 写入一个byte数组的制定起始脚标的长度。这个在拷贝文件或者网络传输的时候会常用的,因为需要数出的数据通常不会正好被byte数组b的长度整除,而是有多余的,这里就需要只输出多余的字节。

一个例子:

import java.io.*;
class Test5 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos1 = new FileOutputStream("D:/abc1.txt");
        for (int x = 1; x < 512; x += 16) {
            fos1.write(x);
        }
        fos1.close();
        FileOutputStream fos2 = new FileOutputStream("D:/abc2.txt");
        fos2.write("我是一个字符串".getBytes());
        fos2.close();
    }
}

用2进制的方式打开abc1.txt,可以看到

clip_image001

第一行是1<x<255时的输出结果

第二行是255<x<512时的输出结果

两者的结果完全一致。

FileInputStream

int read() 从此输入流中读取一个数据字节。返回读到的byte的int形式,到文件末尾返回的是-1。

int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。返回的为读到的数组的长度。到文件末尾返回-1.

int read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。返回的为读到的数组的长度。到文件末尾返回-1.

import java.io.*;
class BinFileReadDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis1 = new FileInputStream("D:/abc1.txt");
        int num1;
        while ((num1 = fis1.read()) != -1) {
            System.out.print(Integer.toHexString(num1) + " ");
        }
        fis1.close();
        System.out.println("");
        System.out.println("--------------------------------------------------");
        FileInputStream fis2 = new FileInputStream("D:/abc2.txt");
        int num2;
        byte[] b = new byte[1024];
        num2 = fis2.read(b);
        System.out.write(b, 0, num2);
        fis2.close();
    }
}

一段拷贝文件的代码

import java.io.*;
class BinFileCopyDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:/abc1.mp3");
        FileOutputStream fos = new FileOutputStream("D:/abc2.mp3");
        byte[] b = new byte[1024 * 1024];
        int len;
        while ((len = fis.read(b)) != -1) {
            fos.write(b, 0, len);
        }
        fis.close();
        fos.close();
    }
}

对于字节流,也有对应的增强类BufferedOutputStream和BufferedInputStream。

一个拷贝文件的例子:

import java.io.*;
class Test5 {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        BufferedInputStream buffis = new BufferedInputStream(
                new FileInputStream("D:/jdk-7u9-windows-x64.exe"));
        BufferedOutputStream buffos = new BufferedOutputStream(
                new FileOutputStream("D:/jdk-7u9-windows-x64-2.exe"));
        int num;
        while ((num = buffis.read()) != -1) {
            buffos.write(num);
        }
        buffis.close();
        buffos.close();
        long end = System.currentTimeMillis();
        System.out.println((end - start) + "毫秒");
    }
}

拷贝jdk-7u9-windows-x64.exe这个90M的文件用了差不多4s时间。而用FileInputStream拷贝(将byte数组设定为1024*1024)的时间却可以做到0.2秒,不懂为什么,超出物理极限了。

上面的例子都是死的,现在尝试一下由用户键盘输入做一些操作:

由用户输入字符串,代码将用户的键盘输入转成大写返回。

import java.io.*;
class UpperCaseUserKeyboardInputDemo1 {
    public static void main(String[] args) throws IOException {
        InputStream in = System.in;
        int b = 0;
        StringBuilder sb = new StringBuilder();
        while (true) {
            b = in.read();
            if ((char) b == '\r')
                continue;
            if ((char) b == '\n') {
                String str = sb.toString();
                if ("over".equals(str))
                    break;
                System.out.println(str.toUpperCase());
                sb.delete(0, sb.length());
            }
            else
                sb.append((char) b);
        }
        in.close();
    }
}

以上代码较为繁琐和臃肿,考虑到处理换行,可使用BufferedReader,而且键盘输入是字节流,还要考虑到字节流向字符流的转换,所以有了下面的代码:

import java.io.*;
class UpperCaseUserKeyboardInputDemo2 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufr = new BufferedReader(new InputStreamReader(
                System.in));
        String line = null;
        while ((line = bufr.readLine()) != null) {
            if ("over".equals(line))
                break;
            System.out.println(line.toUpperCase());
        }
        bufr.close();
    }
}

将键盘录入转成大写后输出到一个文件中的例子:

import java.io.*;
class Test5 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufr = new BufferedReader(new InputStreamReader(
                System.in));
        BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream("d:/www.txt")));
        String line = null;
        while ((line = bufr.readLine()) != null) {
            if ("over".equals(line))
                break;
            bufw.write(line.toUpperCase());
            bufw.newLine();
        }
        bufr.close();
        bufw.close();
    }
}

第一遍看毕老师的视频的时候,觉得输入输出流的问题很纠结,有点没有思路的感觉。在自己翻看API文档后,第二遍看毕老师的视频的时候,就觉得清晰很多了。毕老师总结的很好:

1、 明确源和目的。

2、 明确是字符还是字节。

3、 想使用哪种输入输出方式(及组合)。

其实文件在硬盘上都是2进制的,应该都用FileInputStream来读取。

文件:

我们想读取一个文本文件的时候,应该先用FileInputStream来读取文件,然后把字节流转成字符流来处理。但是有一个类帮助我们做了转换,它就是InputStreamReader,而且它有一个子类,是FileReader,并提供read方法来传出字符所对应的字节。所以直接用FileReader就好了。同时增强类BufferedReader还为我们提供了readLine方法便于我们操作。要想将字符串写入文件时,也有类FileWriter帮我们做了转换。

clip_image002

FileReader在读取文件的时候,是按照指定的字符集(并未提供方法指定字符集)进行转换后,变成字符的,如果需要指定读取文件编码的时候,就需要用到FileInputFileStream把文件转成字节流,然后用InputStreamReader指定其他需要的编码进行转换后再进行操作。对于需要指定编码写入文件的时候同理需要OutputStreamWriter和FileOutPuteStream来操作.

对于字节文件(程序、媒体文件等),由于不涉及到字节流向字符流(或者字符流向字节流)的转换,所以相对来说比较简单。用FileInputSteam读取,将读取到的字节流存在内存中,然后用FileOutPutStream输出即可。

比较特殊的是键盘和控制台显示,也就是System.out和System.in。

查阅文档后:System.out其实是System类中的一个成员,其类型为PrintStream,而 System.in的类型为InputStream(字节流),下面比较一下两个类:

clip_image003

System.err默认使用的也是控制台显示,所以其类型和System.out是一致的。相对System.in,System.out要复杂一些。主要是提供多种数据类型(char、char[]、String、int、long、float、double、居然还有object)的输出方式(print和println)。

另外说一点,System提供了setErr、setIn和setOut方法可将其默认流更改流向。

posted @ 2012-12-29 00:49  qinbin  阅读(447)  评论(0编辑  收藏  举报