目錄
讀取文件
為什麼會這樣呢?
首頁 Java java教程 Java不能使用字元流讀取非文字二進位檔案的原因是什麼

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

Apr 30, 2023 pm 03:34 PM
java

讀取文件

剛學Java的IO流部分時,書上說只能使用字節流去讀取圖片、視頻等非文本二進位文件,不能使用字符流,否則文件會損壞。所以我就一直記住這一點了,但是為什麼不能使用,這一直是我的一個疑惑。今天,我又想到了這個問題,所以乾脆就一鼓作氣把它解決了吧。

先來看一個關於圖片複製的程式碼範例: 注意:我的電腦是存在 D:/DB這個路徑的,如果你沒有,DB這個資料夾,必須建立一個。

package dragon;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ReadImage {
	public static void main(String[] args) throws IOException {
		String imgPath = "D:/DB/husky/kkk.jpeg";
		String byteImgCopyPath = "D:/DB/husky/byteCopykkk.jpeg";
		String charImgCopyPath = "D:/DB/husky/charCopykkk.jpeg";
		Path srcPath = Paths.get(imgPath);
		Path desPath2 = Paths.get(byteImgCopyPath);
		Path desPath3 = Paths.get(charImgCopyPath);
		
		byteRead(srcPath.toFile(), desPath2.toFile());
		System.out.println("字节复制执行成功!");
		
		characterRead(srcPath.toFile(), desPath3.toFile());
		System.out.println("字符复制执行成功!");
		
	}
	
	static void byteRead(File src, File des) throws IOException {
		try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(des))) {
			int hasRead = 0;
			byte[] b = new byte[1024];
			while ((hasRead = bis.read(b)) != -1) {
				bos.write(b, 0, hasRead);
			}
		}
	}
	
	static void characterRead(File src, File des) throws IOException {
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(src), "UTF-8"));
				BufferedWriter writer = new BufferedWriter(new FileWriter(des))) {
			int hasRead = 0;
			char[] c = new char[1024];
			while ((hasRead = reader.read(c)) != -1) {
				writer.write(c, 0, hasRead);
			}
		}
	}
}
登入後複製

運行結果: 可見,使用字元流確實無法讀取圖片這樣的二進位文件,必須使用位元組流。

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

圖片大小變化: 可見,使用字元流後圖片大小變化了,使用位元組流則不會。

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

為什麼會這樣呢?

透過上面那個例子,我們可以看到確實是無法使用字元流複製文件,並且使用字元流複製文件後,文件的大小也會變化,這就引出我們今天要討論的標題了。

我們先來想一想,為什麼文字檔打開可以顯示文字? 我們都知道電腦處理的文件無論是文字或非文字的文件,最終在電腦內部都是以二進位的形式儲存的。

使用文字編輯器的16進位模式開啟一個文字檔:

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

#使用編輯器的16進制模式開啟上面程式使用的圖片檔:

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

比較兩張圖片中的數據,應該發現不了什麼差別吧,但是為什麼文字資料就可以顯示出文字呢?這是一個非常基礎的問題,大學裡面的基礎課都是講過這方面的內容–字符編碼表。我最開始學習的是C 語言,接觸最早的編碼表是ASCII(美國資訊交換標準代碼),後來學習java接觸的是Unicode(萬國碼,這個名字和它的起源很契合。我們目前最常使用的是UTF-8,是針對Unicode的一種可變長度字元編碼。)

注意: 使用UTF-8 也是分為含有BOM(Byte Order Mark,字節順序標記) 和沒有的兩種形式,而且混用會導致錯誤,感興趣的可以去了解一下。

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

字元編碼表的作用體現在編碼上,引述百科的一段話:

在顯示器上看見的文字、圖片等資訊在電腦裡面其實不是我們看見的樣子,即使你知道所有資訊都儲存在硬碟裡,把它拆開也看不見裡面有任何東西,只有些碟片。假設,你用顯微鏡把盤片放大,會看見盤片表面凹凸不平,凸起的地方被磁化,凹的地方是沒有被磁化;凸起的地方代表數字1,凹的地方代表數字0。硬碟只能用0和1來表示所有文字、圖片等資訊。那麼字母”A”在硬碟上是如何儲存的呢?可能小張電腦儲存字母」A」是1100001,而小王儲存字母」A」是11000010,這樣雙方交換資訊時就會誤解。例如小張把1100001發送給小王,小王不認為1100001是字母”A”,可能認為這是字母”X”,於是小王在用記事本訪問存儲在硬碟上的1100001時,在屏幕上顯示的就是字母”X”。也就是說,小張和小王使用了不同的編碼表。

所以字元編碼表就是二進位數字和字元之間的一一映射,例如65 (數字)代表A,所以下面這段程式碼會在螢幕上輸出A。

char c = 65;
System.out.println(c);
登入後複製

我們使用一個循環來測試一下:

char c = 0;
for (int i  = 9999; i < 10009; i++) {
	c = (char) i;
	System.out.print(c+" ");
}
登入後複製

測試結果:(當然了,這個取決於你的當前的字元編碼表,如果使用ASCII,估計就有意思了。)

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

这样就解释了前面那个问题(为什么文本文件打开可以显示文字?),我们之所以可以看见文本文件的字符是因为计算机按照我们文件的编码(ASCII、UTF-8或者GBK等),从字符编码表中找出来对应的字符。 所以,当我们使用记事本打开二进制文件会看到乱码,这就是原因。文件的复制过程也是复制的二进制数据,而不是真实的文字。

因此可以这样理解文件复制的过程:

  • 字符流:二进制数据 --编码-> 字符编码表 --解码-> 二进制数据

  • 字节流:二进制数据 —> 二进制数据

所以问题就是出现在编码和解码的过程中,既然是字符的编码表,那它就是包含所有的字符,但是字符的数量是有限的,这就意味着它不能表示一些超过编码表的字符,因为根本不存在表中。所以,JVM 会使用一些字符进行替换,基本上都是乱码(所以大小会发生变化),而且如果有一个数据恰好是-1,那么读取就会中断,引起数据丢失。

例如如下代码使用字符流读取就会错误:

	String filename = "D:/DB/fos.txt";     //文件名
	byte[] b = new byte[] {-1, -1};      //两个字节,127的二进制就是 1111 1111
	//数据写入文件
	try (FileOutputStream fos = new FileOutputStream(filename)) {
		fos.write(b, 0, b.length);  //将两个127连续写入,就是 1111 1111 1111 1111
	}
	File file = new File(filename);
	//输出文件的大小
	System.out.println("file length: " + file.length());
	char[] c = new char[2];
	//使用字符流读取文件
	try (FileReader reader = new FileReader(filename)) {
		int count = reader.read(c);    //Java使用Unicode编码,读取的是从 0-65535 之间的数字。
		System.out.println("以文本形式输出:" + new String(c, 0, count)+"   "+count);
		for (char d : c) {  
			System.out.println("字符为:" + d);
		}
	}
	System.out.println("表示字符:" + c[0]);
	
	//再写入文件
	try (FileWriter writer = new FileWriter(filename)) {
		writer.write(c, 0, 2);
	}
	File f = new File(filename);
	System.out.println("file length: " + f.length());
登入後複製

结果:

Java不能使用字元流讀取非文字二進位檔案的原因是什麼

说明: 我将两个1字节的-1写入(字节流)了文本文件(注意是字节:-1,不是字符:-1),然后再读取(字符流),再写入(字符流)就已经出现了问题。读取出的字符显示了一个奇怪的符号,而且它的值为:65533,这个值如果用字节表示的话,一个字节是不够的,所以文件的大小就会变化。在非文本的二进制数据中,出现这种情况都是正常的,因为本来就不是按照字符编码的。

因为字符都是正数,而非字符编码的话,字节数可能是负数(很可能),但是负数在字符看来就是正数,这也是为什么-1,被读成 65533的原因。可以看出来,读取就已经错误了。

注意: 这里的重点是对于使用字符流读取非文本文件,在读取-写入的过程中的问题。

以上是Java不能使用字元流讀取非文字二進位檔案的原因是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHP的影響:網絡開發及以後 PHP的影響:網絡開發及以後 Apr 18, 2025 am 12:10 AM

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP:許多網站的基礎 PHP:許多網站的基礎 Apr 13, 2025 am 12:07 AM

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

PHP與Python:用例和應用程序 PHP與Python:用例和應用程序 Apr 17, 2025 am 12:23 AM

PHP適用於Web開發和內容管理系統,Python適合數據科學、機器學習和自動化腳本。 1.PHP在構建快速、可擴展的網站和應用程序方面表現出色,常用於WordPress等CMS。 2.Python在數據科學和機器學習領域表現卓越,擁有豐富的庫如NumPy和TensorFlow。

See all articles