IO系列(三) - 文件讀寫操作介紹

一、摘要

在之前的文章中,我們了解到在 Java I/O 體系中,File 類是唯一代表磁盤文件本身的對象

File 類定義了一些與平臺無關的方法來操作文件,包括檢查一個文件是否存在、創建、刪除文件、重命名文件、判斷文件的讀寫權限是否存在、設置和查詢文件的最近修改時間等等操作。

值得注意的地方是,Java 中通常的 File 并不代表一個真實存在的文件對象,當你通過指定一個路徑描時,它就會返回一個代表這個路徑相關聯的一個虛擬對象,這個可能是一個真實存在的文件或者是一個包含多個文件的目錄

下面我們一起來看看 File 類有哪些操作方法,以及實際使用過程中如何避坑。

二、File 類介紹

大家 JDK 中源代碼,你會發現 File 類沒有無參構造方法,最常用的是使用下面的構造方法來生成 File 對象。

以 windows 操作系統為例,操作文件的方式如下!

// 指定一個完整路徑,獲取文件對象
File file = new File("D:\\Files\\test.txt");
System.out.println(file1.getName());// 指定一個父文件路徑和子文件名稱,獲取文件對象
File file = new File("D:\\Files", "test.txt");
System.out.println(file2.getName());

File 類中定義了很多關于 File 對象的一些操作方法,我們通過一段代碼一起來看看。

public static void main(String[] args) throws Exception {// 指定一個文件完整路徑,獲取文件對象File file = new File("D:\\Files\\test.txt");// 獲取文件父節點目錄對象File parentFile = file.getParentFile();// 判斷指定路徑的文件目錄是否存在if(parentFile.exists()){System.out.println("文件目錄存在");} else {// 創建文件夾,可以自動創建多級文件夾parentFile.mkdirs();System.out.println("文件目錄不存在,創建一個文件目錄");}// 判斷指定父節點路徑的是否是一個目錄if(parentFile.isDirectory()){System.out.println("父節點路徑是一個目錄");}// 判斷指定路徑的文件是否存在if(file.exists()){System.out.println("文件存在");} else {// 創建文件file.createNewFile();System.out.println("文件不存在,創建一個文件");}// 獲取目錄下的所有文件/文件夾(僅該層路徑下)File[] files = parentFile.listFiles();System.out.print("路徑下有文件:");for (File f : files) {System.out.print(f + ";");}System.out.println();// 獲取文件名、文件夾名System.out.println("files[0]的文件名:" + files[0].getName());// 獲取文件、文件夾路徑System.out.println("files[0]的文件路徑:" + files[0].getPath());// 獲取文件、文件夾絕對路徑System.out.println("files[0]的絕對路徑:" + files[0].getAbsolutePath());// 獲取文件父目錄路徑System.out.println("files[0]的父文件夾名:" + files[0].getParent());// 判斷文件、文件夾是否存在System.out.println(files[0].exists() ? "files[0]的存在" : "files[0]的不存在");// 判斷文件是否可寫System.out.println(files[0].canWrite() ? "files[0]的可寫" : "files[0]的不可寫");// 判斷文件是否可讀System.out.println(files[0].canRead() ? "files[0]的可讀" : "files[0]的不可讀");// 判斷文件是否可執行System.out.println(files[0].canExecute() ? "file[0]可執行" : "file[0]不可執行");// 判斷文件、文件夾是不是目錄System.out.println(files[0].isDirectory() ? "files[0]的是目錄" : "files[0]的不是目錄");// 判斷拿文件、文件夾是不是標準文件System.out.println(files[0].isFile() ? "files[0]的是文件" : "files[0]的不是文件");// 判斷路徑名是不是絕對路徑System.out.println(files[0].isAbsolute() ? "files[0]的路徑名是絕對路徑" : "files[0]的路徑名不是絕對路徑");// 獲取文件、文件夾上一次修改時間System.out.println("files[0]的最后修改時間:" + files[0].lastModified());// 獲取文件的字節數,如果是一個文件夾則這個值為0System.out.println("files[0]的大小:" + files[0].length() + " Bytes");// 獲取文件路徑URI后的路徑名System.out.println("files[0]的路徑轉換為URI:" + files[0].toURI());// 下面的代碼邏輯,假設目錄下有3個以上文件// 對文件重命名File newfile = new File(file.getParentFile(), "22.txt");  //新的文件名稱files[0].renameTo(newfile);// 刪除指定的文件、文件夾files[1].delete();// 當虛擬機終止時刪除指定的文件、文件夾files[2].deleteOnExit();
}

輸出結果如下:

文件目錄存在
父節點路徑是一個目錄
文件存在
路徑下有文件:D:\Files\1.txt;D:\Files\2.txt;D:\Files\3.txt;
files[0]1.txt
files[0]的文件路徑:D:\Files\1.txt
files[0]的絕對路徑:D:\Files\1.txt
files[0]的父文件夾名:D:\Files
files[0]的存在
files[0]的可寫
files[0]的可讀
file[0]不可執行
files[0]的不是目錄
files[0]的是文件
files[0]的路徑名是絕對路徑
files[0]的最后修改時間:1686814709000
files[0]的大小:8 Bytes
files[0]的路徑轉換為URI:file:/D:/Files/1.txt

示例代碼中,基本比較全面地演示了 File 的一些基本用法,比如文件或者文件夾的新增、重命名、刪除,以及獲取文件或者文件夾相關信息等操作。

其中有兩點地方,值得注意:

  • 第一個就是分隔符的問題。不同的操作系統,路徑分隔符是不一樣的,這個可以通過File.separator解決,具體實現看下面
  • 第二個就是刪除的如果是一個文件夾的話,文件夾下還有文件/文件夾,是無法刪除成功的

關于不同操作系統下的路徑符號問題解決辦法!(windows->“\”;Linux->“/”)

在實際的編程過程中,我們不可能為了區分操作系統,然后又單獨寫一份文件路徑。

可以通過File.separator來實現跨平臺的編程邏輯,File.separator會根據不同的操作系統取不同操作系統下的分隔符。

以上面的示范代碼為例,我們可以對寫法進行如下改造!

// windows 系統下的文件絕對路徑定義方式
String path = "d:"+File.separator +"Files"+File.separator+"text.txt";
File file = new File(path);

文件的路徑結果會與預期一致!

三、文件的讀寫操作

對文件的讀寫,可以通過字節流或者字符流接口來完成,但不管哪種方式,大致分以下幾個步驟完成。

  • 第一步:獲取一個文件 file 對象
  • 第二步:通過 file 對象,獲取一個字節流或者字符流接口的對象,進行讀寫操作
  • 第三步:關閉文件流

具體的代碼實踐如下!

3.1、通過字節流接口寫入

字節流接口的文件寫入,可以通過OutputStream下的子類FileOutputStream來實現文件的數據寫入操作。

具體實例如下:

// 創建一個 readWriteDemo.txt 文件
File file = new File("readWriteDemo.txt");
if(!file.exists()){file.createNewFile();
}// 向文件中寫入數據(這種方式會覆蓋原始數據)
OutputStream outputStream = new FileOutputStream(file);
String str = "我們一起學習Java";
outputStream.write(str.getBytes(StandardCharsets.UTF_8));
outputStream.close();

上面的操作方式會覆蓋原始數據,如果想在已有的文件里面,進行追加寫入數據,可以如下方式實現。

// 追加數據寫入(這種方式不會覆蓋原始數據)
OutputStream appendOutputStream = new FileOutputStream(file, true);
String str = "-----這是追加的內容------";
appendOutputStream.write(str.getBytes(StandardCharsets.UTF_8));
appendOutputStream.close();
3.2、通過字節流接口讀取

字節流方式的文件讀取,可以通過InputStream下的子類FileInputStream來實現文件的數據讀取操作。

具體實例如下:

// 獲取 readWriteDemo.txt 文件
File file = new File("readWriteDemo.txt");
if(file.exists()){// 獲取文件流InputStream input = new FileInputStream(file);// 臨時區byte[] buffer = new byte[1024];// 分次讀取數據,每次最多讀取1024個字節,將數據讀取到臨時區之中,同時返回讀取的字節個數,如果遇到文件末尾,會返回-1int len;while ((len = input.read(buffer)) > -1) {// 字節轉為字符串String msg = new String(buffer, 0, len, StandardCharsets.UTF_8);System.out.println(msg);}// 數據讀取完畢之后,關閉輸入流input.close();
}
3.3、通過字符流接口寫入

在之前的文章中,我們了解到為了簡化字符的數據傳輸操作,JDK 提供了 Writer 與 Reader 字符流接口。

字符流方式的文件寫入,可以通過Writer下的子類FileWriter來實現文件的數據寫入操作。

具體實例如下:

// 創建一個 newReadWriteDemo.txt 文件
File file = new File("newReadWriteDemo.txt");
if(!file.exists()){file.createNewFile();
}
// 實例化Writer類對象
Writer out = new FileWriter(file) ;
// 輸出字符串
out.write("Hello");
// 輸出換行
out.write("\n");
// 追加信息,append 方法底層本質調用的是 write 方法
out.append("我們一起來學習Java");// 關閉輸出流
out.close();
3.4、通過字符流接口讀取

字符流方式的文件讀取,可以通過Reader下的子類FileReader來實現文件的數據讀取操作。

具體實例如下:

// 創建一個 newReadWriteDemo.txt 文件
File file = new File("newReadWriteDemo.txt");
if(file.exists()){// 實例化輸入流Reader reader = new FileReader(file);// 臨時區char[] buffer = new char[1024];// 分次讀取數據,每次最多讀取1024個字符,將數據讀取到臨時區之中,同時返回讀取的字節個數,如果遇到文件末尾,會返回-1int len;while ((len = reader.read(buffer)) > -1) {// 字符轉為字符串String msg = new String(buffer, 0, len);System.out.println(msg);}// 關閉輸入流reader.close();
}
3.5、文件拷貝

在實際的軟件開發過程中,避免不了文件拷貝。通過以上的接口方法,我們可以很容易的寫出一個文件復制的方法。

比如以字節流操作為例,具體實例如下:

// 1. 創建一個字節數組作為數據讀取的臨時區
byte[] buffer = new byte[1024];
// 2. 創建一個 FileInputStream 對象用于讀取文件
InputStream input = new FileInputStream(new File("input.txt"));
// 3. 創建一個 FileOutputStream 對象用于寫入文件
OutputStream output = new FileOutputStream(new File("output.txt"));
// 4. 循環讀取文件內容到臨時區,并將臨時區中的數據寫入到輸出文件中
int length;
while ((length = input.read(buffer)) != -1) {output.write(buffer, 0, length);
}
// 5. 關閉輸入流
input.close();
// 6. 關閉輸出流
output.close();

除此之外,JDK 也支持采用緩存流讀寫技術來實現數據的高效讀寫

之所為高效,是因為字節緩沖流內部維護了一個緩沖區,讀寫時先將數據存入緩沖區中,當緩沖區滿時再將數據一次性讀取出來或者寫入進去,這樣可以減少與磁盤實際的 I/O 操作次數,可以顯著提升讀寫操作的效率。

比如以字節流緩沖流為例,包裝類分別是:BufferedInputStream(字節緩存輸入流) 和 BufferedOutputStream(字符緩存輸入流)

采用緩沖流拷貝文件,具體實例如下:

// 1. 創建一個字節數組作為數據讀取的臨時區
byte[] buffer = new byte[1024];
// 2. 創建一個 BufferedInputStream 緩存輸入流對象用于讀取文件
InputStream bis = new BufferedInputStream(new FileInputStream(new File("input.txt")));
// 3. 創建一個 BufferedOutputStream 緩存輸出流對象用于寫入文件
OutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("output.txt")));// 4. 循環讀取文件內容到臨時區,并將緩沖區中的數據寫入到輸出文件中
int length;
while ((length = bis.read(buffer)) != -1) {bos.write(buffer, 0, length);
}
// 5. 關閉輸入流
bis.close();
// 6. 關閉輸出流
bos.close();

在大文件的拷貝中,使用緩存流比不使用緩存流技術至少快 10 倍,耗時是很明顯的,大家可以親自試一下。

四、字節流與字符流的互轉

在之前的文章中,我們了解到字節流與字符流,兩者其實是可以互轉的。

其中 InputStreamReader 和 OutputStreamWriter 就是轉化橋梁。

4.1、字節流轉字符流的操作

字節流轉字符流的操作,主要體現在數據的讀取階段,轉化過程如下圖所示:

以上文中的字節流接口讀取文件為例,如果我們想要轉換字符流接口來讀取數據,具體的操作方式如下:

// 獲取 readWriteDemo.txt 文件
File file = new File("readWriteDemo.txt");
if(file.exists()){// 獲取字節輸入流InputStream inputStream = new FileInputStream(file);// 轉字符流輸入流,指定 UTF_8 編碼規則,讀取數據Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);// 緩沖區char[] buffer = new char[1024];// 分次讀取數據,每次最多讀取1024個字符,將數據讀取到緩沖區之中,同時返回讀取的字節個數int len;while ((len = reader.read(buffer)) > -1) {// 字符轉為字符串String msg = new String(buffer, 0, len);System.out.println(msg);}// 關閉輸入流reader.close();inputStream.close();
}

當讀取數據的時候,先通過字節流讀取,再轉成字符流讀取。

字節流轉字符流,需要指定編碼規則,如果沒有指定,會取當系統默認的編碼規則。

4.2、字符流轉字節流的操作

字符流轉字節流的操作,主要體現在數據的寫入階段,轉化過程如下圖所示:

以上文中的字節流接口寫入文件為例,如果我們想要轉換字符流接口來寫入數據,具體的操作方式如下:

// 創建一個 newReadWriteDemo.txt 文件
File file = new File("readWriteDemo.txt");
if(!file.exists()){file.createNewFile();
}// 獲取字節輸出流
OutputStream outputStream = new FileOutputStream(file);
// 轉字符流輸出流,指定 UTF_8 編碼規則,寫入數據
Writer out = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
// 輸出字符串
out.write("Hello");
// 輸出換行
out.write("\n");
// 追加信息,append 方法底層本質調用的是 write 方法
out.append("我們一起來學習Java");// 關閉輸出流
out.close();
outputStream.close();

同樣的,當寫入數據的時候,先通過字符流寫入,再轉成字節流輸出。

字符流轉字節流,也需要指定編碼規則,如果沒有指定,會取當系統默認的編碼規則。

五、小結

本文主要圍繞 Java 對磁盤文件的讀取和寫入數據的方式做了一次簡單的總結。

內容難免有所遺漏,歡迎網友留言指出!

六、參考

1、博客園 - 五月的倉頡 - IO和File

七、寫到最后

最近無意間獲得一份阿里大佬寫的技術筆記,內容涵蓋 Spring、Spring Boot/Cloud、Dubbo、JVM、集合、多線程、JPA、MyBatis、MySQL 等技術知識。需要的小伙伴可以點擊如下鏈接獲取,資源地址:技術資料筆記。

不會有人刷到這里還想白嫖吧?點贊對我真的非常重要!在線求贊。加個關注我會非常感激!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11962.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11962.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11962.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

撳針在醫保上叫什么?

點擊文末領取撳針的視頻教程跟直播講解 創新型皮內針(撳針)——醫保甲類產品 皮內針(撳針)技術屬于重點推廣的中醫適宜技術,是將特制的小型針具固定于腧穴部位的皮內或皮下做較長時間留針的一種方法,稱“…

2024年 C++音視頻開發學習路線(ffmpeg/rtsp/srs/webrtc/hls)

在音視頻工作領域,很多人可能會陷入徘徊和迷茫的境地。音視頻的知識紛繁復雜,自己學習非常困難,既需要非常扎實的基礎知識,又需要有很多的工程經驗;不知道如何學,怎樣才能查漏補缺自己的技術短板。 對于音…

QT C++ widget layout 嵌套 例子2

在上篇文章中描述了實中套虛(用setLayout),虛中套實(用addWidget)。 本文再加1條,虛中套虛(用addLayout)。 所謂虛中套虛,是layout 套 layout 。 另外用循環代碼生成從…

記錄接口請求偶發504 Gateway Time-out問題

項目場景: 我們將服務部署到A公司服務器中,使用了共五臺服務器,分別是:1.NG服務器 2.日志服務器 3.緩存服務器 4.應用服務器1 5.應用服務器2 。而請求過來首先到達的是他們的物理代理服務器,然后再轉發請求到我們的ng…

【Neo4jJDK開箱即用的安裝全流程】

neo4j:命令行本地訪問loclhost neo4j:命令行本地訪問loclhost2 neo4j操作 Neo4j桌面版數據庫導出導入 Neo4j安裝與配置以及JDK安裝與配置教程(超詳細) Neo4j 安裝、使用教程 Neo4j安裝教程 Neo4J桌面版的配置和連接Pycharm jdk-neo對應版本 JDK ORACLE中…

數據結構(四)————二叉樹和堆(中)

制作不易,三連支持一下唄!!! 文章目錄 前言一、堆的概念及結構二、堆的實現三.堆的應用 總結 前言 CSDN 這篇博客介紹了二叉樹中的基本概念和存儲結構,接下來我們將運用這些結構來實現二叉樹 一、堆的概念及結構 1…

招聘公司要求跳槽時間間隔不能太短,我的簡歷不符合要求,怎么辦?

很多招聘公司要求就很奇葩,什么三五原則,什么二一原則,意思就是,你幾年內,不能在超過幾家公司內任職。你就說多奇葩啊,他們都不能保證自己的員工在自己公司干多久,甚至裁掉剛干了半年的員工,也是他們干出來的事,然后他們還好意思有這種奇葩要求。 目錄 1 虛假的雙向選…

OpenPCDet算法的網絡結構及工作原理

OpenPCDet是一個用于三維點云目標檢測的開源算法庫。它提供了完整的目標檢測流程,包括數據預處理、網絡模型、損失函數、后處理等。OpenPCDet基于PyTorch框架實現,并針對點云數據進行了深度優化,以實現高效的目標檢測和定位。 OpenPCDet的目…

SpringMVC 注解詳解

SpringMVC 注解詳解 1. 引言 SpringMVC 是 Spring 框架中的 Web 層解決方案,它通過注解的方式來簡化 MVC 模式的實現。本篇將詳細介紹 SpringMVC 中常用的注解及其用法。 2. SpringMVC 注解分類 SpringMVC 的注解主要分為以下幾類: 2.1 核心注解 C…

用sunoAI寫粵語歌的方法,博主已經親自實踐可行

粵語歌還是很好聽的,那么如何使用suno進行粵語歌的創作呢? 本文和大家進行分享下如何進行粵語歌曲的創作。 訪問地址如下(電腦端/手機端一個地址): ?https://suno3.cn/#/?i8NCBS8_WXTT 在微信瀏覽器中也可以直接…

css 案例 橫向滾動漸變

效果 完整代碼&#xff1a; <template><view class"content"><view class"tab"><view class"tab-item" v-for"(item,index) in tab" :key"index" click"handlerTab(index)":class"ind…

winserver系統設置圖片查看器

新建 .bat 批處理執行文件&#xff0c;內容如下&#xff1a; echo off&cd&color 0a&cls echo Set Win10 Photo Viewer reg add "HKLM\SOFTWARE\Microsoft\Windows Photo Viewer\Capabilities\FileAssociations" /v ".jpg" /t REG_SZ /d Photo…

MySQL——利用變量進行查詢操作

新建鏈接&#xff0c;自帶world數據庫&#xff0c;里面自帶city表格。 DQL # MySQL利用變量進行查詢操作 set cityNameHaarlemmermeer; select * from city where NamecityName;# 多個結果查詢 set cityName1Haarlemmermeer; set cityName2Breda; set cityName3Willemstad; s…

Mysql--期末復習

目錄 一.變量 1.系統變量 global session 2.用戶自定義變量 3.局部變量 二.觸發器 1.pysx函數(銷售)拼音縮寫觸發器 2.goods維護(存儲過程) 3.xkglxt 4.訂單延期 一.變量 1.系統變量 global session 2.用戶自定義變量 1.不用提前聲明&#xff0c;使用時直接 變量…

古月居講師/簽約作者招募計劃

機器人&#xff0c;作為一個集成了多學科技術的復雜系統&#xff0c;其開發過程充滿了挑戰。為了幫助開發者們更好地克服這些挑戰&#xff0c;提升項目的開發效率和質量&#xff0c;古月居特別招募[博客簽約作者/課程講師]。如果您平常熱愛記錄、分享開發者經驗的習慣&#xff…

Emby for Mac(輕松管理多媒體影音庫)1.9.9中文版

Emby for Mac是一款強大的多媒體影音庫管理工具&#xff0c;可以幫助用戶輕松管理和瀏覽自己的影音資源。它可以將用戶的個人視頻、音樂和照片組合在一起&#xff0c;并將其流式傳輸到用戶的設備上。 Emby for Mac 1.9.9中文版下載 Emby for Mac具有易于使用的界面&#xff0c;…

分布式與一致性協議之PBFT算法

在分布式系統中&#xff0c;數據的一致性是一個至關重要的問題。為了保證分布式系統中節點之間的數據一致性&#xff0c;人們提出了許多一致性協議和算法。 其中&#xff0c;PBFT&#xff08;Practical Byzantine Fault Tolerance&#xff0c;實用拜占庭容錯&#xff09;算法是…

content-type之multipart/form-data和application/json比較

multipart/form-data 知識點: 用途: 主要用于文件上傳以及包含非ASCII字符或二進制數據的表單數據提交。它將表單數據分割成多個部分&#xff0c;每個部分前都有一個boundary字符串作為分隔&#xff0c;可以包含文本字段和文件字段。結構: 包含了Content-Disposition頭部&…

機器學習案例:加州房產價格(四)

參考鏈接&#xff1a;https://hands1ml.apachecn.org/2/#_12 數據探索和可視化、發現規律 通過之前的工作&#xff0c;你只是快速查看了數據&#xff0c;對要處理的數據有了整體了解&#xff0c;現在的目標是更深的探索數據。 首先&#xff0c;保證你將測試集放在了一旁&…

【Docker系列】Linux部署Docker Compose

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…