不知道說啥了,看看吧
JavaEE & 文件操作和IO
在之前的學(xué)習(xí)中,基本上都是圍繞內(nèi)存展開的~
-
MySQL 主要是操作硬盤的
-
文件IO也是是操作硬盤的~
IO : input output
1. 文件系統(tǒng)操作
- 創(chuàng)造文件,刪除文件,重命名文件,創(chuàng)建目錄······
- 一些操作沒有權(quán)限也做不了~
1.1 路徑
- 就是我們的文件系統(tǒng)上的一個(gè)文件/目錄的具體位置
- 目錄:文件夾
- 計(jì)算機(jī)的目錄是有層級(jí)結(jié)果的,即N叉樹
- 我的代碼庫目錄:
- 那么這篇文章的源碼所在的目錄具體位置是什么呢?
- 所以,路徑就是:D:/馬庫/marathon-april-2023/文件IO
- 這是個(gè)絕對(duì)路徑
-
- 絕對(duì)路徑,即從盤符開始到具體文件/目錄
- 相對(duì)路徑,從指定目錄開始到具體文件/目錄
- 要確認(rèn)**(基準(zhǔn))工作目錄**是什么~
而里面的src目錄下有java文件,out目錄里面就有class文件,同樣有對(duì)應(yīng)的路徑
- / 分割,推薦!
- \ 分割的話要加轉(zhuǎn)義字符\ , 即 \\
- 一般只能適用于Windows
- …/ 代表這一級(jí)的上一個(gè)目錄
- . 代表這當(dāng)前目錄(與后續(xù)目錄要以 / 分割,即 . / )通??梢允÷?/li>
-
. . 代表當(dāng)前目錄的上一級(jí)目錄(與后續(xù)目錄要以 / 分割,即 . . / )
- . . / . . / 代表上一級(jí)目錄的上一級(jí)目錄~
- 默認(rèn)源頭的源頭是“此電腦”目錄,可以不寫
絕對(duì)路徑可以認(rèn)為是以“此電腦”為工作目錄的相對(duì)路徑
-
并且任何一個(gè)文件/目錄,對(duì)應(yīng)的路徑,肯定是唯一的
- 在LInux可能出現(xiàn)兩個(gè)不同路徑找到同一文件的情況~
- 但是在Windows上不存在~
-
路徑與文件一一對(duì)應(yīng)~
-
路徑為文件的身份
1.2 文本文件 與 二進(jìn)制文件
- 這個(gè)就是字面意思了
文本文件:
- 存儲(chǔ)字符(不僅僅是char類型)
二進(jìn)制文件:
- 存儲(chǔ)什么都OK,因?yàn)槿魏螖?shù)據(jù)都是以二進(jìn)制為根本的
判斷:
- 記事本打開,是文本就是文本,是二進(jìn)制就是二進(jìn)制~
-
.txt文件:文本 / 二進(jìn)制,看你怎么創(chuàng)造的~
-
.java / .c 文件 : 文本文件
- 拖動(dòng)到記事本里
- 拖動(dòng)到記事本里
-
.class / .exe 文件: 二進(jìn)制文件~
- .jpg / mp3 二進(jìn)制文件
- 亂碼,即把這一個(gè)個(gè)字節(jié)轉(zhuǎn)化為字符型,而原本不是字符型的~
- pdf xlsx doc … : 二進(jìn)制文件
- csv excel的文本格式:
1.3 文件系統(tǒng)操作
- Java標(biāo)準(zhǔn)庫提供了一個(gè)類:File
- File對(duì)象代表著一個(gè)文件,是那個(gè)文件的抽象表示~
- 硬盤上的文件 ==> 內(nèi)存中的File對(duì)象 ==> 在內(nèi)存上改變硬盤上的一些東西
1.3.1 構(gòu)造File對(duì)象
- 需要傳一個(gè)文件路徑為參數(shù)~
- 這個(gè)文件可以存在也可以不存在
例如這張圖片~
1.3.2 使用File對(duì)象
- 不手動(dòng)常見文件是不會(huì)自動(dòng)創(chuàng)建的
- 不會(huì)再new的時(shí)候創(chuàng)建
序號(hào) | 方法名 | 方法說明 |
---|---|---|
1 | String getParent() | 返回 File 對(duì)象的父目錄文件路徑 |
2 | String getName() | 返回 FIle 對(duì)象的純文件名稱 |
3 | String getPath() | 返回 File 對(duì)象的文件路徑 |
4 | String getAbsolutePath() | 返回 File 對(duì)象的絕對(duì)路徑 |
5 | String getCanonicalPath() | 返回 File 對(duì)象的修飾過的絕對(duì)路徑 |
6 | boolean exists() | 判斷 File 對(duì)象描述的文件是否真實(shí)存在 |
7 | boolean isDirectory() | 判斷 File 對(duì)象代表的文件是否是一個(gè)目錄 |
8 | boolean isFile() | 判斷 File 對(duì)象代表的文件是否是一個(gè)普通文件 |
9 | boolean createNewFile() | 根據(jù) File 對(duì)象,自動(dòng)創(chuàng)建一個(gè)空文件。成功創(chuàng)建后返 回 true |
10 | boolean delete() | 根據(jù) File 對(duì)象,刪除該文件。成功刪除后返回 true |
11 | void deleteOnExit() | 根據(jù) File 對(duì)象,標(biāo)注文件將被刪除,刪除動(dòng)作會(huì)到 JVM 運(yùn)行結(jié)束時(shí)才會(huì)進(jìn)行 |
12 | String[] list() | 返回 File 對(duì)象代表的目錄下的所有文件名 |
13 | File[] listFiles() | 返回 File 對(duì)象代表的目錄下的所有文件,以 File 對(duì)象表示 |
14 | boolean mkdir() | 創(chuàng)建 File 對(duì)象代表的目錄 |
15 | boolean mkdirs() | 創(chuàng)建 File 對(duì)象代表的目錄,如果必要,會(huì)創(chuàng)建中間目錄 |
16 | boolean renameTo(File dest) | 進(jìn)行文件改名,也可以視為我們平時(shí)的剪切、粘貼操作 |
17 | boolean canRead() | 判斷用戶是否對(duì)文件有可讀權(quán)限 |
18 | boolean canWrite() | 判斷用戶是否對(duì)文件有可寫權(quán)限 |
小小演示:
- 絕對(duì)路徑
public static void main(String[] args) throws IOException {
File file = new File("d:/馬圖/瞪眼.jpg");
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsoluteFile());
System.out.println(file.getCanonicalFile());
}
-
IOException是IO操作的會(huì)拋出的常見異常
- 是首查異常,也叫編譯時(shí)異常
-
相對(duì)路徑
- 默認(rèn)是與src目錄的上一級(jí)的為工作目錄~
- 就是項(xiàng)目所在目錄
- 而不是src這一級(jí)
- 打印得出來這個(gè)文件不代表就存在這個(gè)文件~
- 是否存在?
-
性質(zhì),創(chuàng)造文件,刪除文件
- 是存在既不是目錄又不是普通文件的文件的
- 例如socket文件等等~
public static void main(String[] args) {
File file = new File("./helloWorld.txt");
System.out.println(file.exists());
System.out.println(file.isDirectory());//是目錄嗎?(文件夾)
System.out.println(file.isFile());//是文件嗎?(普通文件)
}
- 創(chuàng)建與刪除目錄
public static void main(String[] args) {
File file = new File("./helloWorld");
if(!file.exists()) {
file.mkdir();
}
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
}
make directory
- 路徑轉(zhuǎn)化為數(shù)組
獲取目錄里的所有文件/目錄
- list ==> 文件 /目錄名數(shù)組
- listFile ==> File對(duì)象 數(shù)組
public static void main(String[] args) {
File file = new File("helloWorld");
String[] results1 = file.list();
File[] results2 = file.listFiles();
System.out.println(Arrays.toString(results1));
System.out.println(Arrays.toString(results2));
}
- 重命名
public static void main(String[] args) {
File file = new File("helloWorld");
file.renameTo(new File("HELLO_WORLD"));
}
2. 文件內(nèi)容操作
-
針對(duì)文件內(nèi)容進(jìn)行 讀 與 寫
-
文件操作依賴于一些類,或者說是多組類
- 文本 ==> ”字符流“
- 二進(jìn)制 ==> “字節(jié)流”
“流”:
- 數(shù)據(jù)的運(yùn)輸像河流一樣,流向哪,從哪流來~
2.1 獲取文件輸入流InputStream(字節(jié)流)
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("HELLO_WORLD");
//coding
inputStream.close();
}
有了輸入流,就相當(dāng)于你有了“介質(zhì)”
- 相當(dāng)于打開文件,文件的信息可以出來
關(guān)閉輸入流
- 相當(dāng)于關(guān)閉文件
- 如果不關(guān)閉,可能會(huì)導(dǎo)致,文件資源泄露 ===>
- 進(jìn)程里有個(gè)文件描述符表,一旦打開文件多了,這個(gè)表可能會(huì)爆了,導(dǎo)致機(jī)器出問題!
- Java的對(duì)象,沒用了會(huì)自動(dòng)釋放,但是這里的流對(duì)象并不會(huì)!??!
正確的寫法:(利用finally保證關(guān)閉能夠進(jìn)行)
- try括號(hào)內(nèi)為打開文件操作,默認(rèn)finally關(guān)閉文件~
- try with resources操作
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("HELLO_WORLD/123.txt")) {
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
- InputStream實(shí)現(xiàn)Closeable接口,那么就可以這樣操作~
- 這就是try with resource操作的要求
-
注意:后續(xù)內(nèi)容都是以這種方式打開與隱式關(guān)閉文件的
2.1.1 read方法
- 只能說文件里有個(gè)指針指著讀在哪了,并不是讀了后原文件就刪了~
方法名 | 方法說明 |
---|---|
int read() | 一次讀一個(gè)字節(jié)并返回,返回-1代表讀完了 |
int read(byte[] b) | 填滿此數(shù)組為止,返回-1表示讀完(可能填不滿) |
int read(byte[] b, int off, int len) | 填滿此數(shù)組的[off, off + len)為止,返回-1表示讀完(可能填不滿) |
- 在java對(duì)此方法的描述中提到:返回的字節(jié)轉(zhuǎn)化為int類型,范圍是0 - 255
手寫一些數(shù)據(jù):
2.1.2 不帶參數(shù)的read方法
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("HELLO_WORLD/123.txt")) {
int b = 0;
do {
b = inputStream.read();
System.out.println(b);
}while(b != -1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
- 測速結(jié)果:
- 對(duì)于中文:
- 測試結(jié)果:
- 不是說一個(gè)中文 ==> 一個(gè)char字符 兩個(gè)字節(jié)嗎,為什么這里是三個(gè)字節(jié)一個(gè)漢字
- 那是因?yàn)閁nicode每個(gè)字符是兩個(gè)字節(jié)
- UTF-8漢字是三個(gè)字節(jié),其他字符一個(gè)字節(jié)~
- 我編譯器無腦全設(shè)置UTF-8了
- 而讀取的內(nèi)容可沒有規(guī)定就是Java的char類型呀~
- 但是我們可以通過一些手段翻譯這個(gè)東西,后面講~
- 字符對(duì)應(yīng)表 - 查詢網(wǎng)站:查看字符編碼(UTF-8) (mytju.com)
E9 : 233 A9 : 169 AC : 172 ················
完美對(duì)應(yīng)~
2.1.3 給定數(shù)組的read方法
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("HELLO_WORLD/123.txt")) {
byte[] bytes = new byte[9];
System.out.println(inputStream.read(bytes));
System.out.println(Arrays.toString(bytes));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
- 測試結(jié)果:
-
咋變負(fù)數(shù)了?
- 因?yàn)樽x取到的字節(jié)仍然是 -128 - 127 的
- 只不過剛才返回int類型的是無符號(hào)的~
-
如何翻譯呢?
- 用String的構(gòu)造方法~
2.2 獲取文件輸出流OutputStream(字節(jié)流)
public static void main(String[] args) {
//每次打開輸出流,都會(huì)清空文件內(nèi)容~
try(OutputStream outputStream = new FileOutputStream("HELLO_WORLD/123.txt")) {
} catch (IOException e) {
e.printStackTrace();
}
}
- 每次打開文件,會(huì)清空原內(nèi)容!
2.2.1 write方法
方法名 | 方法說明 |
---|---|
void write(int b) | 傳入一個(gè)int型,內(nèi)部強(qiáng)行轉(zhuǎn)化為byte型 |
void write(byte[] b) | 將整個(gè)字節(jié)數(shù)組寫入文件中 |
int write(byte[] b, int off, int len) | 將字節(jié)數(shù)組的[off, off + len)部分寫入文件中 |
void flush() | 重要:我們知道 I/O 的速度是很慢的,所以,大多的 OutputStream 為 了減少設(shè)備操作的次數(shù),在寫數(shù)據(jù)的時(shí)候都會(huì)將數(shù)據(jù)先暫時(shí)寫入內(nèi)存的 一個(gè)指定區(qū)域里,直到該區(qū)域滿了或者其他指定條件時(shí)才真正將數(shù)據(jù)寫 入設(shè)備中,這個(gè)區(qū)域一般稱為緩沖區(qū)。但造成一個(gè)結(jié)果,就是我們寫的 數(shù)據(jù),很可能會(huì)遺留一部分在緩沖區(qū)中。需要在最后或者合適的位置, 調(diào)用 flush(刷新)操作,將數(shù)據(jù)刷到設(shè)備中。 |
- flush很重要,在關(guān)閉之前沒有flush,文件內(nèi)容就無法得以更新
2.2.2 write 傳入單個(gè)字節(jié)的構(gòu)造方法
public static void main(String[] args) {
//每次打開輸出流,都會(huì)清空文件內(nèi)容~
try(OutputStream outputStream = new FileOutputStream("HELLO_WORLD/123.txt")) {
outputStream.write(1);
outputStream.write(2);
outputStream.write(3);
outputStream.write(4);
outputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
2.2.3 write 傳入字節(jié)數(shù)組的構(gòu)造方法
public static void main(String[] args) {
//每次打開輸出流,都會(huì)清空文件內(nèi)容~
try(OutputStream outputStream = new FileOutputStream("HELLO_WORLD/123.txt")) {
outputStream.write(new byte[]{1, 2, 3, 4, 5, 6, 7});
outputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
2.3 字符流 Reader 與 Writer
2.3.1 Reader的讀方法
- 對(duì)比于字節(jié)流,這里讀的是字符,讀進(jìn)字符數(shù)組~
public static void main(String[] args) throws FileNotFoundException {
try(Reader reader = new FileReader("HELLO_WORLD/123.txt")) {
char ch = (char)reader.read();
char[] chars = new char[7];
reader.read(chars);
System.out.println(ch);
System.out.println(chars);
int c = 0;
do {
c = reader.read();
System.out.println((char)c);
}while (c != -1);
} catch (IOException e) {
e.printStackTrace();
}
- 測試結(jié)果:
- 同樣read返回-1,代表讀完了~
2.3.2 Writer的寫操作
- 對(duì)比于字節(jié)流,這里寫入的是字符,字符數(shù)組,或者字符串~
public static void main(String[] args) {
try(Writer writer = new FileWriter("HELLO_WORLD/123.txt")) {
writer.write('0');
writer.write(new char[]{'1', '2', '3', '4', '5', '6'});
writer.write("789");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
-
寫操作跟字節(jié)流一樣,無此文件,自動(dòng)創(chuàng)建~
-
并且還會(huì)清空原內(nèi)容
-
測試結(jié)果:
3. 小程序練習(xí):全文檢索
- 就是遍歷目錄,并在文件內(nèi)容中查找信息
接下來以簡單粗暴的方式去實(shí)現(xiàn)~
3.1 控制臺(tái)輸入根目錄與關(guān)鍵字
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.print("請(qǐng)輸入要掃描的根目錄:");
String root = scanner.next();
File file = new File(root);
if(!file.isDirectory()) { // 1. 目錄不存在 2. 不是目錄
System.out.println("輸入錯(cuò)誤");
return;
}
System.out.print("請(qǐng)輸入要查詢的詞:>");
String words = scanner.next();
scan(file, words);//掃描
}
- 根據(jù)根目錄構(gòu)造File對(duì)象
- 如果這個(gè)file對(duì)象不是目錄或者不存在的話,則說明輸入錯(cuò)誤,直接返回退出程序
- 如果是目錄,輸入要關(guān)鍵字
- 調(diào)用scan方法對(duì)目錄進(jìn)行掃描(自己實(shí)現(xiàn))
3.2 scan遞歸方法
- n叉樹就得寫循環(huán)來遞歸了
- 如果是掃描到二進(jìn)制文件,我們也不指望里面有我們要的文本,因?yàn)槎M(jìn)制一般存放一些后端數(shù)據(jù)信息,并不是給人看的,不是觀賞性的,但是二進(jìn)制文件還是可能會(huì)讀到的~
- 記得設(shè)立遞歸出口,死遞歸會(huì)導(dǎo)致棧溢出
public static void scan(File file, String words) throws IOException {
File[] files = file.listFiles();
if(files == null) {
// 這里空目錄對(duì)應(yīng)的并不是空數(shù)組!是null~
return;
}else {
for (int i = 0; i < files.length; i++) {
File f = files[i];
if(f.isFile()) {
String content = readAll(f);
if(content.contains(words)) {
System.out.println(f.getCanonicalFile());
}
}
if(f.isDirectory()) {
scan(f, words);
}
//兩種都不是的其他文件,就不能讀~
}
}
}
3.3 readAll讀取文件方法
- 利用StringBuilder拼接字符串~
- 用Reader字符流讀取數(shù)據(jù)~
- 對(duì)于Java,這些流對(duì)象只是讀取方式,對(duì)文件是二進(jìn)制還是文本沒有要求
- 最終返回
public static String readAll(File f) {
StringBuilder stringBuilder = new StringBuilder();
try (Reader reader = new FileReader(f)){
while(true) {
int c = reader.read();
if(c == -1) {
break;
}
stringBuilder.append((char)c);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
- 堆溢出~
3.4 測試
- 測試用例:
- 如果文件數(shù)量多,內(nèi)容多,以此法會(huì)卡的半死
- 到時(shí)候我們學(xué)習(xí)一下“倒排索引”這種數(shù)據(jù)結(jié)構(gòu),可能能夠很好地優(yōu)化!
- 根目錄是:d:/馬庫/marathon-april-2023
- 關(guān)鍵字是:馬大帥
- 測試結(jié)果:
-
測試結(jié)果正常!
- 另外兩個(gè)可能是其他項(xiàng)目里提到了這個(gè)關(guān)鍵字 ^ V ^
文章到此結(jié)束!謝謝觀看
可以叫我 小馬,我可能寫的不好或者有錯(cuò)誤,但是一起加油鴨??!文件操作的講解告一段落,后面也會(huì)涉及到哦!文章來源:http://www.zghlxwxcb.cn/news/detail-489851.html
實(shí)踐才是最好的學(xué)習(xí)!文章來源地址http://www.zghlxwxcb.cn/news/detail-489851.html
到了這里,關(guān)于【JavaEE】文件操作和IO-目錄掃描全文檢索小程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!