亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談一下Java為什么不能使用字符流讀取非文本的二進制文件

 更新時間:2023年04月14日 09:06:09   作者:CrazyDragon_King  
這篇文章主要介紹了淺談一下為什么不能使用字符流讀取非文本的二進制文件,剛學Java的IO流部分時,書上說只能使用字節(jié)流去讀取圖片、視頻等非文本二進制文件,不能使用字符流,否則文件會損壞,需要的朋友可以參考下

讀取文件

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

先來看一個關于圖片復制的代碼示例: 注意:我的電腦是存在 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 desPath1 = Paths.get(byteImgCopyPath);
		Path desPath2 = Paths.get(charImgCopyPath);
		
		byteRead(srcPath.toFile(), desPath1.toFile());
		System.out.println("字節(jié)復制執(zhí)行成功!");
		
		characterRead(srcPath.toFile(), desPath2.toFile());
		System.out.println("字符復制執(zhí)行成功!");
		
	}
	
	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);
			}
		}
	}
}


運行結果: 可見,使用字符流確實無法讀取圖片這樣的二進制文件,必須使用字節(jié)流。

在這里插入圖片描述

圖片大小變化: 可見,使用字符流后圖片大小變化了,使用字節(jié)流則不會。

在這里插入圖片描述

為什么會這樣呢?

通過上面那個例子,我們可以看到確實是無法使用字符流復制文件,并且使用字符流復制文件后,文件的大小也會變化,這就引出我們今天要討論的標題了。

我們先來想一想,為什么文本文件打開可以顯示文字? 我們都知道計算機處理的文件無論是文本還是非文本的文件,最終在計算機內部都是以二進制的形式存儲的。

使用文本編輯器的16進制模式打開一個文本文件:

在這里插入圖片描述

使用編輯器的16進制模式打開上面程序使用的圖片文件:

在這里插入圖片描述

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

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

在這里插入圖片描述

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

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

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

char c = 65;
System.out.println(c);

我們使用一個循環(huán)來測試一下:

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

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

在這里插入圖片描述

這樣就解釋了前面那個問題(為什么文本文件打開可以顯示文字?),我們之所以可以看見文本文件的字符是因為計算機按照我們文件的編碼(ASCII、UTF-8或者GBK等),從字符編碼表中找出來對應的字符。 所以,當我們使用記事本打開二進制文件會看到亂碼,這就是原因。文件的復制過程也是復制的二進制數據,而不是真實的文字。

因此可以這樣理解文件復制的過程:

  • 字符流:二進制數據 --編碼-> 字符編碼表 --解碼-> 二進制數據
  • 字節(jié)流:二進制數據 —> 二進制數據

所以問題就是出現在編碼和解碼的過程中,既然是字符的編碼表,那它就是包含所有的字符,但是字符的數量是有限的,這就意味著它不能表示一些超過編碼表的字符,因為根本不存在表中。所以,JVM 會使用一些字符進行替換,基本上都是亂碼(所以大小會發(fā)生變化),而且如果有一個數據恰好是-1,那么讀取就會中斷,引起數據丟失。

例如如下代碼使用字符流讀取就會錯誤:

	String filename = "D:/DB/fos.txt";     //文件名
	byte[] b = new byte[] {-1, -1};      //兩個字節(jié),127的二進制就是 1111 1111
	//數據寫入文件
	try (FileOutputStream fos = new FileOutputStream(filename)) {
		fos.write(b, 0, b.length);  //將兩個127連續(xù)寫入,就是 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());

結果:

在這里插入圖片描述

說明: 我將兩個1字節(jié)的-1寫入(字節(jié)流)了文本文件(注意是字節(jié):-1,不是字符:-1),然后再讀?。?strong>字符流),再寫入(字符流)就已經出現了問題。讀取出的字符顯示了一個奇怪的符號,而且它的值為:65533,這個值如果用字節(jié)表示的話,一個字節(jié)是不夠的,所以文件的大小就會變化。在非文本的二進制數據中,出現這種情況都是正常的,因為本來就不是按照字符編碼的。

因為字符都是正數,而非字符編碼的話,字節(jié)數可能是負數(很可能),但是負數在字符看來就是正數,這也是為什么-1,被讀成 65533的原因??梢钥闯鰜?,讀取就已經錯誤了。

注意: 這里的重點是對于使用字符流讀取非文本文件,在讀取-寫入的過程中的問題。

總結

這個問題算是基本解決了,如果想要了解更多,估計需要閱讀一些專業(yè)的書籍才行了,不過到了這一步,我覺得已經可以了。它也要求我們掌握關于計算機的一些基本的入門知識了。雖然這個問題拖了很久才解決,但是也是因為我最近開始使用Java的IO流進行編程,以前的話,只是記住了那句話,但是動手實踐卻沒有去做,這也是應該多動手編程、多積累才能解決問題。

到此這篇關于淺談一下為什么不能使用字符流讀取非文本的二進制文件的文章就介紹到這了,更多相關不能使用字符流讀取非文本二進制文件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Nacos配置文件使用經驗及CAP原則詳解

    Nacos配置文件使用經驗及CAP原則詳解

    這篇文章主要為大家介紹了Nacos配置文件使用經驗及CAP規(guī)則詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-02-02
  • java 實現音樂播放器的簡單實例

    java 實現音樂播放器的簡單實例

    這篇文章主要介紹了java 實現音樂播放器的簡單實例的相關資料,希望通過本文能幫助到大家,實現這樣的功能,需要的朋友可以參考下
    2017-09-09
  • java實現pgsql自動更新創(chuàng)建時間與更新時間的兩種方式小結

    java實現pgsql自動更新創(chuàng)建時間與更新時間的兩種方式小結

    本文主要介紹了java實現pgsql自動更新創(chuàng)建時間與更新時間的兩種方式小結,主要包括通過數據庫自身實現以及通過mybatisplus的TableField注解添加,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Java8新特性之lambda(動力節(jié)點Java學院整理)

    Java8新特性之lambda(動力節(jié)點Java學院整理)

    這篇文章主要介紹了Java8新特性之lambda(動力節(jié)點Java學院整理)表達式的相關知識,包括lambda語法方面的知識,非常不錯,具有參考借鑒價值,需要的朋友參考下吧
    2017-06-06
  • java結束當前循環(huán)常用代碼

    java結束當前循環(huán)常用代碼

    在?Java中,當我們要結束一個循環(huán)時,通常會使用循環(huán)變量的實現類來結束,但在實際開發(fā)中,我們經常會遇到某個循環(huán)結束后需要進行其他的操作的情況,在本文中給大家分享java結束當前循環(huán)常用代碼,感興趣的朋友跟隨小編一起看看吧
    2023-06-06
  • java 中 阻塞隊列BlockingQueue詳解及實例

    java 中 阻塞隊列BlockingQueue詳解及實例

    這篇文章主要介紹了java 中 阻塞隊列BlockingQueue詳解及實例的相關資料,需要的朋友可以參考下
    2017-03-03
  • springboot2.0.0配置多數據源出現jdbcUrl is required with driverClassName的錯誤

    springboot2.0.0配置多數據源出現jdbcUrl is required with driverClassN

    這篇文章主要介紹了springboot2.0.0配置多數據源出現jdbcUrl is required with driverClassName的錯誤,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • springdata jpa單表操作crud的實例代碼詳解

    springdata jpa單表操作crud的實例代碼詳解

    這篇文章主要介紹了springdata jpa單表操作crud的實例代碼詳解,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • Springboot如何加載靜態(tài)圖片

    Springboot如何加載靜態(tài)圖片

    這篇文章主要介紹了Springboot如何加載靜態(tài)圖片,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java實現桌面右下角彈窗效果

    java實現桌面右下角彈窗效果

    這篇文章主要為大家詳細介紹了java實現桌面右下角彈窗效果,模仿類似于qq消息彈窗,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07

最新評論