特意寫一章關于IO流的知識,不為別的,是因為IO流實在太重要了
檔案
檔案物件
File 就是檔案物件,我們電腦上的檔案和檔案夾都可以用這個File實體化
public class Test{
public static void main(String[] args){
// 獲取絕對路徑D盤的hello檔案夾
File f1 = new File("D:/hello");
// 獲取相對路徑的hello.txt檔案
File f2 = new File("hello.txt");
// 獲取D盤的hello檔案夾下hello.txt檔案的實體
File f3 = new File(f1,"hello.txt");
// 輸出三個檔案或者檔案夾的絕對路徑
System.out.println(f1.getAbsolutePath());
System.out.println(f2.getAbsolutePath());
System.out.println(f3.getAbsolutePath());
}
}
上面代碼注釋講的已經非常的詳細了,用三種方式創建一個檔案物件,怎么獲取檔案的絕對路徑
檔案常用方法
假設我們已經實體化了一個File物件f
- 判斷是否存在 f.exists()
- 判斷是否是檔案夾 f.isDirectory()
- 判斷是否是檔案 f.isFile()
- 獲取檔案的長度 f.length()
- 檔案最后修改的時間 f.lastModified()
- 設定檔案最后修改時間 f.setLastModified(0) 這里的0代表的是時間戳 也就是1970.1.1 08:00:00
- 檔案重命名 f.renameTo(“newName”)
- 以字串陣列的形式,回傳當前檔案夾下面的所有檔案 f.list()
- 以檔案陣列的形式,回傳當前檔案夾下面的所有檔案 f.listFiles()
- 以字串形式回傳所在檔案夾 f.getParent()
- 以字串形式回傳獲取所在檔案夾 f.getParentFile()
- 創建一個檔案夾 f.mkdir() 如果說f所在的父檔案夾不存在就會拋出例外
- 創建一個檔案夾 f.mkdirs() 如果說f所在的父檔案夾不存在就會自動給你創建一個父檔案夾
- 洗掉檔案 f.delete()
- JVM結束的時候洗掉檔案 f.deleteOnExit()
遍歷C:\WINDOWS下面所有檔案
public class Test{
public static void main(StringP[] args){
File f = new File("C:/WINDOWS");
File[] files;
int k=0;
long max = 0;
if(f.exists){
// 把檔案夾下面的所有檔案都取出來
files = f.listFiles();
// 遍歷所有的檔案取出體積最大的檔案
for(int i=0;i<files.length();i++){
if(files[i].length()>max){
//如果比最大的檔案還大就把他的下標記下來
k = i;
max = files[i].length();
}
}
}else{
System.out.println("檔案夾不存在");
}
// 輸出找到的最大的檔案路徑和它體積
System.out.println("最大檔案路徑: "+files[k]);
System.out.println("最大檔案體積: "+files[k].length()+" 位元組");
}
}
編碼
編碼是資訊從一種形式或格式轉換為另一種形式的程序
常見的編碼
常見的編碼方式有如下幾種:
- ISO-8859-1 ASCII 數字和西歐字母
- GBK GB2312 BIG5 中文
- UNICODE (統一碼,萬國碼)
看樣子,我們的中文就是屬于這個GBK這種編碼了,也就是說中文只能用GBK編碼來存盤嘛?
這個肯定是錯的,UNICODE稱為萬國碼,也就是全世界任何國家的字符都是可以儲存的,只不過,他占用的空間是最大的,比入英文字母只需要1位元組的空間,漢字需要3位元組的空間,但是Unicode它不管你是英文字母還是漢字,都統一給你4個位元組,這樣的話是比較浪費空間的,所以就誕生了UTF-8這種基于Unicode減肥版本的編碼方式,
UTF-8它可以對英文或者數字使用一個位元組,對漢字使用三個位元組,這樣的話就避免了空間的浪費,
Java源代碼中的漢字在執行之后,都會變成JVM中的字符,而這些中文字符采用的編碼方式,都是Unicode
流
什么流?流就是一系列的資料,
當不同的介質之間有資料互動的時候,Java就是使用流來實作
比如讀取檔案的資料到程式中,站在程式的角度來看,就叫做輸入流,
檔案輸入流
如何創建檔案輸入流,看以下代碼:
public class Test{
public static void main(String[] args){
try{
File f = new File("D:/hello.txt");
// 通過這個檔案輸入流可以將硬碟上檔案中的資料讀取到java程式記憶體中來
FileInputStream fis = new FileInputStream(f);
}catch(IOException e){
e.printStackTrace();
}
}
}
位元組流
位元組輸入流
InputStream是位元組輸入流,同時也是抽象類,只提供方法宣告,不提供方法的具體實作,
FileInputStream是InputStream的子類,我們下面就用FileInputStream來實作讀取檔案的內容
public class Test{
public static void main(String[] args){
try{
// 準備一個文本檔案,里面內容是AB
File f = new File("D:/test.txt");
// 創建檔案輸入流
FileInputStream fis = new FileInputStream(f);
// 創建一個位元組陣列,它的長度就是檔案的長度
byte[] all = new byte[(int)f.length()];
// 以位元組流的形式讀取檔案中的所有內容到位元組陣列
fis.read(all);
for(byte b : all){
System.out.println(b);
}
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
結果是65和66 也就是A和B對應的ASCII碼
位元組輸出流
OutputStream 是位元組輸出流,更InputStream也是一樣的,抽象類,
所以我們用FileOutputStream為例往檔案中寫入資料
public class Test{
public static void main(String[] args){
try{
// 準備一個文本檔案,里面內容是空的
File f = new File("D:/test.txt");
// 創建檔案輸出流
FileOutputStream fis = new FileOutputStream(f);
// 創建一個位元組陣列,里面寫上A和B的ASCII碼
byte[] data = {65,66};
// 以位元組流的形式將data里的資料寫入到輸出流
fis.write(all);
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
我們打開對應的檔案,發現資料就已經寫在里面了,但是輸出流跟輸入流有一個區別,就是輸出流的時候不一定要求檔案存在,當檔案不存在的時候,會自動給我們創建一個新的檔案,而輸入流則會拋出例外
字符流
字符流:就是在位元組流的基礎上,加上編碼,形成的資料流
字符流分為字符輸入流Reader 和 字符輸出流 Writer
他們常用的子類就是FileReader 和 FileWriter
字符輸入流
在之前我們使用了位元組輸入流讀取檔案中的內容,讀取的AB列印出來是65和66
現在我們來看一下使用字符輸入流讀取檔案是什么樣的結果
public class Test {
public static void main(String[] args) {
// 準備檔案hello.txt其中的內容是AB
File f = new File("d:/hello.txt");
// 創建基于檔案的Reader
try (FileReader fr = new FileReader(f)) {
// 創建字符陣列,其長度就是檔案的長度
char[] all = new char[(int) f.length()];
// 以字符流的形式讀取檔案所有內容
fr.read(all);
for (char b : all) {
// 列印出來是A B
System.out.println(b);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
字符輸入流可以直接讀取檔案中的字符
字符輸出流
我們可以直接將一串字符存盤到檔案中,代碼如下:
public class Test{
public static void main(String[] args){
// 創建檔案物件,檔案內容是空的
File f = new File("D:/hello.txt");
// 要寫入的字串
String str = "hello";
char[] cs = str.toCharArray();
try(FileWrter fw = new FileWriter(f)){
fw.write(cs)
}catch(IOException e){
e.printStackTrace();
}
}
}
文件中的內容就會變稱hello
中文讀取
上面我們學習了位元組流和字符流,那么我們分別用兩種流來從檔案中讀取漢字內容,
用位元組流讀取中文內容
首先你得了解文本是以哪種編碼方式進行保存的
使用位元組流讀取文本后,再使用對應的編碼方式去識別這些文字
public class Test{
File f = new File("D:/hello.txt");
try(FileInputStream fis = new FileInputStream(f)){
byte[] all = new byte[(int) f.length()];
fis.read(all);
//將讀到的位元組進行編碼輸出
String str = new String(all,"GBK");
System.out.println(str);
}catch(IOException e){
e.printStackTrace();
}
}
用字符流讀取中文內容
FileReader得到的字符,肯定是已經把位元組根據某種編碼方式識別成的字符,
FIleReader的編碼方式可以用Charset.defaultCharset()獲取,如果是中文的作業系統那么就是GBK
假設現在我們的檔案是用UTF-8編碼方式保存的,那么我們該如何使用字符流讀取文字呢?
答案就是使用InputStreamReader,這個是位元組流到字符流的橋接器,它可以按照指定的字符集讀取位元組并將它們轉換為字符,
public class TestStream {
public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
File f = new File("D:/hello.txt");
System.out.println("默認編碼方式:"+Charset.defaultCharset());
//FileReader得到的是字符,所以一定是已經把位元組根據某種編碼識別成了字符了
//而FileReader使用的編碼方式是Charset.defaultCharset()的回傳值,如果是中文的作業系統,就是GBK
try (FileReader fr = new FileReader(f)) {
char[] cs = new char[(int) f.length()];
fr.read(cs);
System.out.printf("FileReader會使用默認的編碼方式%s,識別出來的字符是:%n",Charset.defaultCharset());
System.out.println(new String(cs));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//FileReader是不能手動設定編碼方式的,為了使用其他的編碼方式,只能使用InputStreamReader來代替
//并且使用new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 這樣的形式
try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) {
char[] cs = new char[(int) f.length()];
isr.read(cs);
System.out.printf("InputStreamReader 指定編碼方式UTF-8,識別出來的字符是:%n");
System.out.println(new String(cs));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

為什么中字的前面有一個?,如果你是用記事本另存為UTF-8的字符集編碼的話,那么在第一個位元組有一個標識,叫做BOM,用來標識這個檔案是使用UTF-8編碼的
快取流
以介質是硬碟為例,位元組流和字符流的弊端:
在每一次讀寫的時候,都會訪問硬碟, 如果讀寫的頻率比較高的時候,其性能表現不佳,
為了解決以上弊端,采用快取流,
快取流在讀取的時候,會一次性讀較多的資料到快取中,以后每一次的讀取,都是在快取中訪問,直到快取中的資料讀取完畢,再到硬碟中讀取,
就好比吃飯,不用快取就是每吃一口都到鍋里去鏟,用快取就是先把飯盛到碗里,碗里的吃完了,再到鍋里去鏟
快取流在寫入資料的時候,會先把資料寫入到快取區,直到快取區達到一定的量,才把這些資料,一起寫入到硬碟中去,按照這種操作模式,就不會像位元組流,字符流那樣每寫一個位元組都訪問硬碟,從而減少了IO操作
快取字符輸入流
BufferedReader可以一次性讀取一行資料
public class Test{
public static void main(String[] args){
/**
準備一個檔案,里面的內容是
hello
world
你好
*/
File f = new File("d:/hello.txt");
// 快取字符輸入流必須在一個存在的流的基礎上創建
try(
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
){
while(true){
// 一次讀取一行
String line = br.readLine();
if(line == null){
break;
}
System.out.println(line);
}catch(IOException e){
e.printStackTrace();
}
}
}
}
快取字符輸出流
PrintWrite,可以一次寫出一行資料
public class TestStream {
public static void main(String[] args) {
// 向檔案lol2.txt中寫入三行陳述句
File f = new File("d:/hello.txt");
try (
PrintWriter pw = new PrintWriter(f);
) {
pw.println("hello");
pw.println("world");
pw.println("你好");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
這里需要注意的是,快取字符輸入流的構造方法的引數是一個Reader,也就是一個字符流,而PrintWriter的構造引數可以是檔案物件也可以是位元組流或者字符流物件!
快取位元組輸入流和輸出流
分別是BufferedInputStream 和 BufferedOutputStream
下面代碼演示的是通過快取位元組輸入流和輸出流實作檔案的復制
public class Test{
public static void main(String[] args){
File file = new File("E:/我的檔案/臨時檔案/視屏檔案.ts");
File file1 = new File("E:/我的檔案/臨時檔案/視屏檔案(副本).ts");
try (
FileInputStream fis = new FileInputStream(f);
FileOutputStream fos = new FileOutputStream(f1);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
){
byte[] bytes = new byte[1024];
int len;
while(true){
len = bis.read(bytes);
if(len == -1){
break;
}
bos.write(bytes,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/11685.html
標籤:其他
上一篇:裂紋連接
