文件
文件是一種在硬盤上存儲數據的方式,操作系統幫我們把硬盤的一些細節都封裝起來了,程序員只需要了解文件相關的接口即可,相當于操作文件就是間接的操作硬盤了
硬盤用來存儲數據,和內存相比硬盤的存儲空間更大,訪問速度更慢,成本更低,持久化存儲,操作系統通過“文件系統”這樣的模塊來管理硬盤
不同的文件系統管理文件的方式都是類似的
通過目錄(directory,平常叫文件夾,專業術語叫目錄)構成了N叉樹的樹形結構
我們在文件系統中都是通過路徑來確定一個具體的文件
同樣是一個cat.jpg文件,站在不同的基準目錄上,查找的路徑是不相同的
文件系統上存儲的文件,具體來說又分成兩大類
1.文本文件–存儲的是字符
字符怎么定義呢?
有個表叫utf8,這個表上數據的組合就是字符
2.二進制文件–存儲的是二進制數據
判斷文本文件和二進制文件最簡單的方式就是
直接用記事本打開,如果打開之后能看懂,就是文本,否則就是二進制
像word文檔,ppt,excel這些,如果托到記事本上,都是二進制文件,雖然word文檔里存的東西是漢字等可以看懂的內容,但是word文檔不僅僅包含我們自己輸入的內容,還有行間距,文字格式等眾多內容
但是如果把excel的后綴改成csv格式,就是文本文件了
Java標準庫中操作文件(對文件系統操作)
操作文件可以理解成通過java代碼把你硬盤的文件刪除修改創建等,也就是通過java代碼操作文件系統
Java 中通過 java.io.File 類來對一個文件(包括目錄)進行抽象的描述。注意, File 對象可以對應到一個真實存在的文件,也可以對應到一個不存在的文件
獲取文件路徑
public static void main(String[] args) throws IOException {File file=new File("./test.txt");System.out.println(file.getParent());//上級目錄System.out.println(file.getName());//文件名System.out.println(file.getPath());//new File后面的是什么路徑,就輸出什么System.out.println(file.getAbsolutePath());//工作目錄拼接上當前目錄 在idea中運行一個程序,工作目錄就是項目所在的目錄System.out.println(file.getCanonicalPath());//對getAbsolutePath的這個路徑進行了修飾,讓這個路徑沒有多余的東西,比如那個.}
創建文件
public static void main(String[] args) {File file=new File("./test.txt");System.out.println(file.exists());//在當前項目所在的路徑下(D:\code\Java\java-related-code\File)沒有test.txt這個文件,應該是falseSystem.out.println(file.isFile());//都沒有這個文件了,這兩行肯定都是false了System.out.println(file.isDirectory());}
但是我們可以先創建這個文件
文件跟目錄的區別可以認為是文件是這個路徑的重點,而目錄下面還有別的路徑
刪除文件
public static void main(String[] args) {File file=new File("./test.txt");file.delete();}
public static void main(String[] args) throws InterruptedException {File file=new File("./test.txt");//不是立刻刪除,等到程序運行結束再刪除file.deleteOnExit();Thread.sleep(2000);//程序運行兩秒會結束,所以這個文件在兩秒之后刪除}
創建目錄
單層目錄
public static void main(String[] args) {File file = new File("./testDir");// mk => make dir => directory// mkdir 一次只能創建一層目錄. mkdirs 可以一次創建多級目錄file.mkdir();//file.mkdirs();}
多層目錄
public static void main(String[] args) {File file = new File("./testDir/111/222");// mk => make dir => directory// mkdir 一次只能創建一層目錄. mkdirs 可以一次創建多級目錄//file.mkdir();file.mkdirs();}
文件重命名
public static void main(String[] args) {File file = new File("./test.txt");File file2 = new File("./src/test.txt");//本來test.txt跟src是同級別的,現在把test.txt移動到src目錄下面了
//因此文件重命名也可以做到移動文件的效果file.renameTo(file2);}
針對文件內容操作
Reader
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;// Reader 使用.
public class Demo6
{public static void main(String[] args) throws IOException{// FileReader 構造方法, 可以填寫一個文件路徑(絕對路徑/相對路徑都行), 也可以填寫一個構造好的 File 對象
// Reader reader = new FileReader("d:/test.txt");
// try {
// // 中間的代碼無論出現啥情況, close 都能保證執行到.
// } finally {
// // 拋出異常, 或者 return, close 就都執行不到了~~
// reader.close();
// }// 上述使用 finally 的方式能解決問題, 但是不優雅.// 使用 try with resources 是更好的解決方案.try (Reader reader = new FileReader("d:/test.txt")){while (true){char buf[] = new char[1024];int n = reader.read(buf);//讀到的有效字符的個數if (n == -1){// 讀到文件末尾了.break;}//實際只讀了n個字符,小于n就行了for (int i = 0; i < n; i++){System.out.print(buf[i] + ",");}}}}
}
文件泄露相當于打開很多文件,使用完之后都不關閉,文件描述符表就滿了,再打開新的文件,文件描述符就裝不下了,這些文件就不知道上哪里去了,就造成了文件泄露
InputStream
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;public class Demo7 {public static void main(String[] args) throws IOException {try (InputStream inputStream = new FileInputStream("d:/test.txt")) {while (true) {byte[] buf = new byte[1024];int n = inputStream.read(buf);if (n == -1) {break;}for (int i = 0; i < n; i++) {System.out.printf("%x ", buf[i]);}String s = new String(buf, 0, n, "utf8");//把buf數組的從0到n下標在string的構造方法中,通過utf8的編碼轉換,轉換成人能看懂的字符串System.out.println(s);}}}
}
于是我們可以借助Scanner來完成上述操作
平時我們輸入是
Scanner scanner=new Scanner(System.in);
那么system.in的類型也是一個InputStream
于是我們就可以
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;public class Demo8 {public static void main(String[] args) throws IOException {try (InputStream inputStream = new FileInputStream("d:/test.txt")) {Scanner scanner = new Scanner(inputStream);// 此時就是從 test.txt 這個文件中讀取數據了!!String s = scanner.next();System.out.println(s);}}
}
write
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;public class Demo9
{public static void main(String[] args) throws IOException {try (Writer writer = new FileWriter("d:/test.txt", true)) {// write 是可以一次直接寫一個字符串. 這個是非常方便的.writer.write("hello java");}}
}
經典面試題
1.掃描指定目錄,并找到名稱中包含指定字符的所有普通文件(不包含目錄),并且后續詢問用戶是否要刪除該文件
import java.io.File;
import java.util.Scanner;public class Demo10 {private static Scanner scanner = new Scanner(System.in);public static void main(String[] args) {// 1. 讓用戶輸入一個目錄. 后續的查找都是針對這個目錄來進行的.System.out.println("請輸入要搜索的根目錄: ");File rootPath = new File(scanner.next());// 2. 再讓用戶輸入要搜索/要刪除的關鍵詞.System.out.println("請輸入要刪除的關鍵詞: ");String word = scanner.next();// 3. 判定一下當前輸入的目錄是否有效.if (!rootPath.isDirectory()) {System.out.println("您此時輸入的路徑不是合法目錄!");return;}// 4. 遍歷目錄. 從根目錄出發, 按照 深度優先(遞歸) 的方式, 進行遍歷scanDir(rootPath, word);}public static void scanDir(File currentDir, String word) {// 1. 先列出當前目錄中都包含哪些內容.File[] files = currentDir.listFiles();if (files == null || files.length == 0) {// 空的目錄或者非法的目錄return;}// 2. 遍歷列出的文件, 分兩個情況分別討論.for (File f : files) {// 加個日志, 方便看程序執行的過程.System.out.println(f.getAbsolutePath());if (f.isFile()) {// 3. 如果當前文件是普通文件, 看看文件名是否包含了 word, 來決定是否要刪除.dealFile(f, word);} else {// 4. 如果當前文件是目錄文件, 就遞歸執行 scanDirscanDir(f, word);}}}private static void dealFile(File f, String word) {// 1. 先判定當前文件名是否包含 wordif (!f.getName().contains(word)) {// 此時這個文件不包含 word 關鍵詞. 直接跳過.return;}// 2. 包含 word 就需要詢問用戶是否要刪除該文件?System.out.println("該文件是: " + f.getAbsolutePath() + ", 是否要確認刪除? (Y/N)");String choice = scanner.next();if (choice.equals("Y") || choice.equals("y")) {f.delete();}// 如果是其他值, 都忽略.}
}
2.進行普通文件的復制
import java.io.*;
import java.util.Scanner;// 完成文件復制.
public class Demo11 {public static void main(String[] args) throws IOException {Scanner scanner = new Scanner(System.in);// 1. 輸入路徑并且合法性判定System.out.println("請輸入要復制的源文件路徑: ");String src = scanner.next();File srcFile = new File(src);if (!srcFile.isFile()) {System.out.println("您輸入的源文件路徑非法!");return;}System.out.println("請輸入要復制到的目標路徑: ");String dest = scanner.next();File destFile = new File(dest);// 不要求目標文件本身存在. 但是得保證目標文件所在的目錄, 得是存在的.// 假設目標文件寫作 d:/tmp/cat2.jpg, 就需要保證 d:/tmp 目錄是存在的.if (!destFile.getParentFile().isDirectory()) {System.out.println("您輸入的目標文件路徑非法!");return;}// 2. 進行復制操作的過程. 按照字節流打開.try (InputStream inputStream = new FileInputStream(srcFile);OutputStream outputStream = new FileOutputStream(destFile)) {while (true) {byte[] buffer = new byte[20480];int n = inputStream.read(buffer);System.out.println("n = " + n);if (n == -1) {System.out.println("讀取到 eof, 循環結束. ");break;}outputStream.write(buffer, 0, n);}}}
}