欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

讀取文件

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

先來(lái)看一個(gè)關(guān)于圖片復(fù)制的代碼示例: 注意:我的電腦是存在 D:/DB這個(gè)路徑的,如果你沒(méi)有,DB這個(gè)文件夾,必須建立一個(gè)。

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é)復(fù)制執(zhí)行成功!");
		
		characterRead(srcPath.toFile(), desPath2.toFile());
		System.out.println("字符復(fù)制執(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);
			}
		}
	}
}


運(yùn)行結(jié)果: 可見(jiàn),使用字符流確實(shí)無(wú)法讀取圖片這樣的二進(jìn)制文件,必須使用字節(jié)流。

在這里插入圖片描述

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

在這里插入圖片描述

為什么會(huì)這樣呢?

通過(guò)上面那個(gè)例子,我們可以看到確實(shí)是無(wú)法使用字符流復(fù)制文件,并且使用字符流復(fù)制文件后,文件的大小也會(huì)變化,這就引出我們今天要討論的標(biāo)題了。

我們先來(lái)想一想,為什么文本文件打開(kāi)可以顯示文字? 我們都知道計(jì)算機(jī)處理的文件無(wú)論是文本還是非文本的文件,最終在計(jì)算機(jī)內(nèi)部都是以二進(jìn)制的形式存儲(chǔ)的。

使用文本編輯器的16進(jìn)制模式打開(kāi)一個(gè)文本文件:

在這里插入圖片描述

使用編輯器的16進(jìn)制模式打開(kāi)上面程序使用的圖片文件:

在這里插入圖片描述

對(duì)比兩張圖片中的數(shù)據(jù),應(yīng)該發(fā)現(xiàn)不了什么區(qū)別吧,但是為什么文本數(shù)據(jù)就可以顯示出文字呢?這是一個(gè)非?;A(chǔ)的問(wèn)題,大學(xué)里面的基礎(chǔ)課都是講過(guò)這方面的內(nèi)容–字符編碼表。 我最開(kāi)始學(xué)習(xí)的是 C 語(yǔ)言,接觸最早的編碼表是 ASCII(美國(guó)信息交換標(biāo)準(zhǔn)代碼),后來(lái)學(xué)習(xí)java接觸的是 Unicode(萬(wàn)國(guó)碼,這個(gè)名字和它的起源很契合。我們目前最常使用的是UTF-8,是針對(duì)Unicode的一種可變長(zhǎng)度字符編碼。)

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

在這里插入圖片描述

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

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

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

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

我們使用一個(gè)循環(huán)來(lái)測(cè)試一下:

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

測(cè)試結(jié)果:(當(dāng)然了,這個(gè)取決于你的當(dāng)前的字符編碼表,如果使用 ASCII,估計(jì)就有意思了。)

在這里插入圖片描述

這樣就解釋了前面那個(gè)問(wèn)題(為什么文本文件打開(kāi)可以顯示文字?),我們之所以可以看見(jiàn)文本文件的字符是因?yàn)橛?jì)算機(jī)按照我們文件的編碼(ASCII、UTF-8或者GBK等),從字符編碼表中找出來(lái)對(duì)應(yīng)的字符。 所以,當(dāng)我們使用記事本打開(kāi)二進(jìn)制文件會(huì)看到亂碼,這就是原因。文件的復(fù)制過(guò)程也是復(fù)制的二進(jìn)制數(shù)據(jù),而不是真實(shí)的文字。

因此可以這樣理解文件復(fù)制的過(guò)程:

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

所以問(wèn)題就是出現(xiàn)在編碼和解碼的過(guò)程中,既然是字符的編碼表,那它就是包含所有的字符,但是字符的數(shù)量是有限的,這就意味著它不能表示一些超過(guò)編碼表的字符,因?yàn)楦静淮嬖诒碇?。所以,JVM 會(huì)使用一些字符進(jìn)行替換,基本上都是亂碼(所以大小會(huì)發(fā)生變化),而且如果有一個(gè)數(shù)據(jù)恰好是-1,那么讀取就會(huì)中斷,引起數(shù)據(jù)丟失。

例如如下代碼使用字符流讀取就會(huì)錯(cuò)誤:

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

結(jié)果:

在這里插入圖片描述

說(shuō)明: 我將兩個(gè)1字節(jié)的-1寫入(字節(jié)流)了文本文件(注意是字節(jié):-1,不是字符:-1),然后再讀?。?strong>字符流),再寫入(字符流)就已經(jīng)出現(xiàn)了問(wèn)題。讀取出的字符顯示了一個(gè)奇怪的符號(hào),而且它的值為:65533,這個(gè)值如果用字節(jié)表示的話,一個(gè)字節(jié)是不夠的,所以文件的大小就會(huì)變化。在非文本的二進(jìn)制數(shù)據(jù)中,出現(xiàn)這種情況都是正常的,因?yàn)楸緛?lái)就不是按照字符編碼的。

因?yàn)樽址际钦龜?shù),而非字符編碼的話,字節(jié)數(shù)可能是負(fù)數(shù)(很可能),但是負(fù)數(shù)在字符看來(lái)就是正數(shù),這也是為什么-1,被讀成 65533的原因??梢钥闯鰜?lái),讀取就已經(jīng)錯(cuò)誤了。

注意: 這里的重點(diǎn)是對(duì)于使用字符流讀取非文本文件,在讀取-寫入的過(guò)程中的問(wèn)題。

總結(jié)

這個(gè)問(wèn)題算是基本解決了,如果想要了解更多,估計(jì)需要閱讀一些專業(yè)的書籍才行了,不過(guò)到了這一步,我覺(jué)得已經(jīng)可以了。它也要求我們掌握關(guān)于計(jì)算機(jī)的一些基本的入門知識(shí)了。雖然這個(gè)問(wèn)題拖了很久才解決,但是也是因?yàn)槲易罱_(kāi)始使用Java的IO流進(jìn)行編程,以前的話,只是記住了那句話,但是動(dòng)手實(shí)踐卻沒(méi)有去做,這也是應(yīng)該多動(dòng)手編程、多積累才能解決問(wèn)題。

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

相關(guān)文章

  • Nacos配置文件使用經(jīng)驗(yàn)及CAP原則詳解

    Nacos配置文件使用經(jīng)驗(yàn)及CAP原則詳解

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

    java 實(shí)現(xiàn)音樂(lè)播放器的簡(jiǎn)單實(shí)例

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

    java實(shí)現(xiàn)pgsql自動(dòng)更新創(chuàng)建時(shí)間與更新時(shí)間的兩種方式小結(jié)

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

    Java8新特性之lambda(動(dòng)力節(jié)點(diǎn)Java學(xué)院整理)

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

    java結(jié)束當(dāng)前循環(huán)常用代碼

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

    java 中 阻塞隊(duì)列BlockingQueue詳解及實(shí)例

    這篇文章主要介紹了java 中 阻塞隊(duì)列BlockingQueue詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • springboot2.0.0配置多數(shù)據(jù)源出現(xiàn)jdbcUrl is required with driverClassName的錯(cuò)誤

    springboot2.0.0配置多數(shù)據(jù)源出現(xiàn)jdbcUrl is required with driverClassN

    這篇文章主要介紹了springboot2.0.0配置多數(shù)據(jù)源出現(xiàn)jdbcUrl is required with driverClassName的錯(cuò)誤,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • springdata jpa單表操作crud的實(shí)例代碼詳解

    springdata jpa單表操作crud的實(shí)例代碼詳解

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

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

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

    java實(shí)現(xiàn)桌面右下角彈窗效果

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

最新評(píng)論