瘋狂Java學(xué)習(xí)筆記(69)---------Lock">瘋狂Java學(xué)習(xí)筆記(69)---------Lock
961
2025-03-31
瘋狂Java學(xué)習(xí)筆記(55)----------字節(jié)流與字符流
字節(jié)流與字符流
在java.io包中操作文件內(nèi)容的主要有兩大類:字節(jié)流、字符流,兩類都分為輸入和輸出操作。在字節(jié)流中輸出數(shù)據(jù)主要是使用OutputStream完成,輸入使的是InputStream,在字符流中輸出主要是使用Writer類完成,輸入流主要使用Reader類完成。(這四個(gè)都是抽象類)
處理流的用法:
按照流是否直接與特定的地方(如磁盤、內(nèi)存、設(shè)備等)相連,分為節(jié)點(diǎn)流和處理流兩類。
節(jié)點(diǎn)流:可以從或向一個(gè)特定的地方(節(jié)點(diǎn))讀寫數(shù)據(jù)。如FileReader
處理流:是對(duì)一個(gè)已存在的流的連接和封裝,通過所封裝的流的功能調(diào)用實(shí)現(xiàn)數(shù)據(jù)讀寫。
如BufferedReader。處理流的構(gòu)造方法總是要帶一個(gè)其他的流對(duì)象做參數(shù)。一個(gè)流對(duì)象經(jīng)過其他流的多次包裝,稱為流的鏈接。
常用的節(jié)點(diǎn)流
父 ?類 ?InputStream OutputStream Reader Writer
文 ?件 ?*FileInputStream FileOutputStrean FileReader FileWriter 文件進(jìn)行處理的節(jié)點(diǎn)流
數(shù) ?組 ?*ByteArrayInputStream ?ByteArrayOutputStream ?CharArrayReader ?CharArrayWriter
對(duì)數(shù)組進(jìn)行處理的節(jié)點(diǎn)流(對(duì)應(yīng)的不再是文件,而是內(nèi)存中的一個(gè)數(shù)組)
字符串 ?*無 ? ?無 ?StringReader StringWriter 對(duì)字符串進(jìn)行處理的節(jié)點(diǎn)流
管 ?道 ?*PipedInputStream ?PipedOutputStream ?PipedReader ?PipedWriter 對(duì)管道進(jìn)行處理的節(jié)點(diǎn)流
常用處理流(關(guān)閉處理流使用關(guān)閉里面的節(jié)點(diǎn)流)
父 ?類 ?InputStream OutputStream Reader Writer
緩沖流 ?*BufferedImputStrean BufferedOutputStream BufferedReader BufferedWriter ----需要父類作為參數(shù)構(gòu)造,增加緩沖功能,避免頻繁讀寫硬盤,可以初始化緩沖數(shù)據(jù)的大小,由于帶了緩沖功能,所以就寫數(shù)據(jù)的時(shí)候需要使用flush方法。
轉(zhuǎn)換流 ?*InputStreamReader ?OutputStreamWriter- ?要inputStream 或OutputStream作為參數(shù),實(shí)現(xiàn)從字節(jié)流到字符流的轉(zhuǎn)換?數(shù)據(jù)流 ?*DataInputStream DataOutputStream -提供將基礎(chǔ)數(shù)據(jù)類型寫入到文件中,或者讀取出來,為什么要有這個(gè)流呢?
看這樣的分析,如果沒有這種流的話,有一個(gè)long,本身只占8個(gè)字節(jié),如果我要寫入到文件,需要轉(zhuǎn)成字符串,然后在轉(zhuǎn)成字符數(shù)組,那空間會(huì)占用很多,但是有了這種流之后就很方便了,直接將這8個(gè)字節(jié)寫到文件就完了。。是不是既節(jié)約了內(nèi)存空間有讓程序?qū)懫饋砀臃奖愫?jiǎn)單了吶。寫倒是很簡(jiǎn)單,但是讀取的時(shí)候就注意了,根據(jù)讀取的數(shù)據(jù)類型,指針會(huì)往下移,所以你寫的順序必須要和讀的順序一致才能完成你正確的需求。
因此,我們使用處理流時(shí)的典型思路是:使用處理流來包裝字節(jié)流,程序通過處理流來執(zhí)行輸入/輸出功能,讓字節(jié)流與底層的I/O設(shè)備、文件進(jìn)行交互。
輸入輸出流體系:
Java的輸入輸出流提供了近40個(gè)類:看似毫無規(guī)律,但我們可以按功能劃分:如下圖:
通常來說,字節(jié)流的功能比字符流的功能強(qiáng)大,因?yàn)橛?jì)算機(jī)里的所有數(shù)據(jù)都是二進(jìn)制的,你懂得!
但我還是喜歡字符流,直接明了。
轉(zhuǎn)換流
兩個(gè)轉(zhuǎn)換流:
1.將字節(jié)轉(zhuǎn)換成字符流
InputStreamReader:將字節(jié)輸入流轉(zhuǎn)換成字符輸入流,OutPutStreamWriter:將字節(jié)輸出流轉(zhuǎn)換成字符輸出流
2.從字符流到字節(jié)流:可以從字符流中獲取char[]數(shù)組,轉(zhuǎn)換為String,然后調(diào)用String的API函數(shù)getBytes() 獲取到byte[],然后就可以通過ByteArrayInputStream、ByteArrayOutputStream來實(shí)現(xiàn)到字節(jié)流的轉(zhuǎn)換。
package com.haixu.io;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class KeyinTest {
public static void main(String[] args) {
try {
//將system.in對(duì)象轉(zhuǎn)換成Reader對(duì)象
System.out.println("請(qǐng)輸入:");
InputStreamReader reader = new InputStreamReader(System.in);
//將Reader對(duì)象轉(zhuǎn)換成BufferedReader對(duì)象
BufferedReader br = new BufferedReader(reader);
String buffer = null;
//進(jìn)行逐行的讀取
while((buffer = br.readLine()) != null){
//如果讀取的內(nèi)容為exit,則退出程序
if(buffer.equals("exit")){
System.exit(1);
}
System.out.println("輸入內(nèi)容為:" + buffer);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
finally{reader.close();br.close()}//不要忘記關(guān)閉流 我忘記了,后加入的!
弄點(diǎn)高大尚的:
推回輸入流
在輸入輸出流中,有兩個(gè)特殊的流:? PushbackInputStream 和? PushbackReader
Pushback用于輸入流允許字節(jié)被讀取然后返回(即“推回”)到流。PushbackInputStream類實(shí)現(xiàn)了這個(gè)想法。它提供了一種機(jī)制來“窺視”在沒有受到破壞的情況下輸入流生成了什么。
PushbackInputStream有兩個(gè)構(gòu)造函數(shù):
PushbackInputStream(InputStream inputStream)
PushbackInputStream(InputStream inputStream, int numBytes)
第一種形式創(chuàng)建了一個(gè)允許一個(gè)字節(jié)推回到輸入流的流對(duì)象。第二種形式創(chuàng)建了一個(gè)具有numBytes長(zhǎng)度緩沖區(qū)的推回緩沖流。它允許多個(gè)字節(jié)推回到輸入流。除了具有與InputStream相同的方法,PushbackInputStream提供了unread( )方法,表示如下:
void unread(int ch)
void unread(byte buffer[ ])
void unread(byte buffer, int offset, int numChars)
第一種形式推回ch的低位字節(jié),它將是隨后調(diào)用read( )方法所返回的下一個(gè)字節(jié)。第二種形式返回buffer緩沖器中的字節(jié)。第三種形式推回buffer中從offset處開始的numChars個(gè)字節(jié)。如果在推回緩沖器為滿時(shí)試圖返回一個(gè)字節(jié),IOException異常將被引發(fā)。Java 2 對(duì)PushbackInputStream作了一些小的修改:它實(shí)現(xiàn)skip( )方法。
實(shí)例:
package com.haixu.io;
import java.io.FileReader;
import java.io.PushbackReader;
public class PushBackTest {
/**
* 推回輸入流練習(xí)
*
* */
public static void main(String[] args) {
try {
/*
* 創(chuàng)建推回輸入流的對(duì)象,并設(shè)定緩存的大小為:64字節(jié)
* */
PushbackReader pr = new PushbackReader(
new FileReader("E://Java編程//Java06//src//com//haixu//io//PushbackTest.java") , 64);
char [] buf = new char[64];
//用于保存上次讀取的字符串內(nèi)容
String lastContent = "";
int hasRead = 0;
//循環(huán)讀取文件內(nèi)容
while((hasRead = pr.read(buf)) > 0 ){
String content = new String(buf , 0 , hasRead);
int targetIndex = 0 ;
//將上次讀取的內(nèi)容與本次讀取的內(nèi)容拼接起來
//查看是否包含目標(biāo)字符串,如果包含目標(biāo)字符串
if((targetIndex = (lastContent + content).indexOf("new PushbackReader"))>0){
//將本次內(nèi)容與上次內(nèi)容一起推回緩沖區(qū)
pr.unread((lastContent + content).toCharArray());
int len = targetIndex >32 ? 32 : targetIndex;
//指定讀取指定長(zhǎng)度的內(nèi)容
pr.read(buf , 0 , len);
//打印輸出
System.out.println(new String(buf ,0 , len));
System.exit(0);
}else{
System.out.println(lastContent);
//將本次內(nèi)容設(shè)定上次讀取的內(nèi)容
lastContent = content;
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
重定向輸入輸出
Java的標(biāo)準(zhǔn)輸入/輸出分別通過System.in和System.out來代表,在默認(rèn)的情況下分別代表鍵盤和顯示器,當(dāng)程序通過System.in來獲得輸入時(shí),實(shí)際上是通過鍵盤獲得輸入。當(dāng)程序通過System.out執(zhí)行輸出時(shí),程序總是輸出到屏幕。
在System類中提供了三個(gè)重定向標(biāo)準(zhǔn)輸入/輸出的方法
static void setErr(PrintStream err) 重定向“標(biāo)準(zhǔn)”錯(cuò)誤輸出流
static void setIn(InputStream in)??? 重定向“標(biāo)準(zhǔn)”輸入流
static void setOut(PrintStream out)重定向“標(biāo)準(zhǔn)”輸出流
package com.haixu.io;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class RedirectOut {
public static void main(String[] args) {
try {
/*
* 一次性創(chuàng)建PrintStream輸出流
*/
PrintStream ps = new PrintStream(new FileOutputStream("out.text"));
//將標(biāo)準(zhǔn)輸入重定向到ps輸入流中
System.setOut(ps);
//向標(biāo)準(zhǔn)輸入一個(gè)字符串
System.out.println("普通字符串");
//向標(biāo)準(zhǔn)輸出輸出一個(gè)對(duì)象
System.out.println((new RedirectOut()));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
package com.haixu.io;
import java.io.FileInputStream;
import java.util.Scanner;
public class RedirectIN {
public static void main(String[] args) {
try {
//創(chuàng)建FileInputStreamd對(duì)象
FileInputStream fis = new FileInputStream("E://Java編程//Java06//src//com//haixu//io//RedirectIN.java");
//標(biāo)準(zhǔn)的輸入重定向到fis輸入流
System.setIn(fis);
//使用System.in創(chuàng)建Scanner對(duì)象,用于標(biāo)準(zhǔn)的輸入
Scanner sc = new Scanner(System.in);
//回車
sc.useDelimiter("/n");
while(sc.hasNext()){
System.out.println("鍵盤輸入的內(nèi)容" + sc.next());
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
RandomAccessFile
RandomAccessFile是用來訪問那些保存數(shù)據(jù)記錄的文件的,你就可以用seek( )方法來訪問記錄,并進(jìn)行讀寫了。這些記錄的大小不必相同;但是其大小和位置必須是可知的。但是該類僅限于操作文件。
RandomAccessFile不屬于InputStream和OutputStream類系的。實(shí)際上,除了實(shí)現(xiàn)DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也實(shí)現(xiàn)了這兩個(gè)接口),它和這兩個(gè)類系毫不相干,甚至不使用InputStream和OutputStream類中已經(jīng)存在的任何功能;它是一個(gè)完全獨(dú)立的類,所有方法(絕大多數(shù)都只屬于它自己)都是從零開始寫的。這可能是因?yàn)镽andomAccessFile能在文件里面前后移動(dòng),所以它的行為與其它的I/O類有些根本性的不同??偠灾?,它是一個(gè)直接繼承Object的,獨(dú)立的類。
基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream結(jié)合起來,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移動(dòng)用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少字節(jié)數(shù)。此外,它的構(gòu)造函數(shù)還要一個(gè)表示以只讀方式("r"),還是以讀寫方式("rw")打開文件的參數(shù) (和C的fopen( )一模一樣)。它不支持只寫文件。
只有RandomAccessFile才有seek搜尋方法,而這個(gè)方法也只適用于文件。BufferedInputStream有一個(gè)mark( )方法,你可以用它來設(shè)定標(biāo)記(把結(jié)果保存在一個(gè)內(nèi)部變量里),然后再調(diào)用reset( )返回這個(gè)位置,但是它的功能太弱了,而且也不怎么實(shí)用。
內(nèi)存映射文件
內(nèi)存映射文件能讓你創(chuàng)建和修改那些因?yàn)樘蠖鵁o法放入內(nèi)存的文件。有了內(nèi)存映射文件,你就可以認(rèn)為文件已經(jīng)全部讀進(jìn)了內(nèi)存,然后把它當(dāng)成一個(gè)非常大的數(shù)組來訪問。這種解決辦法能大大簡(jiǎn)化修改文件的代碼。
fileChannel.map(FileChannel.MapMode mode, long position, long size)將此通道的文件區(qū)域直接映射到內(nèi)存中。注意,你必須指明,它是從文件的哪個(gè)位置開始映射的,映射的范圍又有多大;也就是說,它還可以映射一個(gè)大文件的某個(gè)小片斷。
MappedByteBuffer是ByteBuffer的子類,因此它具備了ByteBuffer的所有方法,但新添了force()將緩沖區(qū)的內(nèi)容強(qiáng)制刷新到存儲(chǔ)設(shè)備中去、load()將存儲(chǔ)設(shè)備中的數(shù)據(jù)加載到內(nèi)存中、isLoaded()位置內(nèi)存中的數(shù)據(jù)是否與存儲(chǔ)設(shè)置上同步。這里只簡(jiǎn)單地演示了一下put()和get()方法,除此之外,你還可以使用asCharBuffer( )之類的方法得到相應(yīng)基本類型數(shù)據(jù)的緩沖視圖后,可以方便的讀寫基本類型數(shù)據(jù)。
/*
* 程序功能:演示了RandomAccessFile類的操作,同時(shí)實(shí)現(xiàn)了一個(gè)文件復(fù)制操作。
*/
package com.lwj.demo;
import java.io.*;
public class RandomAccessFileDemo {
public static void main(String[] args) throws Exception {
RandomAccessFile file = new RandomAccessFile("file", "rw");
// 以下向file文件中寫數(shù)據(jù)
file.writeInt(20);// 占4個(gè)字節(jié)
file.writeDouble(8.236598);// 占8個(gè)字節(jié)
file.writeUTF("這是一個(gè)UTF字符串");// 這個(gè)長(zhǎng)度寫在當(dāng)前文件指針的前兩個(gè)字節(jié)處,可用readShort()讀取
file.writeBoolean(true);// 占1個(gè)字節(jié)
file.writeShort(395);// 占2個(gè)字節(jié)
file.writeLong(2325451l);// 占8個(gè)字節(jié)
file.writeUTF("又是一個(gè)UTF字符串");
file.writeFloat(35.5f);// 占4個(gè)字節(jié)
file.writeChar('a');// 占2個(gè)字節(jié)
file.seek(0);// 把文件指針位置設(shè)置到文件起始處
// 以下從file文件中讀數(shù)據(jù),要注意文件指針的位置
System.out.println("——————從file文件指定位置讀數(shù)據(jù)——————");
System.out.println(file.readInt());
System.out.println(file.readDouble());
System.out.println(file.readUTF());
file.skipBytes(3);// 將文件指針跳過3個(gè)字節(jié),本例中即跳過了一個(gè)boolean值和short值。
System.out.println(file.readLong());
file.skipBytes(file.readShort()); // 跳過文件中“又是一個(gè)UTF字符串”所占字節(jié),注意readShort()方法會(huì)移動(dòng)文件指針,所以不用加2。
System.out.println(file.readFloat());
//以下演示文件復(fù)制操作
System.out.println("——————文件復(fù)制(從file到fileCopy)——————");
file.seek(0);
RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");
int len=(int)file.length();//取得文件長(zhǎng)度(字節(jié)數(shù))
byte[] b=new byte[len];
file.readFully(b);
fileCopy.write(b);
System.out.println("復(fù)制完成!");
}
}
/**
*
* @param skip 跳過多少過字節(jié)進(jìn)行插入數(shù)據(jù)
* @param str 要插入的字符串
* @param fileName 文件路徑
*/
public static void beiju(long skip, String str, String fileName){
try {
RandomAccessFile raf = new RandomAccessFile(fileName,"rw");
if(skip < 0 || skip > raf.length()){
System.out.println("跳過字節(jié)數(shù)無效");
return;
}
byte[] b = str.getBytes();
raf.setLength(raf.length() + b.length);
for(long i = raf.length() - 1; i > b.length + skip - 1; i--){
raf.seek(i - b.length);
byte temp = raf.readByte();
raf.seek(i);
raf.writeByte(temp);
}
raf.seek(skip);
raf.write(b);
raf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Java 數(shù)據(jù)結(jié)構(gòu)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。