【轉】輸入/輸出流 - 全面掌握IO
File類:
程序中操作文件和目錄都可以使用File類來完成即不管是文件還是目錄都是使用File類來操作的,File能新建,刪除,重命名文件和目錄,但File不能訪問文件內容本身,如果需要訪問文件本身,則需要使用輸入/輸出流,該類是位于java.io包下的
輸入與輸出IO:
輸入流:只能從中讀取數據,而不能向其中寫入數據(由InputStream(字節流)和Reader(字符流)作為基類)
輸出流:只能向其寫入數據,而不能從中讀取數據(由OutputStream(字節流)和Writer(字符流)作為基類)
java的io總共涉及40多個類,但都是從這四個抽象基類中派生出來的
InputStream最重要的三個read方法:

Reader中的read方法:

從這兩個抽象類提供的方法就可以看出其實功能基本是一樣的,只是操作的數據單元不一樣而已
由于InputStream與Reader都是抽象類,是不能進行實例化的,我們只能用他們的子類來創建實例,它們分別提供了一個子類用于讀取文件的輸入流:FileInputStream和
FileReader,這兩個子類都是節點流(與處理流相對)-----會直接與指定的文件關聯而無包裝。下面代碼演示FileInputStream使用read(byte[] b):
package xidian.sl.io;
import java.io.FileInputStream;
import java.io.FileReader;
public class InputStreamTest {
/**
* 使用FileInputStream讀取該類本身
* */
public static void FileInputStreamTest() throws Exception{
FileInputStream fis = null;
try{
//創建字節輸入流
fis = new FileInputStream("src/xidian/sl/io/InputStreamTest.java");
//創建一個長度為1024的字節數組來存取
byte[] bbuf = new byte[1024];
//用于保存實際讀取的字節數
int hasRead = 0;
//使用循環來進行重復讀取
while((hasRead = fis.read(bbuf))> 0){
//取出字節,將字節數組轉化為字符串輸出
System.out.println(new String(bbuf, 0 , hasRead));
}
}finally{
//關閉文件輸入流
fis.close();
}
}
/**
* 使用FileReader讀取該類本身
* */
public static void FileReaderTest() throws Exception{
FileReader fr = null;
try{
//創建字節輸入流
fr = new FileReader("src/xidian/sl/io/InputStreamTest.java");
//創建一個長度為1024的字節數組來存取
char[] bbuf = new char[40];
//用于保存實際讀取的字節數
int hasRead = 0;
//使用循環來進行重復讀取
while((hasRead = fr.read(bbuf))> 0){
//取出字節,將字節數組轉化為字符串輸出
System.out.println(new String(bbuf, 0 , hasRead));
}
}finally{
//關閉文件輸入流
fr.close();
}
}
public static void main(String[] args) throws Exception{
InputStreamTest.FileInputStreamTest();
InputStreamTest.FileReaderTest();
}
}
可以看到這兩個子類的使用方式可以說是完全一樣的,不過這里要注意一個問題:字節流FileInputStream是根據字節來讀取的,而一個中文是占兩個字節的,如果包含很多中文的文件被字節流分多次進行讀取,可能會造成亂碼,因為有可能會導致剛好將一個中文分兩次讀取,這樣就會亂碼了,因此如果中文包含多的話還是使用字符流FileReader比較好,
在字節流與字符流之間選擇的規律:如果需要進行輸入/輸出的內容是文本內容,則應該考慮使用字符流,如果需要進行輸入/輸出的是二進制內容,則應該考慮使用字節流,因為字節流的功能比字符流強大,計算機中所有的數據都是二進制的,而字節流可以處理所有的二進制文件;
OutputStream中最重要的write方法:

Writer中最重要的write方法:

Writer類中多了兩個對字符串的操作類,因此如果是直接輸出字符串就選用Writer會比較的方便;
與輸入流一樣,輸出流也有兩個文件操作的子類:FileOutputStream和FileWrite
package xidian.sl.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
public class OutputStreamTest {
/**
* 使用字節流輸出
* */
public static void FileOutputStreamTest() throws Exception{
FileInputStream fis = null;
FileOutputStream fos = null;
try{
//創建字節輸入流
fis = new FileInputStream("src/xidian/sl/io/InputStreamTest.java");
//創建字節輸出流
fos = new FileOutputStream("src/xidian/sl/io/Output.txt");
byte[] bt = new byte[40];
int hasRead = 0;
//循環從輸入流中讀取數據
while((hasRead = fis.read(bt))> 0){
//每讀取一個,即寫入文件輸出流,讀了多少就寫多少
fos.write(bt, 0, hasRead);
}
}catch (Exception e) {
e.printStackTrace();
}finally{
/**
* 流在關閉時會自動執行flash,將緩沖中的數據flush到物理節點中
* 所以關閉時很重要的
* */
if(fis != null){
fis.close();
}
if(fos != null){
fos.close();
}
}
}
/**
* 使用字符流輸出字符串會顯得比較的方便
* */
public static void FileWriteTest() throws Exception{
FileWriter fw = null;
try{
//創建字節輸出流
fw = new FileWriter("src/xidian/sl/io/Output.txt");
fw.write("溫州醫學院\r\n");
fw.write("信息與管理專業\r\n");
fw.write("溫州醫學院\r\n");
fw.write("溫州醫學院\n");
fw.write("溫州醫學院");
}catch (Exception e) {
e.printStackTrace();
}finally{
if(fw != null){
fw.close();
}
}
}
public static void main(String[] args) throws Exception{
OutputStreamTest.FileOutputStreamTest();
OutputStreamTest.FileWriteTest();
}
}
上面是節點流的基本使用,下面將了解處理流的使用,處理流會顯得更加的高效
區分節點流于處理流的方法是:只要流的構造器的參數不是一個物理節點,而是已存在的流,那這個流一定是處理流,因為所有的節點流都是直接以物理io節點作為構造器的參數、
(如file)。
舉例:PrintStream處理流來封裝FileOutputStream節點流,進行輸出,由于PrintStream類的輸出功能非常的強大,因此我們需要輸出文本內容一般都會將輸出流包裝成PrintStream后輸出
package xidian.sl.io;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args){
PrintStream ps = null;
try{
//創建一個節點輸出流
FileOutputStream fos = new FileOutputStream("src/xidian/sl/io/Output.txt");
//以PrintStream處理流來包裝FileOutputStream節點流
ps = new PrintStream(fos);
ps.println("普通字符串");
ps.println(new PrintStreamTest());
}catch (Exception e) {
e.printStackTrace();
}finally{
ps.close();
}
}
}
其實我們一直使用的標準輸出System.out的類型都是PrintStream:

從上面的實例就可以看出將節點流封裝成處理流很簡單,只需調用處理流的構造方法來傳入節點流就可以了;而且看到上面流的關閉只是關閉了處理流而未去關閉節點流,這樣做是完全正確的,以后我們在關閉流的時候只需要關閉最上層的處理流即可;
字符流中有兩個特別的流來處理字符串的:StringReader和StringWriter,

可以看到使用時實例化只需要傳入一個字符串即可:例子:
package xidian.sl.io;
import java.io.StringReader;
import java.io.StringWriter;
public class StringNodeTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String src = "你是個神";
StringReader sr = new StringReader(src);
char[] chars = new char[40];
int hasRead = 0;
try{
//采用循環的方式
while((hasRead = sr.read(chars))>0){
System.out.println(new String(chars, 0, hasRead));
}
}catch (Exception e) {
e.printStackTrace();
}finally{
sr.close();
}
//創建StringWriter
StringWriter sw = new StringWriter(40);
sw.write("你是一個大神");
sw.write("你也是一個大神");
System.out.println(sw.toString());
}
}
io系統提供的兩個轉換流:InputStreamReader和OutputStreamWriter,都是將字節流轉化為字符流
在java中是使用System.in來提供鍵盤輸入的,但這個標準輸入流是InputStream類的實例:

而前面講到了當處理的是文本內容時,使用字符流會顯得比較方便,正好鍵盤輸入就是文本的操作,因此我們有必須將System.in轉換為字符流:
package xidian.sl.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class KeyinTest {
public static void main(String[] args){
BufferedReader br = null;
try{
//將System.in對象轉化為Reader對象
InputStreamReader isr = new InputStreamReader(System.in);
//將節點流包裝為處理流
br = new BufferedReader(isr);
String buffer = null;
//采用循環的方式一行一行讀取
while((buffer = br.readLine()) != null){
System.out.print("輸入的內容 = "+ buffer);
}
}catch (Exception e) {
e.printStackTrace();
}finally{
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
PrintStream是有強大的輸出功能,而BufferReader有強大的輸入(即讀取),因此在操作讀取文本內容時盡可能將其轉化為BufferReader,可以方便的使用readLine()方法
接下來最為強大的文件操作類RandomAccessFile來了,這個類既可以向文件輸入數據,也可以輸出數據,并且他與不同的流最大的不同就是“支持文件任意位置的訪問”,即程序可以控制讀取文件哪個位置的內容;

從構造方法上可以看出,除了提供一個文件或文件名外還需要提供一個String參數mode,mode規定了RandomAccessFile類訪問文件的模式:
1. “r”:以只讀的方式打開指定文件
2. “rw”:以讀取,寫入方式打開指定文件,并且文件不存在會自動進行創建
3.“rws”與“rwd”:與“rw”類似,只是要求文件內容或元數據的每個更新都同步寫入底層存儲設備
浙公網安備 33010602011771號