最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談一下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進制模式打開上面程序使用的圖片文件:

在這里插入圖片描述

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

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

在這里插入圖片描述

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

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

所以字符編碼表就是二進制數(shù)字和字符之間的一個一一映射,例如 65 (數(shù)字)代表 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等),從字符編碼表中找出來對應的字符。 所以,當我們使用記事本打開二進制文件會看到亂碼,這就是原因。文件的復制過程也是復制的二進制數(shù)據(jù),而不是真實的文字。

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

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

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

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

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

結果:

在這里插入圖片描述

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

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

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

總結

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

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

相關文章

  • java中functional interface的分類和使用詳解

    java中functional interface的分類和使用詳解

    這篇文章主要介紹了java中functional interface的分類和使用,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • SpringBoot集成Beetl后統(tǒng)一處理頁面異常的方法

    SpringBoot集成Beetl后統(tǒng)一處理頁面異常的方法

    這篇文章主要介紹了SpringBoot集成Beetl后統(tǒng)一處理頁面異常的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • Java?Spring框架創(chuàng)建項目與Bean的存儲與讀取詳解

    Java?Spring框架創(chuàng)建項目與Bean的存儲與讀取詳解

    本篇文章將介紹Spring項目的創(chuàng)建,IDEA國內源的配置以及Bean的存儲與讀取,所謂的Bean其實就是對象的意思,更詳細地說Spring Bean是被實例的,組裝的及被Spring 容器管理的Java對象
    2022-07-07
  • SpringBoot實現(xiàn)阿里云短信發(fā)送的示例代碼

    SpringBoot實現(xiàn)阿里云短信發(fā)送的示例代碼

    這篇文章主要為大家介紹了如何利用SpringBoot實現(xiàn)阿里云短信發(fā)送,文中的示例代碼講解詳細,對我們學習或工作有一定幫助,需要的可以參考一下
    2022-04-04
  • 基于mybatis 動態(tài)SQL查詢總結

    基于mybatis 動態(tài)SQL查詢總結

    這篇文章主要介紹了mybatis 動態(tài)SQL查詢總結,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring?Cloud?Hystrix?服務降級限流策略詳解

    Spring?Cloud?Hystrix?服務降級限流策略詳解

    這篇文章主要為大家介紹了Spring?Cloud?Hystrix?服務降級限流策略詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Java程序開發(fā)環(huán)境配置圖文教程

    Java程序開發(fā)環(huán)境配置圖文教程

    這篇文章主要為大家詳細介紹了Java程序開發(fā)環(huán)境配置圖文教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • springboot3整合遠程調用的過程解析

    springboot3整合遠程調用的過程解析

    遠程過程調用主要分為:服務提供者,服務消費者,通過連接對方服務器進行請求交互,來實現(xiàn)調用效果,這篇文章主要介紹了springboot3整合遠程調用,需要的朋友可以參考下
    2023-06-06
  • java實現(xiàn)通過綁定郵箱找回密碼功能

    java實現(xiàn)通過綁定郵箱找回密碼功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)通過綁定郵箱找回密碼功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • MyBatis-Plus 與Druid 數(shù)據(jù)源操作

    MyBatis-Plus 與Druid 數(shù)據(jù)源操作

    SpringBoot框架集成MyBatis-Plus和Druid數(shù)據(jù)源,簡化了數(shù)據(jù)操作與監(jiān)控,MyBatis-Plus作為MyBatis的增強工具,自動實現(xiàn)CRUD操作,減少手寫SQL,提供分頁、邏輯刪除等功能,本文介紹MyBatis-Plus & Druid 數(shù)據(jù)源總結,感興趣的朋友一起看看吧
    2024-09-09

最新評論

新龙县| 兴安盟| 浦东新区| 黑山县| 景德镇市| 都匀市| 凤山市| 志丹县| 佛坪县| 南召县| 镇平县| 井陉县| 达孜县| 丽江市| 眉山市| 文登市| 贡嘎县| 安吉县| 靖州| 武冈市| 松原市| 乌鲁木齐县| 弥渡县| 桃江县| 文化| 广宁县| 城步| 滦平县| 建宁县| 渭源县| 宝山区| 铜鼓县| 旺苍县| 竹北市| 富锦市| 邛崃市| 西畴县| 三穗县| 开阳县| 丰都县| 武汉市|