1- 介绍
在前面的指导性文章,我们介绍了输入输出二进制流。学习的输入输出字符流之前你需要去了解它。也可以看看这里:
2- 二进制流和字符流之间的差异
二进制流,每一个读出(相当于8位)的一个字节。
同时,字符流读取每个读数又将一个字符。它是依赖于编码(UTF-8,UTF-16,...)的类型,以决定在每个读数反过来这是1字节,2字节,或3个字节的字节数。让我们来看看下面的图示说明:
UTF-16:
这是日本文字。如果它被存储在一个以UTF-16编码的文件,在硬盘驱动器上的字节类似于图片:
-
两个首字节(254,255)的意思是通知一系列的UTF-16编码的开始
-
接下来是由2个字节编码的字符
-
例如,J字符被编码用2个字节(0和74)
-
P字符用2字节(0和80)编码
- .....
-
-
使用UTF-16编码读取文件时,会排除两个首字节并读取两个连续字节
UTF-8:
上述日本文本记录使用UTF-8编码是有所不同的。可以看到,字节存储在硬盘驱动器:
-
对于正常(latin)的拉丁字符,只需要1个字节来存储。
-
例如,它需要1个字节来存储一个字符(74)
-
它需要1个字节来存储P字符(80)
-
-
这可能需要2个字节或3个字节来存储其它字符
-
在读取规则,它有标准的UTF-8字符的**表
-
读取第一字节,如果是<=127,它是一个ASCII字符
-
相反,如果是>127,它将需要继续读第二个字节,并考虑这两个字节是否为1个字符或不是,就可以知道字符是什么。
-
如果在前面的步骤中,并没有一个字符,它将进行读取第三个字节并全部加入到一个字符。
-
综上所述,当使用任何编码保存文档,需要用对应的编码来读取,否则读取输出将是错误的。
3- 字符流的概述
这是字符流的类层次结构:
4- java.io.Reader类
Reader是一个抽象类。读取字符流是从这个类扩展的。
创建一个文件 test_reader.txt 来使用 Reader 的例子:
- HelloReader.java
package com.yiibai.tutorial.javaio.readerwriter; import java.io.FileReader; import java.io.IOException; import java.io.Reader; public class HelloReader { public static void main(String[] args) throws IOException { // Character stream, read a file // FileReader read file with default encoding of machine running this code. Reader r = new FileReader("test_reader.txt"); int i = -1; // Read one character (return int) while ((i = r.read()) != -1) { // Cast to char. System.out.println((char) i); } r.close(); } }
运行例子结果:
下一个例子是依次读取了许多字符,它们被分配一个临时的数组中。这有助于提高相对于依次读取每个字符的程序效率。
- HelloReader2.java
package com.yiibai.tutorial.javaio.readerwriter; import java.io.FileReader; import java.io.IOException; import java.io.Reader; // This example, read multi characters in once. public class HelloReader2 { public static void main(String[] args) throws IOException { // Character stream, read a file // FileReader read file with default encoding of machine running this code. Reader r = new FileReader("test_reader.txt"); // Create temporary array of characters. char[] temp = new char[10]; int i = -1; // Method read(char[]): // Reads characters into an array. // The number of characters read. // or -1 if the end of the stream has been reached while ((i = r.read(temp)) != -1) { String s = new String(temp, 0, i); System.out.println(s); } r.close(); } }
5- java.io.Writer类
Writer类是一个抽象类。所有输出字符流都是从这个类扩展的。
- HelloWriter.java
package com.yiibai.tutorial.javaio.readerwriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; public class HelloWriter { public static void main(String[] args) throws IOException { File dir = new File("C:/test"); // Create directories, if it not exists. dir.mkdirs(); // Create character stream to write to file. // Using default encoding of machine running this code. Writer w = new FileWriter("C:/test/test_writer.txt"); // Array of characters. char[] chars = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'r', 'i', 't', 'e', 'r' }; // Write characters to stream. for (int i = 0; i < chars.length; i++) { char ch = chars[i]; // Cast to int. int j = (int) ch; // Write to stream. w.write(j); } // Close stream, w.close(); } }
运行示例的结果:
下一个例子是在同一时间向流写入多个字符。具体来说,我们写入字符流的一个数组。这有助于提高相对于依次写入每个字符的程序的效率。
- HelloWriter2.java
package com.yiibai.tutorial.javaio.readerwriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; public class HelloWriter2 { public static void main(String[] args) throws IOException { File dir = new File("C:/test"); // Create directories, if it not exists. dir.mkdirs(); // Create character stream to write file. Writer w = new FileWriter("C:/test/test_writer2.txt"); char[] chars = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'r', 'i', 't', 'e', 'r' }; // Write characters to stream. w.write(chars); // Typically Java cache used to store data (in memory) // when the buffer is full, it pushes the data to the file. // You can actively push data into the file. w.flush(); // Write 'new line' character to stream.s w.write('\n'); String s = "FileWriter"; // Write a String to stream. w.write(s ); // Close stream. // It will push the data in buffer to the file, and close stream. // Finish write file. w.close(); } }
运行示例的结果:
6- 二进制流如何转换成字符流?
当我们有一个二进制流。并且希望将它转换成字符流,怎么做?
在上面的例子中,我们习惯于使用Reader 和 Writer。下一个例子显示读取和写入到流中,要有明确的编码规定。
创建一个文件:test_utf8.txt
- test_utf8.txt
JP日本-八洲
当保存,eclipse会问你要保存成为什么类型的编码。选择UTF-8?
- InputStreamReaderExample.java
package com.yiibai.tutorial.javaio; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; public class InputStreamReaderExample { public static void main(String[] args) throws IOException { // Create binary stream, read a file. InputStream in = new FileInputStream("test_utf8.txt"); // Create character stream from binary stream. // encoding UTF-8 Reader reader = new InputStreamReader(in, "UTF-8"); int i = 0; // Read turn each character while ((i = reader.read()) != -1) { // cast int to char, and print to the Console System.out.println((char) i + " " + i); } reader.close(); } }
运行示例的结果如下:
下一个例子是使用UTF-8编码写入到文件
- OutputStreamWriterExample.java
package com.yiibai.tutorial.javaio; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; public class OutputStreamWriterExample { public static void main(String[] args) throws IOException { File dir = new File("C:/test"); // Create directories if it not exists. dir.mkdirs(); // Create binary output stream, write to a file. OutputStream out = new FileOutputStream("C:/test/test_write_utf8.txt"); // Create character stream from binary stream. // encoding UTF-8. Writer writer = new OutputStreamWriter(out, "UTF-8"); String s = "JP日本-八洲"; writer.write(s); writer.close(); } }
运行示例的结果:
7- java.io.BufferedReader类
// If you want to read each line of data of a text file. BufferedReader is a good choice. // As a direct subclass of the Reader class. // Constructor public BufferedReader(Reader in); // Create BufferedBuffer object, wrap Reader object. // Utility methods of BufferedReader : // Read a line. public String readLine(); // The code example: // Create stream read a file. Reader r=new FileReader("C:/test.txt"); BufferedReader br=new BufferedReader(r); // The code example: InputStream in = new FileInputStream("C:/test.txt"); Reader r = new InputStreamReader(in, "UTF-8"); BufferReader br = new BufferedReader(r);
如果想要读的文本文件的每一行数据。 BufferedReader是一个不错的选择。
- test_multi_lines.txt
## Fruit List Apricots Barbados Cherries Bitter Melon Cherimoya Honeydew Jackfruit Limes Lychee Mango Oranges Pineapple Strawberries
- BufferedReaderExample.java
package com.yiibai.tutorial.javaio.buffered; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; public class BufferedReaderExample { public static void main(String[] args) throws IOException { InputStream in = new FileInputStream("test_multi_lines.txt"); Reader reader = new InputStreamReader(in, "UTF-8"); BufferedReader br = new BufferedReader(reader); String s = null; int i = 0; // Read each line of data // If returns null means ending stream. while ((s = br.readLine()) != null) { i++; System.out.println(i + " : " + s); } br.close(); } }
运行示例的结果如下:
8- java.io.BufferedWriter类
// BufferedWriter class is a direct subclass of Writer. // Constructor // Create BufferedWriter object, wrap other Writer object. public BufferedWriter(Writer out); // Methods of BufferedWriter. // Equivalent to calling write ('\ n'); public String newLine(); // The code example: // Create character stream to write file. Writer w=new FileWriter("C:/jhelp/test_bufferedWriter.txt"); // Create BufferedWriter wrap 'w' BufferedWriter bw=new BufferedWriter(w); bw.write("Hello.."); // Print 'new line' bw.newLine();
9- java.io.FilterReader类
FilterReader是有选择地从字符流读取所需的字符。例如,读取包含HTML标记的文本文件,并在标签中排除字符。你需编写 FileReader 的子类,然后再使用子类。我们不能直接使用 FileReader,因为它是一个抽象类。
例如,流读取过滤字符,读取HTML不过在标签中忽略字符。
例如,输入 "<h1>Hello</h1>" ==> 输出: "Hello".
- RemoveHTMLReader.java
package com.yiibai.tutorial.javaio.filter; import java.io.FilterReader; import java.io.IOException; import java.io.Reader; public class RemoveHTMLReader extends FilterReader { // Used to remember whether we are "inside" a tag boolean intag = false; public RemoveHTMLReader(Reader in) { super(in); } /** * This is the implementation of the no-op read() method of FilterReader. It * calls in.read() to get a buffer full of characters, then strips out the * HTML tags. (in is a protected field of the superclass). */ @Override public int read(char[] buf, int from, int len) throws IOException { // how many characters have been read int charCount = 0; // Loop, because we might read a bunch of characters, then strip them // all out, leaving us with zero characters to return. while (charCount == 0) { // Read characters charCount = super.read(buf, from, len); if (charCount == -1) { // Check for EOF and handle it. return -1; } // Loop through the characters we read, stripping out HTML tags. // Characters not in tags are copied over previous tags // Index of last non-HTML char int last = from; for (int i = from; i < from + charCount; i++) { // If not in an HTML tag if (!intag) { if (buf[i] == '<') { // Check for tag start intag = true; } else { // and copy the character buf[last++] = buf[i]; } } else if (buf[i] == '>') { // check for end of tag intag = false; } } // Figure out how many characters remain charCount = last - from; } // Then return that number. return charCount; } /** * This is another no-op read() method we have to implement. We implement it * in terms of the method above. Our superclass implements the remaining * read() methods in terms of these two. */ @Override public int read() throws IOException { char[] buf = new char[1]; int result = read(buf, 0, 1); if (result == -1) { return -1; } else { return (int) buf[0]; } } }
- RemoveHTMLReaderTest.java
package com.yiibai.tutorial.javaio.filter; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; public class RemoveHTMLReaderTest { public static void main(String[] args) throws IOException { // Create Reader object from StringReader constructor. Reader in = new StringReader("<h1>Hello \n <b>World</b><h1>"); RemoveHTMLReader filterReader = new RemoveHTMLReader(in); BufferedReader br = new BufferedReader(filterReader); String s = null; while ((s = br.readLine()) != null) { System.out.println(s); } br.close(); } }
运行示例的结果:
10- java.io.FilterWriter类
FilterWriter写入字符流,选择写入所需的的字符。通常情况下,你所编写的FilterWriter子类,可以覆盖它的方法,并以你的方式写入数据流中。
下面的例子演示了数据写入流中时如何转换。它可以被认为这是加密的简单方式。
- Rot13.java
package com.yiibai.tutorial.javaio.filter; public class Rot13 { /** * <pre> * a ==> n * b ==> o * c ==> p * d ==> q * e ==> r * ... * y ==> l * z ==> m * </pre> */ public static int rotate(int inChar) { int outChar; if (inChar >= (int) 'a' && inChar <= (int) 'z') { outChar = (((inChar - 'a') + 13) % 26) + 'a'; } else if (inChar >= (int) 'A' && inChar <= (int) 'Z') { outChar = (((inChar - 'A') + 13) % 26) + 'A'; } else { outChar = inChar; } return outChar; } // Test public static void main(String[] args) { for(char ch='a'; ch<='z';ch++ ) { char m= (char)rotate(ch); System.out.println("ch="+ch+" ==> "+ m); } } }
- RotateWriter.java
package com.yiibai.tutorial.javaio.filter; import java.io.FilterWriter; import java.io.IOException; import java.io.Writer; public class RotateWriter extends FilterWriter { // must provide constructor to extend FilterWriter; // objective is to allow constructing a filter stream // connecting to any character output stream (class Writer) public RotateWriter(Writer out) { super(out); } // override one or more write methods to perform filtering // (we override both to be safe) @Override public void write(int outChar) throws IOException { super.write(Rot13.rotate(outChar)); } @Override public void write(char[] cbuf, int offset, int length) throws IOException { char[] tempbuf = new char[length]; for (int i = 0; i < length; i++) { tempbuf[i] = (char) Rot13.rotate(cbuf[offset + i]); } super.write(tempbuf, 0, length); } }
- RotateWriterTest.java
package com.yiibai.tutorial.javaio.filter; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; public class RotateWriterTest { public static void main(String[] args) throws IOException { String s="abcdef"; Writer writer= new StringWriter(); RotateWriter rw= new RotateWriter(writer); rw.write(s.toCharArray(),0,s.length()); rw.close(); String rotateString = writer.toString(); System.out.println("rotateString="+ rotateString); } }
运行示例的结果如下:
11- java.util.PushbackReader类
PushbackReader类允许一个或多个字符返回到输入流。 这使您可以在输入流中向前查看。下面是它的两个构造函数:
public PushbackReader(Reader inputStream) public PushbackReader(Reader inputStream, int bufSize)
还有一些其它方法:
// Pushes back a single character by copying it to // the front of the pushback buffer. // (like - move the cursor back one position)** public void unread(int c) throws IOException
- PushbackReaderDemo.java
package com.yiibai.tutorial.javaio.pushback; import java.io.CharArrayReader; import java.io.IOException; import java.io.PushbackReader; class PushbackReaderDemo { public static void main(String args[]) throws IOException { String s = "if (a == 4) a = 0;\\n"; char buf[] = new char[s.length()]; s.getChars(0, s.length(), buf, 0); CharArrayReader in = new CharArrayReader(buf); PushbackReader f = new PushbackReader(in); int c; while ((c = f.read()) != -1) { switch (c) { // Found character '=' case '=': // Read next character, (after found '-') if ((c = f.read()) == '=') { System.out.print(".eq."); } // If next character different from '='. else { System.out.print("<-"); // Pushes back a single character by copying it to // the front of the pushback buffer. // (like - move the cursor back one position) f.unread(c); } break; default: System.out.print((char) c); break; } } } }
运行示例的结果:
12- java.io.PrintWriter类
// Constructor // PrintWriter is direct subclass of Writer . // It can wrap a character output stream (Writer) or binary output stream (OutputStream), .. public PrintWriter(Writer out) // Wrap a character stream public PrintWriter(Writer out,boolean autoFlush) public PrintWriter(OutputStream out) // Wrap binary stream. public PrintWriter(OutputStream out,boolean autoFlush) public PrintWriter(String fileName) ... // Some methods: public void println(String s) public void print(char ch)
- StackTraceToFile.java
package com.yiibai.tutorial.javaio.printwriter; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.io.Writer; public class StackTraceToFile { public static void main(String[] args) { try { // Do something here // Exception, error divided by 0. int i = 10 / 0; } catch (Exception e) { System.out.println("EXCEPTION ...."); try { File dir = new File("C:/test"); // Create directories if it not exists. dir.mkdirs(); // Create stream to write file. Writer w = new FileWriter("C:/test/stackTrace.txt"); // Create PrintWriter object wrap 'w' // Data written to the PrintWriter will be pushed into 'w'. PrintWriter pw = new PrintWriter(w); // Write 'stack trace' to 'pw'. e.printStackTrace(pw); System.out.println("Finish !"); } catch (Exception e1) { System.out.println("Error:" + e); } } } }
- StackTraceToString.java
package com.yiibai.tutorial.javaio.printwriter; import java.io.PrintWriter; import java.io.StringWriter; public class StackTraceToString { public static void main(String[] args) { try { // Do something here // Exception, error divided by 0. int i = 1000 / 0; } catch (Exception e) { System.out.println("EXCEPTION ...."); try { StringWriter sw = new StringWriter(); // Create PrintWriter object wrap 'sw' // Data written to the PrintWriter will be pushed into 'sw'. PrintWriter pw = new PrintWriter(sw); // Write 'stack trace' to 'pw'. e.printStackTrace(pw); StringBuffer sb = sw.getBuffer(); String s = sb.toString(); System.out.println("Exception String:"); System.out.println(s); } catch (Exception e1) { System.out.println("Error:" + e); } } } }
运行示例的结果:
13- java.io.CharArrayReader类
- CharArrayReaderDemo.java
package com.yiibai.tutorial.javaio.chararray; import java.io.CharArrayReader; import java.io.IOException; public class CharArrayReaderDemo { public static void main(String args[]) throws IOException { String tmp = "abcdefghijklmnopqrstuvwxyz"; int length = tmp.length(); char c[] = new char[length]; tmp.getChars(0, length, c, 0); CharArrayReader input1 = new CharArrayReader(c); CharArrayReader input2 = new CharArrayReader(c, 0, 5); int i; System.out.println("input1 is:"); while ((i = input1.read()) != -1) { System.out.print((char) i); } System.out.println(); System.out.println("input2 is:"); while ((i = input2.read()) != -1) { System.out.print((char) i); } System.out.println(); } }
运行示例的结果:
14- java.io.CharArrayWriter类
还有一些其它方法:
// Writes the contents of the buffer to another character stream. public void writeTo(Writer out) throws IOException
- CharArrayWriterDemo.java
package com.yiibai.tutorial.javaio.chararray; import java.io.CharArrayWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class CharArrayWriterDemo { public static void main(String args[]) throws IOException { char c[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; CharArrayWriter out = new CharArrayWriter(); out.write(c); File dir = new File("C:/test"); dir.mkdirs(); FileWriter f1 = new FileWriter(new File("C:/test/a.txt")); // File written successfully. out.writeTo(f1); FileWriter f2 = new FileWriter(new File("C:/test/b.txt")); // File written successfully. out.writeTo(f2); f1.close(); f2.close(); // CharArrayWriter is closed. out.close(); FileWriter f3 = new FileWriter(new File("C:/test/c.txt")); // Write again to a file. // No Exception from CharArrayWriter but no data will be written. out.writeTo(f3); System.out.println("Done"); } }