@stormxue
2016-11-29
10:12
字數 18686
閱讀 64
javaIO流
javaIO
其他常用與流有關的對象:
字節流 | 字符流 |
---|---|
文件類 | File |
打印流 | PrintStream、PrintWriter |
管道流 | PipedInputStream、PipedOutputStream |
序列流 | SequenceInputStream |
對象序列化流 | SequenceInputStream |
IO流:用於處理設備上的數據
設備:硬盤,內存,鍵盤錄入
IO流具體分類:
1.根據處理的數據類型不同:字節流和字符流
2.根據流向不同:輸入流和輸出流
3.根據功能不同:節點流和處理流
字節流 | 字符流 | |
---|---|---|
輸入流 | InputStream(讀) | Reader |
輸出流 | OutputStream(寫) | Writer |
字符流的由來:
因爲文件編碼的不同,而有了對字符進行高效操作的字符流對象。
原理:其實就是基於字節流讀取字節時,去查了指定的碼錶。
字節流和字符流的區別:
1.字節流讀取的時候,讀到一個字節就返回一個字節。字符流使用了字節流讀到一個或多個字節(中文對應的字節數是兩個,在UTF-8中是3個字節)時,先去查指定的編碼表,將查到的字符返回。
2.字節流可以處理所有類型數據,如圖片,mp3,avi。而字符流只能處理字符數據。
結論:只要是處理純文本數據,就要優先考慮使用字符流。
節點流和處理流:
①節點流爲可以從一個特定的數據源(節點)讀寫數據。
②處理流是“連接”在已存在的流(節點流或處理流)之上,通過對數據的處理爲程序提供更爲強大的讀寫功能。
IO的體系,所具備的基本功能就有兩個:讀 和 寫。
基本的讀寫操作方式:
因爲數據通常都以文件形式存在,所以就要找到IO體系中可以用於操作文件的流對象。通過名稱可以更容易獲取該對象。因爲IO體系中的子類名後綴絕大部分是父類名稱。而前綴都是體現子類功能的名字。
Reader
|--InputStreamReader
|--FileReader:專門用於處理文件的字符讀取流對象。
Writer
|--OutputStreamReader
|--FileWriter:專門用於處理文件的字符寫入流對象。
Reader中的常見方法(數據單位爲字符16bit):
int read():讀取一個字符。返回的是讀到的那個字符。如果讀到流的末尾,返回-1。
int read(char[]):將讀到的字符存入指定的數組中,返回的是讀到的字符個數,也就是往數組裏裝的元素的個數。如果讀到流的末尾,返回-1。
void close():關閉流釋放內存資源
Writer中常見的方法:
write(int c):將一個字符寫入到流中。
write(char[] cbuf):將一個字符數組寫入到流中。
write(String str):將一個字符串寫入到流中。
flush():刷新流,將流中的數據刷新到目的地中,流還存在。
close():關閉資源,在關閉前會先調用flush(),刷新流中的數據去目的地。
FileWriter:
該類沒有特有的方法,只有自己的構造函數。
特點在於:
1.用於處理文本文件
2.該類中有默認的編碼表
3.該類中有臨時緩衝。
構造函數:在寫入流對象初始化時,必須要有一個存儲數據的目的地。
FileWriter(String filename):調用系統資源,在指定位置,創建一個文件,如果該文件已經存在,會被覆蓋。
FileWriter(String fileName, boolean append):當傳入的boolean類型值爲true時,會在指定文件末尾處進行數據的續寫。
FileReader:
用於讀取文本文件的流對象,用於關聯文本文件。
package com.xue.javaIO;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* Created by xue on 2016-10-12.
*/
public class DemoIO {
public static void main(String[] args) {
DemoIO demoIO = new DemoIO();
//demoIO.Demo001();
//demoIO.Demo002();
demoIO.Demo003();
}
/**
* 將文本數據存儲到一個文件中
* 當指定絕對路徑時,定義目錄分隔符有兩種方式:1.反斜線 \\ 要寫兩個。2.斜線 / 寫一個即可。
*/
public void Demo001() {
FileWriter fw = null;
try {
fw = new FileWriter("demo.txt");
fw.write("abc中國人");
fw.flush();//對緩衝區的數據進行刷新,將數據刷到目的地中。
fw.write("KKK");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println();
}
}
}
}
//讀取一個已有的文本文件,將文本數據打印出來
public void Demo002(){
FileReader fr = null;
try {
fr = new FileReader("D:/webpro/java001.java");
int ch=0;
while ((ch=fr.read())!=-1){
System.out.print((char)ch);
}
fr.close();
} catch (FileNotFoundException e) {
System.out.println("文件沒有找到");
}catch (IOException e){
e.printStackTrace();
}
}
//讀一個字符就存入字符數組裏,讀完1Kb再打印
public void Demo003(){
FileReader fr = null;
try {
fr = new FileReader("D:/webpro/java001.java");
char[] buf = new char[1024];
int len;
while ((len=fr.read(buf))!=-1){
System.out.print(new String(buf,0,len));
}
fr.close();
} catch (FileNotFoundException e) {
System.out.println("文件沒有找到");
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
字符流的緩衝區:
緩衝區的出現提高了對流的操作效率。
原理:其實就是將數組進行封裝。
- 對應的對象:
- BufferedWriter:特有方法:newLine():跨平臺的換行符。
- BufferedReader:特有方法:readLine():一次讀一行,到行標記時,將行標記之前的字符數據作爲字符串返回。當讀到末尾時,返回null。
readLine():方法的原理: 其實緩衝區中的該方法,用的還是與緩衝區關聯的流對象的read方法。只不過每一次讀到一個字符,先不進行具體操作,先進行臨時存儲,當讀取到回車標記時,將臨時容器中存儲的數據一次性返回。
在使用緩衝區對象時,要明確,緩衝的存在是爲了增強流的功能而存在,所以在建立緩衝區對象時,要先有流對象存在。其實緩衝內部就是在使用流對象的方法,只不過加入了數組對數據進行了臨時存儲。爲了提高操作數據的效率。
代碼上的體現:
寫入緩衝區對象。
//建立緩衝區對象必須把流對象作爲參數傳遞給緩衝區的構造函數。
BufferedWriter bufw = new BufferedWriter(new FileWriter("buf.txt"));
bufw.write("abce");//將數據寫入到緩衝區
bufw.flush();//對緩衝區的數據進行刷新。將數據刷到目的地中。
bufw.close();//關閉緩衝區,其實關閉的是被包裝在內部的流對象。
上面代碼使用了匿名的FileWriter流,這個流無法關閉。之所以需要關閉流資源,是爲了釋放資源,因爲io是操作系統的動作,例如如果沒有關閉,那麼這個IO資源就一直被java進程佔用,知道java應用結束,這期間操作系統的其他進程就不能訪問該IO資源了。我的看法是既然是匿名對象,肯定是要被回收的,但是什麼時候回收就不一定了,因此建議你自己處理輸入輸出流的關閉操作。
字節流
抽象基類:InputStream,OutputStream。
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
注意:字符流使用的數組是字符數組。char[] chs
字節流使用的數組是字節數組。byte[] bt
字節流可以操作任何數據。
轉換流
特點:
1.是字節流和字符流之間的橋樑。
2.該流對象中可以對讀取到的字節數據進行指定編碼表的編碼轉換。
什麼時候使用呢?
1.當字節和字符之間有轉換動作時。
2.流操作的數據需要進行編碼表的指定時。
具體的對象體現:
InputStreamReader:字節到字符的橋樑
OutPutStreamWriter:字符到字節的橋樑
這兩個流對象是字符流體系中的成員,它們有轉換作用,而本身又是字符流,所以在構造的時候,需要傳入字節流對象進來。
構造函數:
InputStreamReader(InputStream):通過該構造函數初始化,使用的是本系統默認的編碼表GBK.
InputStreamReader(InputStream,String charSet):通過該構造函數初始化,可以指定編碼表。
OutputStreamWriter(OutputStream)
OutputStreamWriter(OutputStream,String charSet)
操作文件的字符流對象是轉換流的子類。
Reader
|--InputStreamReader
|--FileReader
Writer
|--OutputStreamWriter
|--FileWriter
轉換流中的read方法已經融入了編碼表,在底層調用字節流的read方法時將獲取的一個或者多個字節數據進行臨時存儲,並去查指定的編碼表,如果編碼表沒有指定,查的是默認碼錶,那麼轉換流的read方法就可以返回一個字符,比如中文。
轉換流已經完成了編碼轉換的動作,對於直接操作的文本文件的FileReader而言,就不用在重新定義了,只要繼承轉換流,獲取其方法,就可以直接操作文本文件中的字符數據了。
注意:在使用FileReader操作文本數據時,該對象使用的是默認的編碼表。如果要使用指定編碼表,必須使用轉換流。
FileReader fr = new FileReader("a.txt");//操作a.txt的中的數據使用的本系統默認的GBK。
操作a.txt中的數據使用的也是本系統默認的GBK。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
這兩句的代碼的意義相同。
如果a.txt中的文件中的字符數據是通過utf-8的形式編碼,那麼在讀取時,就必須指定編碼表。那麼轉換流必須使用。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"utf-8");
流操作的基本規律
1.明確數據源和數據匯:其實是爲了明確輸入流還是輸出流
2.明確操作的數據是否是純文本數據:其實是爲了明確字符流還是字節流
數據源:鍵盤System.in,硬盤File開頭的流對象,內存(數組).
數據匯:控制檯System.out,硬盤File開頭的流對象,內存(數組)。
數據源:System.in
既然是源,使用的就是輸入流,可用的體系有InputStream,Reader。因爲鍵盤錄入進來的一定是純文本數據,所以可以使用專門操作字符數據的Reader。
發現System.in對應的流是字節讀取流。所以要將其進行轉換,將字節轉成字符即可。
所以要使用Reader體系中:InputStreamReader
接下來,是否需要提高效率呢?如果需要,那麼就加入字符流的緩衝區:BufferedReader
BufferedReader bur = new BufferedReader(new InputStreamReader(System.in));
數據匯:一個文件,硬盤
既然是數據匯,那麼一定是輸出流,可以用的OutputStream,Writer.往文件中存儲的都是文本數據,那麼可以使用字符流較爲方便:Writer,因爲操作的是一個文件。所以使用Writer中的FileWriter。是否要提高效率呢?是,那就使用BufferedWriter.
BufferedWriter bufr = new BufferedWriter(new FileWriter("a.txt"));
附加需求:希望將這些文本數據按照指定的編碼表存入文件中。既然是文本數據,而且還是寫入到文件中,那麼使用的體系還是Writer。因爲要指定編碼表,所以要使用Writer中的轉換流,OutputStreamWriter。是否要提高效率,是,選擇BufferedWrier。
注意:雖然最終是文件,介是不可以選擇FileWriter。因爲該對象是使用默認編碼表。輸出轉換流要接收一個字節輸出流進來,所以要用OutputStram體系,而最終輸出到一個文件中,那麼就要使用OutputStream體系中可以操作的文件的字節流對象:FileOutputStream.
String charSet="utf-8";
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charSet));
可以和流相關的集合對象Properties.
Map
|-Hashtable
|-Properties
Properties:該集合不需要泛型,因爲該集合中的鍵值對都是String類型。
1.存入健值對:setProperty(key,value);
2.獲取指定鍵對應的值:value getProperty(key);
3.獲取集合中所有鍵元素:Enumeration propertyNames();
在jdk1.6版本給該類提供了一個新的方法:Set<String>stringPropertyNames();
4.列出該集合中的所有鍵值對,可以通過參數打印流指定列出到的目的地。
list(PrintStream);list(PrintWriter);
例:list(System.out):將集合中的鍵值對打印到控制檯。
list(new PrintStream("prop.txt")):將集合中的鍵值對存儲到prop.txt文件中。
5.可以將流中的規則數據加載進行集合,並稱爲鍵值對。
load(inputStream);
jdk1.6版本提供的新方法:load(Reader) 注意:流中的數據要是“鍵=值”的規則數據。
6.可以將集合中的數據進行指定目的的存儲。
store(OutputStram,String comment)方法。
store(Writer,String comment):使用該方法存儲時,會帶着當時存儲的時間。
File類:
該類的出現是對文件系統中的文件以及文件夾進行對象的封裝。可以通過對象的思想來操作文件以及文件夾。
1.構造函數:
File(String filename):將一個字符串路徑(相對或者絕對)封裝成File對象,該路徑是可存在的,也可是是不存在。
File(String parent,String child);
File(File parent,String child);
2.特別的字段:separator:跨平臺的目錄分隔符。
例子:File file = new File("c:"+File.separator+"abc"+File.separator + "a.txt");
3.常見方法
1).創建:
boolean createNewFile()throws IOException:創建文件,如果被創建的文件已經存在,則不創建。
boolean mkdir():創建文件夾。
boolean mkdirs():創建多級文件夾。
2).刪除:
boolean delete():可用於刪除文件或者文件夾。
注意:對於文件夾只能刪除不帶內容的空文件夾,對於帶有內容的文件夾,不可以直接刪除,必須要從裏往外刪除。
void deleteOnExit();刪除動作交給系統完成。無論是否發生異常,系統在退出時執行刪除動作。
3).判斷:
boolean canExecute(); boolean canWrite(); boolean canRead();
boolean exists():判斷文件或者文件夾是否存在。
boolean isFile():判斷File對象中封裝的是否是文件。
boolean isDirectory():判斷File對象中封裝的是否是文件夾。
boolean isHidden():判斷文件或者文件夾是否隱藏。在獲取硬盤文件或者文件夾時,對於系統目錄中的文件,java是無法訪問的,所以在遍歷時,可以避免遍歷隱藏文件。
4).獲取:
getName():獲取文件或者文件夾的名稱。
getPath():File對象中封裝的路徑是什麼,獲取的就是什麼.
getAbsolutePath():無論File對象中封裝的路徑是什麼,獲取的都是絕對路徑。
getParent():獲取File對象封裝文件或者文件夾的你目錄。注意:如果封裝的是相對路徑,那麼返回的是null.
long length:獲取文件大小。
long lastModified():獲取文件最後一次修改時間。
Static File[] listRoots():獲取系統中的有效盤符。
String[] list():獲取指定目錄下當前的文件以及文件夾名稱。
String[] list(Filenamefilter):可以根據指定的過濾器,過濾後的文件及文件夾名稱。
File[] listFiles():獲取指定目錄下的文件以及文件夾對象。
5).重命名:
renameTo(File):
File f1 = new File("C:\a.txt");
File f2 = new File("C:\b.txt");
f1.renameTo(f2);//將c盤下的a.txt文件改名爲b.txt文件。
遞歸
其實就是在使用一個功能過程中,又對該功能進行調用,就出現了函數自身調用自身。
注意:1.一定要限定條件,否則內存溢出。
2.使用遞歸時,調用次數不要過多,否則也會出現內存溢出.
需求:想要列出指定目錄下的文件以及文件夾中的文件(子文件).
public class test002 {
public static void main(String[] args) {
test002 s = new test002();
File file = new File("E:"+File.separator+"工具"+File.separator+"Notepad++");
s.listDir(file);
}
public void listDir(File file){
if(file.isDirectory())//判斷file是否是目錄
{
File [] lists = file.listFiles();
if(lists!=null)
{
for(int i=0;i<lists.length;i++)
{
listDir(lists[i]);//是目錄就遞歸進入目錄內再進行判斷
}
}
}
System.out.println(file);//file不是目錄,就輸出它的路徑名,這是遞歸的出口
}
}
需求2:刪除一個帶內容的目錄
public class test002 {
public static void main(String[] args) {
test002 s = new test002();
File dir = new File("E:"+File.separator+"a");
s.removeDir(dir);
}
public void removeDir(File dir){
File[] files = dir.listFiles();
//intellij idea快速生成增強for循環:files.for
for (File file : files) {
if(file.isDirectory()){
removeDir(file);//如果是目錄,繼續遍歷
}else {
file.delete();//如果不是目錄,就刪除文件
}
}
dir.delete();
}
}
IO包中常見對象
- 字節流
FileInputStream FileOutputStream
BufferedInputStream BufferedOutputStream - 字符流
FileReader FileWriter
BufferedReader BufferedWriter
- 轉換流
InputStreamReader OutputStreamWriter
- 轉換流
- 文件對象
File - 打印流
1.PrintStream:是一個字節打印流,System.out對應的類型就是PrintStream.
它的構造函數可以接收三種數據類型的值。1)字符串路徑;2)File對象;3)OutputStream;
2.PrintWriter:是一個字符打印流,構造函數可以接收四種類型的值。
1)字符串路徑;
2)File對象(以上1、2兩種還可以指定編碼表字符集)
3)OutputStream
4)Writer(對於3、4類型的數據,可以指定自動刷新;自動刷新爲true時,只有三個方法可以用:println,printf,format)
//如果想要既有自動刷新,又可執行編碼,要這樣完成流對象的包裝
PrintWrter pw = new PrintWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8"),true);
//實際中不建議這樣寫,因爲這樣無法手動關閉流。
//如果想要提高效率,還要使用打印方法
PrintWrter pw = new PrintWriter(
new BufferdWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8")),true);
所有的帶File的流對象都可以直接操作File對象。
- 管道流
PipedInputStream
pipedOutputStream
特點:讀取管道流和寫入管道流可以進行連接。
連接方式:1)通過兩個流對象的構造函數;2)通過兩個對象的connect方法。
通常兩個流在使用時,需要加入多線程技術,也就是讓讀寫同時運行。
注意:對於read方法,該方法是阻塞式的,也就是沒有數據的情況,該方法會等待。
管道流的主要作用就是可以進行兩個線程間的通信。一個線程作爲管道輸出流,另一個線程作爲管道輸入流,在啓動線程前,只需要將這兩個線程的管道流連接到一起就可以。這要就很方便的實現了兩個線程間的通信。 - RandomAccessFile
該對象並不是流體系中的一員,對象中封裝了字節流,同時還封裝了一個緩衝區(字節數組),通過內部的指針來操作數組中的數據。
該對象特點:1).該對象只能操作文件,所以構造函數接收兩種類型的參數,字符串路徑和File對象。
2).該對象既可以對文件進行讀取,也可以寫入。在進行對象實例化時,必須要指定該對象的操作模式,r rw等。該對象中有可以直接操作基本數據類型的方法。
比較有特點的方法:skipBytes():跳過指定的字節數。
seek();指定指針的位置。
getFilePointer():獲取指針的位置。
通過這些方法,就可以完成對一個文件數據的隨機訪問,想讀那裏就讀那裏,想寫那裏就寫那裏。
該對象功能,可以讀數據,可以寫入數據,如果寫入位置已有數據,會發生數據覆蓋。也就是可以對數據進行修改。
在使用該對象時,建議數據都是有規則的,或者是分段的。
注意:在實例化時,如果要操作的文件不存在,會自動創建,如果存在,則不會創建,如果存在的文件有數據,那麼在沒有指定指針位置的情況下,寫入數據,會將文件開頭的數據覆蓋。
可用於多線程的下載,也就是通過多線程往一個文件中同時存儲數據。 - 序列流,也稱爲合併流
SequenceInputStream:
特點:可以將多個讀取流合併成一個流,這樣操作起來很方便。
原理:其實就是將每一個讀取流對象存儲到一個集合中,最後一個流對象結尾作爲這個流的結尾。
兩個構造函數:
1、SequenceInputStream(InputStream in1,InputStream in2):可以將兩個讀取流合併成一個流。
2、SequenceInputStream(Enumeration en):可以將枚舉中的多個流合併成一個流。
作用:可以用於多個數據的合併。
注意:因爲Enumeration是Vector中特有的取出方式。而Vector被ArrayList取代。所以要使用ArrayList集合效率更高一些。那麼如何獲取Enumeration呢?
/**
* 將1.txt,2.txt,3.txt 通過合併流的方法,將3個文件中的內存存入到4.txt中
* 提示:用Enumeration需要用到Vector的elements()方法,把流對象都加入到Vector集合中
*/
public class SequenceInputStreamTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ArrayList<FileInputStream> vec = new ArrayList<FileInputStream>();
vec.add(new FileInputStream("E:\\1.txt"));
vec.add(new FileInputStream("E:\\2.txt"));
vec.add(new FileInputStream("E:\\3.txt"));
Enumeration<FileInputStream> en = Collections.enumeration(vec);
SequenceInputStream sin = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("E:\\4.txt");
byte[] bytes = new byte[1024];
int len = 0;
while ((len = sin.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
sin.close();
fos.close();
}
}
對文件的切割這裏就不寫了,百度一下,切割原理:一個讀取流對應多個輸出流。
黑馬程序員:合併流(序列流)的介紹:SequenceInputStream
- 對象的序列化
ObjectInputStream ObjectOutputStream
可以通過這兩個流對象直接操作已有對象並將對象進行本地持久化存儲。存儲後的對象可以進行網絡傳輸。
兩個對象的特有方法:
ObjectInputStream
Object readObject():該方法拋出異常:ClassNotFountException.
ObjectOutputStream
void writeObject(Object):被寫入的對象必須實現一個接口:Serializable,否則會拋出:NotSerializableException
Serializable:該接口其實就是一個沒有方法的標記接口。
用於給類指定一個UID。該UID是通過類中的可序列化成員的數字簽名算出來的一個long型的值。
只要是這些成員沒有變化,那麼該值每次運算都一樣。
該值用於判斷被序列化的對象和類文件是否兼容。
如果被序列化的對象需要被不同的類版本所兼容,可以在類中自定義UID.
定義方式:static final long serialVersionUID =42L;
注意:對應靜態的成員變量,不會被序列化。
對應非靜態也不想被序列化的成員而言,可以通過transient關鍵字修飾。
通常,這兩個對象成對使用。
Java對象序列化
-
操作基本數據類型的流對象
DataInputStream
DataInputStream(InputStream);
方法:int readInt():一次讀取四個字節,並將其轉成int值。
boolean readBoolean():一次讀取一個字節。
short readShort();
long readLong();
剩下的數據類型一樣。
String readUTF():按照utf-8修改版讀取字符。注意,它只能讀writeUTF()寫入的字符數據。
DataOutputStream
DataOutputStream(OutputStream);
方法:writeInt(int):一次寫入四個字節。
注意和write(int)不同。write(int)只將該整數的最低一個8位寫入,剩餘三個8位丟棄。
writeBoolean(boolean);
writeShort(short);
writeLong(long);
剩下的數據類型一樣。
writeUTF(String):按照utf-8修改版將字符數據進行存儲,只能通過readUTF讀取。
通常只要操作基本數據類型的數據。就需要通過DataStram進行包裝。
通常成對使用。 -
操作數組的流對象
1.操作字節數組
ByteArrayInputStream
ByteArrayOutputStream
toByteArray();
toString();
writeTo(OutputStream);
2.操作字符數組
CharArrayReader
CharArrayWriter
對於這些流,源是內存,目的也是內存。
而且這些流並未調用系統資源,使用的就是內存中的數組,所以這些在使用的時候不需要close.
操作數組的讀取流在構造時,必須要明確一個數據源。所以要傳入相對應的數組。
對於操作數組的寫入流,在構造函數可以使用空參數。因爲它內置了一個可變長度數組作爲緩衝區。
這幾個流的出現其實就是通過流的讀寫思想在操作數組。
類似的對象同理:StringReader,StringWriter.
編碼轉換
在io中涉及到編碼轉換的流是轉換流和打印流。
但是打印流只有輸出。
在轉換流中是可以指定編碼表的。默認情況下,都是本機默認的碼錶,GBK.
System.getProperty("file.encoding");
常見碼錶:
ASCII:美國標準信息交換碼,使用的是1個字節的7位來表示該表中的字符。
ISO8859-1:拉丁碼錶,使用1個字節來表示。
GB2312:簡體中文碼錶
GBK:簡體中文碼錶,比GB2312融入更多的中文文件和符號。
UTF-8:對unicode進行優化,每一個字節都加入了標識頭。
編碼轉換:字符串>字節數組:編碼,通過getBytes(charset);
字節數組>字符串:解碼,通過String類的構造函數,String(byte[],charset);
如果編錯了,沒救!解錯了有可能還有救!
String s = "你好";
byte[] b = s.getBytes("GBK");//編碼
String s1 = new String(b,"iso8859-1");//解碼
System.out.println(s1);//????
//想要還原
//對s1先進行一次解碼碼錶的編碼,獲取原字節數據,
//然後在對原字節數據進行指定編碼表的解碼
byte[] b1 = s1.getBytes("iso8859-1");
String s2 = new String(b1,"gbk");
System.out.println(s2);//你好
這種情況在tomcat服務器上會出現,老版本的tomcat默認是iso8859-1,tomcat8是utf-8
對於post提交,request.setCharacterEncoding("utf-8");
代碼案例
package com.xue.javaIO;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Scanner;
/**
* Created by xue on 2016-10-11.
*/
public class TestJavaIO {
public static void main(String[] args) {
TestJavaIO testJavaIo = new TestJavaIO();
//testJavaIo.testWordByte();
//testJavaIo.testFileInputStream();
//testJavaIo.testFileReader();
//testJavaIo.testFileOutputStream();
//testJavaIo.testFileOutputStream02();
//testJavaIo.testFileWriter();
//testJavaIo.testA001();
//testJavaIo.testA002();
/* String a = readTextFile("D:\\002.txt",Charset.forName("utf-8"));
System.out.println(a);
a = readTextFile("C:\\00.txt",Charset.forName("GBK"));
System.out.println(a);*/
}
/**
* 測試一個漢字佔幾個字節
* 這個小方法別看這麼簡單,我測試遇到過很多以外的情況,導致不能得出正確結果
* 經過調試都是由於編碼轉換亂碼引起。
*/
public void testWordByte(){
/**start測試一個漢字佔幾個字節 **/
String s = "火";
String s2 = "火";
byte[] bs = null,bs2 = null;
try {
bs = s.getBytes("UTF-8");
bs2 = s2.getBytes("GBK");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("UTF-8:" + bs.length);
System.out.println("GBK:" + bs2.length);
System.out.println("******************************");
//通過讀入文件來測試,準備兩個文件,分別用GBK和UTF-8保存一箇中文漢字
File file = new File("D:\\002.txt");
System.out.println("UTF-8:" + file.length());
file = new File("GBK:" + "C:\\00.txt");
System.out.println(file.length());
/**end測試一個漢字佔幾個字節 **/
}
/**
* 字節流讀取文件數據
* 這種讀取方式無法讀取中文,會有亂碼
*/
public void testFileInputStream(){
int b;
FileInputStream in = null;
try {
in = new FileInputStream("D:\\webpro\\java001.java");
// 說明:如果文件中有漢字,漢字佔兩個字節,回車符也是佔兩個字節的.
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("找不到指定文件");
System.exit(-1);
}
try {
long num = 0;
while ((b = in.read()) != -1) {
System.out.print((char) b);
num++;
}
in.close();
System.out.println();
System.out.println("共讀取了" + num + "個字節");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件讀取錯誤");
System.exit(-1);
}
}
/**
* 字符流讀取文件
*/
public void testFileReader(){
File f = new File("D:\\webpro\\java001.java");
FileReader reader = null;
System.out.println("以字符爲單位讀取文件");
try {
reader = new FileReader(f);
int tempchar;
while((tempchar = reader.read()) != -1){
System.out.print((char)tempchar);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件沒有找到");
}catch(IOException e){
e.printStackTrace();
}finally{
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("輸出流異常");
}
}
}
}
/**
* 字節流複製文件
*/
public void testFileOutputStream(){
int b;
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("D:/webpro/java001.java");
out = new FileOutputStream("D:/webpro/java001fz.java");
while((b=in.read())!=-1){
out.write(b);
}
in.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("找不到指定文件");
System.exit(-1);
}catch(IOException e){
System.out.println("文件複製錯誤");
System.exit(-1);
}
System.out.println("文件已複製");
}
/**
* 字節流複製文件
* 這種方式的好處在於避免了對內存多次讀取
*/
public void testFileOutputStream02(){
FileInputStream fis = null;
FileOutputStream fos = null;
File srcFile = new File("D:/webpro/java001.java");
File copyFile = new File("D:/webpro/java001fz.java");
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(copyFile);
byte b[] = new byte[1024];
int count = 0;
while((count = fis.read(b))>0){
fos.write(b, 0, count);
}
fis.close();fos.close();
System.out.println("文件已複製");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件沒有找到");
}catch(IOException e){
e.printStackTrace();
System.out.println("文件讀取出現問題");
}
}
/**
* 字符流複製文件
*/
public void testFileWriter(){
File file = new File("D:/webpro/java001.java");
File copyFile = new File("D:\\webpro\\java001fz001.txt");
FileReader fr = null;
BufferedReader br = null;//構建一個字符輸入流
FileWriter fw = null;
BufferedWriter bw = null;
String message="";
try {//創建輸入流實例
fr = new FileReader(file);
br = new BufferedReader(fr);
fw = new FileWriter(copyFile);
bw = new BufferedWriter(fw);
while((message = br.readLine())!=null){
bw.write(message);
bw.newLine();//換行
System.out.println("message="+message);
}
bw.flush();
fr.close();br.close();
fw.close();bw.close();
System.out.println("文件已複製");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件沒有找到");
}catch(IOException e){
System.out.println("文件讀取出現問題");
}
}
/**
* 從控制檯輸入保存文件
* exit退出
*第一種方法
*/
public void testA001(){
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
File file = new File("D:\\webpro\\M.txt");
try {
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
int count = 0;
int result;
String message;
System.out.println("Please input message=");
do{
message = br.readLine();
message = (++count)+":"+message;
bw.write(message);
bw.newLine();
result = message.indexOf("exit");
}while(result == -1);
bw.flush();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 第二種控制檯輸出
*/
public void testA002(){
File file = new File("D:\\webpro\\M.txt");
Scanner input = new Scanner(System.in);
int count = 0;
int result;
try {
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
String message;
System.out.println("輸入字符串");
do{
message = input.next();
message = (++count)+":"+message;
bw.write(message);
bw.newLine();
result = message.indexOf("exit");
}while(result == -1);
bw.flush();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 根據指定編碼讀取純文本文件的內容
* @param fileName 文件名
* @param charset 編碼
* @return 文件內容
*/
public static String readTextFile(String fileName,Charset charset){
InputStreamReader in = null;
File file = new File(fileName);
StringBuffer sb = new StringBuffer((int) file.length());
try{
in = new InputStreamReader(new FileInputStream(file),charset);
char[] b = new char[1024];
int n = -1;
while((n=in.read(b))!=-1){
sb.append(b,0,n);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try {
if (in != null) {
in.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
return sb.toString();
}
}