Java之IO流---字符流

来源:互联网 发布:企业网络建设方案 编辑:程序博客网 时间:2024/05/21 20:29

1.概述

上篇Java之IO流—字节流我们详细的讲解了IO体系与字节流的各类用法,本篇继续梳理字符流。

1.1 字符流的由来

在上篇,我提到过用字节流读取中文汉字打印在控制台上,会出现乱码的情况,原因就不赘述了。可见,对于字符的操作,强大如斯的字节流也有失利的时候。这个时候我们本篇的主角—字符流就登上了历史的舞台,展现出它强大的魅力。字符流是建立在字节流之上的,它能够提供字符层次的编码和解码

对于字符的操作,我们当然首选字符流。同时,转换流也为我们建立了字节流到字符流的桥梁,使我们对数据的处理更加灵活。但是也要注意一些细节,对于从转换流获得的字符流,它读取的字符必须在编码表中可以查找的到,否则会造成乱码。对于像图片、视频这样的文件就不适宜用字符流来处理。可以这样形象的理解字符流, 字符流 = 字节流+编码表

编码将字符数据转化为字节数据,解码将字节数据转化为字符数据。

1.2 编码表

计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。
就将各个国家的文字用数字来表示,并一一对应,形成一张表。

编码表的定义

编码表由字符及其对应的数值组成的一张表

常见编码表

ASCII:美国标准信息交换码。 一种使用7个或8个二进制位进行编码的方案ISO8859-1:拉丁码表。欧洲码表 用一个字节的8位表示。GB2312:中国的中文编码表。GBK:中国的中文编码表升级,融合了更多的中文文字符号。GB18030:GBK的取代版本BIG-5码 :通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicodeUTF-8:最多用三个字节来表示一个字符。它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:        它将Unicode编码为00000000-0000007F的字符,用单个字节来表示        它将Unicode编码为00000080-000007FF的字符用两个字节表示        它将Unicode编码为00000800-0000FFFF的字符用3字节表示

2. 字符流

字符流失java的io流的两大分支之一,被设计用来处理字符数据,弥补字节流的短板。

Writer与Reader这两个抽象类是所有字符输出流与字符输入流的基类,研究字符流,先从他们开始。

字符流的继承体系在上篇已经展示了,这里不再赘述。

2.1 Writer

写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

2.11 方法摘要

主要定义了写的一些方法规范。在之后讲述中,子类只会给出特有方法以示区别。

 Writer append(char c)           将指定字符添加到此 writer。  Writer append(CharSequence csq)           将指定字符序列添加到此 writer。  Writer append(CharSequence csq, int start, int end)           将指定字符序列的子序列添加到此 writer.Appendable。 abstract  void close()           关闭此流,但要先刷新它。 abstract  void flush()           刷新该流的缓冲。  void write(char[] cbuf)           写入字符数组。 abstract  void write(char[] cbuf, int off, int len)           写入字符数组的某一部分。  void write(int c)           写入单个字符。  void write(String str)           写入字符串。  void write(String str, int off, int len)           写入字符串的某一部分。 

2.12 FileWriter

用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。

文件是否可用或是否可以被创建取决于底层平台。特别是某些平台一次只允许一个 FileWriter(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。

FileWriter 用于写入字符流。要写入原始字节流,请考虑使用 FileOutputStream。

2.12.1 构造方法摘要

FileWriter(File file)           根据给定的 File 对象构造一个 FileWriter 对象。 FileWriter(File file, boolean append)           根据给定的 File 对象构造一个 FileWriter 对象。 FileWriter(FileDescriptor fd)           构造与某个文件描述符相关联的 FileWriter 对象。 FileWriter(String fileName)           根据给定的文件名构造一个 FileWriter 对象。 FileWriter(String fileName, boolean append)           根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 

此类方法与父类同,就不贴出了。

2.12.2使用示例

public class FileWriterReview {    public static final String LINE_SEPARATOR = System.getProperty("line.separator");    public static void main(String[] args) {        FileWriter fw = null;        try {             fw = new FileWriter("fw.txt",true); // 设置true表示附加内容             char[] cbuf = new char[]{'h','e','l','l','o'};            fw.write(cbuf);            fw.write(LINE_SEPARATOR); // 添加换行            fw.write("写入字符串");            fw.append("附加内容");            //Returns the name of the character encoding being used by this stream.             System.out.println(fw.getEncoding());         } catch (IOException e) {            e.printStackTrace();        }finally{            if (fw!=null) {                try {                    fw.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}

2.13 OutputStreamWriter

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset(字符集) 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。

为了获得最高效率,可考虑将 OutputStreamWriter 包装进 BufferedWriter 中,以避免频繁调用转换器。例如:

Writer out = new BufferedWriter(new OutputStreamWriter(System.out));

2.13.1 构造方法摘要

OutputStreamWriter(OutputStream out)           创建使用默认字符编码的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, Charset cs)           创建使用给定字符集的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, CharsetEncoder enc)           创建使用给定字符集编码器的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, String charsetName)           创建使用指定字符集的 OutputStreamWriter。 

2.13.2 使用示例

public class OutputStreamWriterReview {    public static void main(String[] args) throws IOException {//      test1();        test2();    }    private static void test2() throws IOException {       // 通过转换流,将字符以gbk编码成字节并写入本地文件        OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("osw.txt"), "gbk"); // 指定字符集        osw.write('a');        osw.write("编码为");        osw.write("gbk");        osw.write("你解码吧");        osw.write("告诉你一个秘密", 0, 5); // 写入字符串的一部分        osw.flush();        osw.close();    }    private static void test1() throws IOException {        // 往控制台输出        OutputStreamWriter osw=new OutputStreamWriter(System.out,"utf-8");        osw.write("你好");  // 写入缓冲区        osw.flush();            osw.close();        }}

2.14 BufferedWriter

提供缓冲的字符输出流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。

该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义(通过System.getProperty(“line.separator”)来获取)。并非所有平台都使用新行符 (‘\n’) 来终止各行。

通常 Writer 将其输出立即发送到底层字符或字节流,开销很高。所以可以用BufferedWriter 包装这些Writer(如 FileWriters 和 OutputStreamWriters),来提高效率。例如,

 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));

2.14.1 构造方法摘要与方法摘要

1.构造方法摘要

BufferedWriter(Writer out)           创建一个使用默认大小输出缓冲区的缓冲字符输出流。 BufferedWriter(Writer out, int sz)           创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 

2. 方法摘要
列举了它的特有方法

 void newLine()  写入一个行分隔符。 

2.14.2 使用示例

注意:BufferedReader的readLine方法返回的字符串不包含换行符。

public class BufferedWriterReview {    public static void main(String[] args) {        //writeFile1();        writeFile2();    }    /**     * readLine读取一行     * @throws IOException      */    private static void writeFile() throws IOException {        BufferedWriter bw=new BufferedWriter(new FileWriter("bw2.txt"));        BufferedReader br=new BufferedReader(new FileReader("fw.txt"));        String buff=null;        while((buff=br.readLine())!=null){  //读取行,不包含换行符            //将读取的行内容写入文件,偏移量为0,写入长度为buff的长度            bw.write(buff, 0,buff.length());            bw.newLine(); // 添加换行        }        br.close();        bw.close();    }    /**     * read方法     */    private static void writeFile0() throws IOException {        BufferedWriter bw=new BufferedWriter(new FileWriter("bw.txt"));        BufferedReader br=new BufferedReader(new FileReader("fw.txt"));        char buff[] = new char[1024];        int len;        while((len=br.read(buff))!=-1){            bw.write(buff, 0, len);        }        br.close();        bw.close();    }}

2.15 PrintWriter

提供打印功能的字符输出流:向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。

与 PrintStream 类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。

此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用 checkError() 是否出现错误。

2.15.1 构造方法摘要

通过构造方法可知,它即可操作文件,也可以操作字节输出流与字符输出流

PrintWriter(File file)           使用指定文件创建不具有自动行刷新的新 PrintWriter。 PrintWriter(File file, String csn)           创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。 PrintWriter(OutputStream out)           根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。 PrintWriter(OutputStream out, boolean autoFlush)           通过现有的 OutputStream 创建新的 PrintWriter。 PrintWriter(String fileName)           创建具有指定文件名称且不带自动行刷新的新 PrintWriter。 PrintWriter(String fileName, String csn)           创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。 PrintWriter(Writer out)           创建不带自动行刷新的新 PrintWriter。 PrintWriter(Writer out, boolean autoFlush)           创建新 PrintWriter。 

此类方法大抵都是打印功能。

2.15.2 使用示例

启动自动刷新

 ① PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true); ② 还是应该调用println()(printf 或 format)的方法才可以,不仅仅自动刷新了,还实现了数据的换行。

println() 与下面三行等价:

bw.write();bw.newLine();       bw.flush();
public class PrintWriterDemo {    public static void main(String[] args) throws IOException {        // 创建打印流对象        // PrintWriter pw = new PrintWriter("pw2.txt");        PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true); // 设置自动刷新//       pw.print(666);//       pw.print("hello"); // 不会自动刷新        pw.println(666);        pw.println("hello"); // println()(printf 或 format)致使自动刷新        pw.close();    }}   

2.2 Reader

用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

2.21 方法摘要

abstract  void close()           关闭该流并释放与之关联的所有资源。  void mark(int readAheadLimit)           标记流中的当前位置。  boolean markSupported()           判断此流是否支持 mark() 操作。  int read()           读取单个字符。  int read(char[] cbuf)           将字符读入数组。 abstract  int read(char[] cbuf, int off, int len)           将字符读入数组的某一部分。  int read(CharBuffer target)           试图将字符读入指定的字符缓冲区。  boolean ready()           判断是否准备读取此流。  void reset()           重置该流。  long skip(long n)           跳过字符。 

2.22 FileReader

用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。

FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream。

2.22.1 构造方法摘要

FileReader(File file)           在给定从中读取数据的 File 的情况下创建一个新 FileReader。 FileReader(FileDescriptor fd)           在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。 FileReader(String fileName)           在给定从中读取数据的文件名的情况下创建一个新 FileReader。 

2.22.2 使用示例

public class FileReaderReview {    public static void main(String[] args) {        //ReadMethod1();        ReadMethod2();    }    private static void ReadMethod2() {        FileReader fr = null;        try {            fr= new FileReader("fw.txt");            char[] buff = new char[1024];            int len=0;            // 每次将读取的内容放入一个数组缓冲区,读到内容返回读取的字符长度,否则返回-1            while((len=fr.read(buff))!=-1){                  System.out.print(new String(buff, 0, len));            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            if(fr!=null){                try {                    fr.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    private static void ReadMethod1() {        FileReader fr = null;        try {            fr = new FileReader("fw.txt");            int ch;            while((ch=fr.read())!=-1){  // 每次读取一个字符,读完数据返回-1                System.out.print((char)ch);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            if(fr!=null){                try {                    fr.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}

2.23 InputStreamReader

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。

为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:

 BufferedReader in= new BufferedReader(new InputStreamReader(System.in));

2.23.1 构造方法摘要

可以指定字符集来解码字节文件

InputStreamReader(InputStream in)           创建一个使用默认字符集的 InputStreamReader。 InputStreamReader(InputStream in, Charset cs)           创建使用给定字符集的 InputStreamReader。 InputStreamReader(InputStream in, CharsetDecoder dec)           创建使用给定字符集解码器的 InputStreamReader。 InputStreamReader(InputStream in, String charsetName)           创建使用指定字符集的 InputStreamReader。 

特别指出一个特别的方法

String getEncoding() 返回此流使用的字符编码的名称。 

2.23.2 使用示例

public class InputStreamReaderReview {    public static void main(String[] args) throws Exception {        review3();    }    private static void review3() throws IOException, FileNotFoundException {        // 指定按gbk将字节解码为字符读取到输入流中        InputStreamReader isr=new InputStreamReader(new FileInputStream("osw.txt"), "gbk");        char[] cbuf=new char[1024];        int len=-1;        while ((len=isr.read(cbuf))!=-1) {            System.out.println(new String(cbuf, 0, len));        }        isr.close();    }    private static void review1() throws IOException {        // 使用BufferedReader提高效率        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));        String s=null;        while(!(s=br.readLine()).equals("over"))        {            System.out.println(s.toUpperCase());        }        br.close();    }    private static void review2() throws Exception {        FileOutputStream fos=new FileOutputStream(new File("D:\\changeio.txt"));        // 指定以gbk将字符编码成字节写入流中        OutputStreamWriter osw=new OutputStreamWriter(fos,"GBK");         osw.write("设为GBK写入");        osw.close();        FileInputStream fis = new FileInputStream(new File("D:\\changeio.txt"));        // 指定按gbk将字节解码为字符读取到输入流中        InputStreamReader isr = new InputStreamReader(fis,"GBK");         char[] cbuf = new char[1024];        int len=0;        while ((len=isr.read(cbuf))!=-1) {            System.out.println(new String(cbuf,0,len));        }        isr.close();    }}

2.24 BufferedReader

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如,

 BufferedReader in= new BufferedReader(new FileReader("foo.in"));

2.24.1 构造方法摘要与方法摘要

1.构造方法

BufferedReader(Reader in)           创建一个使用默认大小输入缓冲区的缓冲字符输入流。 BufferedReader(Reader in, int sz)           创建一个使用指定大小输入缓冲区的缓冲字符输入流。 

2.特殊方法

String readLine()  读取一个文本行。 

2.24.2 使用示例

注意:使用BufferedReader读取的一行不包含换行符

public class BufferedReaderReview {    public static void main(String[] args) {        readFile();    }    private static void readFile() {        FileReader fr=null;        CustomBufferedReader br=null;        try {            fr=new FileReader("jar.txt");            br = new CustomBufferedReader(fr);            String line=null;            while((line=br.readLine())!=null){//不包含 line-termination characters                System.out.println(br.getLineNumber()+":"+line);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            if(br!=null){                try {                    br.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}

2.24.3 自定义BufferedReader与BufferedWriter

上面用到了CustomBufferedReader,它是一个自定义的BufferedReader。可以戳java学习笔记之BufferedReader与BufferedWriter看看实现

3 RandomAccessFile

RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对文件的随机访问读取和写入。

随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。

通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出 EOFException(是一种 IOException)。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException,而不是 EOFException。需要特别指出的是,如果流已被关闭,则可能抛出 IOException。

3.1 构造方法摘要

RandomAccessFile(String name, String mode)           创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 RandomAccessFile(File file, String mode)           创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。                  将创建一个新的 FileDescriptor 对象来表示此文件的连接。 mode 参数指定用以打开文件的访问模式。    允许的值及其含意为:     值       含意    "r"     以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。      "rw"    打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。      "rws"   打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。      "rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。      "rwd" 模式可用于减少执行的 I/O 操作数量。使用 "rwd" 仅要求更新要写入存储的文件的内容;    使用 "rws" 要求更新要写入的文件内容及其元数据,这通常要求至少一个以上的低级别 I/O 操作。 

3.2 方法摘要

方法大抵都是一些读写操作,下面只列出一些特殊的方法

FileDescriptor getFD()           返回与此流关联的不透明文件描述符对象。  long getFilePointer()           返回此文件中的当前偏移量。  String readLine()           从此文件读取文本的下一行。  String readUTF()           从此文件读取一个字符串。  void writeUTF(String str)           使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。  void seek(long pos)           设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。 int skipBytes(int n)           尝试跳过输入的 n 个字节以丢弃跳过的字节。 

3.3 使用示例

public class RandomAccessFileDemo {    public static void main(String[] args) throws IOException {        //write();        read();    }    /**     * 读取数据     * @throws IOException     */    private static void read() throws IOException {        // 创建随机访问流对象        RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");        String s = raf.readUTF();        System.out.println(s);        System.out.println("当前文件指针的位置是:" + raf.getFilePointer());        int i = raf.readInt();        System.out.println(i);        // 该文件指针可以通过 getFilePointer方法读取,并通过 seek 方法设置。        System.out.println("当前文件指针的位置是:" + raf.getFilePointer());        char ch = raf.readChar();        System.out.println(ch);        System.out.println("当前文件指针的位置是:" + raf.getFilePointer());        // 使用seek跳转到指定位置        raf.seek(16);        ch = raf.readChar();        System.out.println(ch);    }    /**     * 写入数据到文件     * @throws IOException     */    private static void write() throws IOException {        // 创建随机访问流对象        RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");        // 写入        raf.writeUTF("你好啊~");  // utf-8 一个汉字3个字节        raf.writeInt(66);        raf.writeChar('u');        raf.close();    }    // output    //  你好啊~    //  当前文件指针的位置是:12    //  66    //  当前文件指针的位置是:16    //  u    //  当前文件指针的位置是:18    //  u}

源码

0 0
原创粉丝点击