Java程序設計第章文件和流解讀.ppt_第1頁
Java程序設計第章文件和流解讀.ppt_第2頁
Java程序設計第章文件和流解讀.ppt_第3頁
Java程序設計第章文件和流解讀.ppt_第4頁
Java程序設計第章文件和流解讀.ppt_第5頁
已閱讀5頁,還剩68頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、第八章 文件和流,圖形圖像研究所 計算機科學與技術、軟件學院浙江工業(yè)大學,高飛,陸佳煒等。Java程序設計實用教程。北京:清華大學出版社,2013(ISBN:978-7-302-31695-4) 高飛,趙小敏等。Java程序設計實用教程習題集。北京:清華大學出版社,2013(ISBN:978-7-302-32051-7,高飛 教授,博士生導師 Tel.URL: http:/,前言,本章的目的:什么是流?什么是字節(jié)流和字符流?如何訪問磁盤上的文件屬性?如何將對象保存到磁盤文件中(對象序列化)?Java中亂碼問題是如何形成的,回顧關鍵詞:用戶的輸入是邪惡的!Try-c

2、atch-finally、throws、throw,小節(jié)安排,文件和流,8.2.1 輸入字節(jié)流:InputStream,8.3、字符流,8.4、文件,8.2.2 輸出字節(jié)流:OutputStream,8.3.1 輸入字符流:Reader,8.3.2 輸出字符流:Writer,8.3.3 BufferedReader和BufferedWriter,8.1、流的基本概念,8.2、字節(jié)流,8.5、對象序列化,8.6、Java中的亂碼問題,8.3.4 字節(jié)流和字符流的異同,8.1、流的基本概念,8.2、字節(jié)流,8.2、字節(jié)流,輸入字節(jié)流:InputStream,8.2、字節(jié)流,*從mytext.txt

3、文件讀出并顯示在屏幕上*/ import java.io.*; public class FileIn public static void main(String args) try FileInputStream rf=new FileInputStream(H:/java/temp/mytext.txt);/打開文件 int b; while(b=rf.read()!=-1)/用read()方法逐個字節(jié)讀取 System.out.print(char)b); /轉換成char并顯示 rf.close(); catch(IOException ie) System.out.println(

4、ie); catch(Exception e) System.out.println(e);,8.2、字節(jié)流,輸出字節(jié)流:OutputStream,8.2、字節(jié)流,*以下示例用于說明如何利用FileOutputStream進行文件復制*/ import java.io.*; public class TestFileCopy public static void main(String args) try /復制的源文件TestVector.java FileInputStream rf=new FileInputStream(G:/java/TestVector.java); /復制的目的文

5、件TV2.txt,若不存在,則會自動創(chuàng)建 FileOutputStream wf=new FileOutputStream(G:/java/TV2.txt); byte b=new byte512; int count=-1; /每次讀取512個字節(jié),count用于記錄實際讀取的字節(jié)數(shù) while(count=rf.read(b, 0, 512)!=-1) wf.write(b,0,count); rf.close(); wf.close(); catch(IOException ie) System.out.println(ie.toString(); catch(Exception e)

6、System.out.println(e.toString();,小節(jié)安排,文件和流,8.2.1 輸入字節(jié)流:InputStream,8.3、字符流,8.4、文件,8.2.2 輸出字節(jié)流:OutputStream,8.3.1 輸入字符流:Reader,8.3.2 輸出字符流:Writer,8.3.3 BufferedReader和BufferedWriter,8.1、流的基本概念,8.2、字節(jié)流,8.5、對象序列化,8.6、Java中的亂碼問題,8.3.4 字節(jié)流和字符流的異同,8.3、字符流,8.3、字符流,輸入字符流:Reader,8.3、字符流,* 讀取G:/aillo.txt文件的內(nèi)容

7、(一行一行讀),并將其內(nèi)容寫入G:/jacky.txt中 知識點: java讀文件、寫文件- */ import java.io.*; public class TestFileWR public static void main(String args) try /創(chuàng)建FileReader對象,用來讀取字符流 FileReader fr = new FileReader(G:/aillo.txt); /緩沖指定文件的輸入 BufferedReader br = new BufferedReader(fr); /創(chuàng)建FileWriter對象,用來寫入字符流 FileWriter fw = ne

8、w FileWriter(G:/jacky.txt); /將緩沖對文件的輸出 BufferedWriter bw = new BufferedWriter(fw); String strLine;/用來保存每次讀取的一行,8.3、字符流,while (br.ready() strLine = br.readLine();/讀取一行 bw.write(strLine); /寫入文件 bw.newLine(); System.out.println(strLine);/在屏幕上輸出 bw.flush(); /刷新該流的緩沖,即將該流輸出到目的 bw.close(); br.close(); fw.

9、close(); fr.close(); catch (IOException e) e.printStackTrace();,8.3、字符流,輸出字符流:Writer,8.3、字符流,*從鍵盤輸入一行文字,寫入文件TestFileOut.txt中*/ import java.io.*; public class TestFileOut public static void main(String args) char c=new char512; byte b=new byte512; int n,i; try FileWriter wf=new FileWriter(TestFileOut

10、.txt); /從鍵盤讀入文字并存入字節(jié)數(shù)組b中 n=System.in.read(b); for(i=0;in;i+) ci=(char)bi; wf.write(c); wf.close(); catch(IOException e) System.out.println(e);,輸入123456ABCDEF并回車,輸入Java是一門優(yōu)秀的語言!并回車,8.3、字符流,*從鍵盤輸入一行文字(可輸入中文字符),寫入文件TestFileOut.txt中*/ import java.io.*; public class TestFileOutCH public static void main(

11、String args) char c=new char512; int n,i; try FileWriter wf=new FileWriter(TestFileOutCH.txt); /利用InputStreamReader正確讀取中文 System.out.print(請輸入中文:); InputStreamReader isr=new InputStreamReader(System.in); n=isr.read(c,0,512);/一次性讀取512個字符,n表示實際讀取的字符數(shù) wf.write(c); wf.close(); System.out.println(剛輸入的數(shù)據(jù)為

12、:+String.valueOf(c,0,n); catch(IOException e) System.out.println(e);,8.3、字符流,字符緩沖流:BufferedReader和BufferedWriter,以緩沖區(qū)方式對數(shù)據(jù)進行輸入輸出。 所謂緩沖區(qū),是指一片臨時的內(nèi)存區(qū)域。BufferedReader和BufferedWriter分別擁有8192個字符(16384個字節(jié))的緩沖區(qū)。 當BufferedReader從源(文件、網(wǎng)絡、鍵盤或其他進程)讀取字符數(shù)據(jù)時,會先盡量從源中讀入字符數(shù)據(jù)并置入緩沖區(qū),而之后若使用read()方法,會先從緩沖區(qū)中進行讀取。如果緩沖區(qū)數(shù)據(jù)不足

13、,才會再從源中讀取。 使用BufferedWriter時,寫入的數(shù)據(jù)并不會先輸出到目的地,而是先存儲至緩沖區(qū)中。如果緩沖區(qū)中的數(shù)據(jù)滿了,才會一次對目的地進行寫入。例如一個文件,通過緩沖區(qū)可減少對硬盤的輸入/輸出動作,以提高文件存取的效率,8.3、字符流,*本程序首先在控制臺輸入字符(逐行輸入),程序將輸入的文字存儲至指定的文件中,如果要結束程序,輸入quit字符串即可。*/ import java.util.*; import java.io.*; public class TestFileBRW public static void main(String args) try /緩沖Syst

14、em.in輸入流 /System.in是字節(jié)流,通過InputStreamReader將其轉換為字符流 BufferedReader bufReader = new BufferedReader(new InputStreamReader(System.in); /緩沖FileWriter BufferedWriter bufWriter = new BufferedWriter(new FileWriter(args0); String input = null; /每讀一行進行一次寫入動作 while(!(input = bufReader.readLine().equals(quit)

15、bufWriter.write(input,8.3、字符流,*newLine()方法寫入與操作系統(tǒng)相依的換行字符,依執(zhí)行環(huán)境當時的OS來決定該輸出那種換行字符*/ bufWriter.newLine(); bufReader.close(); bufWriter.close(); catch(ArrayIndexOutOfBoundsException e) System.out.println(沒有指定文件); catch(IOException e) e.printStackTrace();,8.3、字符流,字節(jié)流和字符流的異同,本質(zhì)區(qū)別在于byte和char。字節(jié)流采用二進制直接傳輸,用

16、字符流則牽涉到本地系統(tǒng)的編碼問題,在網(wǎng)絡通訊中,強烈建議使用byte字節(jié)流方式。 字節(jié)流與字符之間的轉化通過InputStreamReader和OutputStreamWriter來關聯(lián),實際上是通過byte和String來關聯(lián)。在實際開發(fā)中出現(xiàn)的漢字問題實際上都是在字符流和字節(jié)流之間轉化不統(tǒng)一而造成的。在從字節(jié)流轉化為字符流時,也就是從byte轉化為String時,使用如下構造方法: publicString(bytebytes,StringcharsetName) 這個方法中有一個關鍵的字符集編碼參數(shù)charsetName,通常我們都省略了,那系統(tǒng)就用操作系統(tǒng)的默認的language。而在

17、字符流轉化為字節(jié)流時,實際上是String轉化為byte時,是使用如下方法進行轉化: byteString.getBytes(StringcharsetName) 字符流和字節(jié)流是根據(jù)處理數(shù)據(jù)的不同來區(qū)分的。字節(jié)流按照8位傳輸,字符流按照16位傳輸由于字符流使用Unicode字符集,支持多國文字,因此若流要跨越多種平臺傳輸,應使用字符流。字符流的傳輸效率比字節(jié)流的高,小節(jié)安排,文件和流,8.2.1 輸入字節(jié)流:InputStream,8.3、字符流,8.4、文件,8.2.2 輸出字節(jié)流:OutputStream,8.3.1 輸入字符流:Reader,8.3.2 輸出字符流:Writer,8.3

18、.3 BufferedReader和BufferedWriter,8.1、流的基本概念,8.2、字節(jié)流,8.5、對象序列化,8.6、Java中的亂碼問題,8.3.4 字節(jié)流和字符流的異同,8.4.1、文件,File文件相關屬性,例如: /假設當前目錄為G:/Java1,有文件 G:/Java1/TestFilePRO.java和目錄/G:/Java1/gfei File f=new File(TestFilePRO.java); File d=new File(Gfei); System.out.println(f.getName=+f.getName();/輸出TestFilePRO.jav

19、a System.out.println(f.getPath=+f.getPath();/輸出TestFilePRO.java /以下語句輸出G:Java1TestFilePRO.java System.out.println(f.getAbsolutePath=+f.getAbsolutePath(); System.out.println(f.getParent=+f.getParent();/輸出null System.out.println(f.length=+f.length();/筆者示例中輸出668 System.out.println(d.getName=+d.getName(

20、);/輸出Gfei System.out.println(d.getPath=+d.getPath();/輸出Gfei System.out.println(d.getAbsolutePath=+d.getAbsolutePath();/輸出G:java1Gfei System.out.println(d.getParent=+d.getParent();/輸出null System.out.println(d.length=+d.length();/輸出0,8.4.1、文件,File文件操作,例如: File ds=new File(subdir); File dd=new File(gfe

21、i1); File fs=new File(TestFileSRC.java); File fd=new File(TestFileDEST.txt); ds.renameTo(dd);/改為gfei1 fs.renameTo(fd);/改為TestFileDEST.txt,public boolean renameTo(File dest) /重新命名文件或目錄 /刪除文件或目錄。如果是目錄,則該目錄必須為空才能刪除。 public boolean delete(,8.4.1、文件,目錄操作,public boolean mkdir() /創(chuàng)建目錄 public String list()/

22、返回一個字符串數(shù)組,是給定目錄下的文件與子目錄 public File listFiles()/返回File對象數(shù)組,是給定目錄下的文件,8.4.1、文件,*打印某目錄下(包含子目錄)所有文件和文件大小*/ import java.io.*; public class TestFileLIST public static void main(String args)throws IOException File files=new File(.);/.表示當前目錄(與TestFileLIST.java所在的同一個目錄) listPath(files); public static void l

23、istPath(File f) throws IOException String file_list=f.list(); for(int i=0;ifile_list.length;i+) File cf=new File(f.getPath(),file_listi); if(cf.isDirectory() /判斷是否為子目錄 listPath(cf);/列舉該子目錄下的文件 if(cf.isFile()/判斷是否為文件 try /輸出文件大小 System.out.println(cf.getCanonicalPath()+:+cf.length(); catch(IOExceptio

24、n e) e.printStackTrace();,8.4.2、隨機訪問文件類:RandomAccessFile,RandomAccessFile類提供了對文件的隨機訪問方式,即可在文件的任意位置讀或寫數(shù)據(jù)而且可以同時進行讀和寫的操作,8.4.2、隨機訪問文件類:RandomAccessFile,提供了類似于readInt()的其他方法用于讀取byte、boolean、char、double、float、long、short、unsignedbyte、unsignedshort等數(shù)據(jù)類型的方法,即readByte()、readBoolean()、readChar()、readDouble()、

25、readFloat()、readLong()、readShort()、readUnsignedByte()、readUnsignedShort()等 提供了writeByte()、writeBoolean()、writeChar()、writeDouble()、writeFloat()、writeLong()、writeShort()等方法用于寫入數(shù)據(jù),8.4.2、隨機訪問文件類:RandomAccessFile,*向文件中寫入10個數(shù)據(jù),第i個數(shù)據(jù)圓周率*i(i=0, 1, 2, , 9),然后將第2個(i=2)改為0,最后將10個數(shù)據(jù)全部輸出*/ import java.io.IOExce

26、ption; import java.io.RandomAccessFile; public class TestFileRAF public static void main(String args ) try RandomAccessFile f=new RandomAccessFile(TestFileRAF.txt, rw);/可讀寫 int i; double d; /寫:向文件寫入10個數(shù)據(jù) for (i=0; i10; i+) f.writeDouble(Math.PI*i,8.4.2、隨機訪問文件類:RandomAccessFile,修改:對文件中第2個double數(shù)據(jù)改為0

27、f.seek(16);/文件指針往前走16個字節(jié)(2個double數(shù)據(jù)) f.writeDouble(0); f.seek(0);/文件指針回到文件首部 /讀?。簩⑷繑?shù)據(jù)讀出并打印到屏幕中 for (i=0; i 10; i+) d=f.readDouble( ); System.out.println( + i + : + d); f.close( ); catch (IOException e) System.err.println(發(fā)生異常: + e); e.printStackTrace( );,8.4.2、隨機訪問文件類:RandomAccessFile,8.4.3、文件過濾接口:

28、FileFilter和FilenameFilter,FileFilter和FilenameFilter是為開發(fā)者提供在文件系統(tǒng)中進行過濾或者說是搜索所需要文件的功能,均有accept()方法: /FileFilter: file表示要過濾目錄中的文件對象 public boolean accept(File file); /*FilenameFilter: 參數(shù)dir是要過濾的目錄, name是目錄中的文件名*/ public boolean accept(File dir, String name); 區(qū)別:FileFilter提供文件對象的訪問方法,而FilenameFilter是按照目錄

29、和文件的名稱的方式來工作,8.4.3、文件過濾接口:FileFilter和FilenameFilter,實現(xiàn)文件或目錄過濾所需步驟,聲明一個過濾器類并實現(xiàn)FileFilter或FilenameFilter接口中的accept方法。 使用File類的list()和listFiles()進行過濾,其參數(shù)為第步中的過濾器類的對象作為參數(shù),就可實現(xiàn)對文件名的過濾。File類的list()和listFiles()方法聲明如下: public String list(FilenameFilter filter) public File listFiles(FilenameFilter filter) pu

30、blic File listFlies(FileFilter filter) 其中,filter須是第步中實現(xiàn)了FileFilter或FilenameFilter接口中的accept方法的過濾器類的對象,8.4.3、文件過濾接口:FileFilter和FilenameFilter,import java.io.*; /第步:聲明過濾類ListFilter并實現(xiàn)FilenameFilter接口中的accept方法 class ListFilter implements FilenameFilter private String pre=“”, ext=“”;/pre表示文件前綴,ext表示文件后

31、綴 public ListFilter(String filterstr) int i, j; filterstr=filterstr.toLowerCase(); i=filterstr.indexOf(*); j=filterstr.indexOf(.); if(i0) pre=filterstr.substring(0,i); if(i=-1,8.4.3、文件過濾接口:FileFilter和FilenameFilter,實現(xiàn)accept方法 public boolean accept(File dir,String filename) boolean y=true; try filena

32、me=filename.toLowerCase(); y=filename.startsWith(pre),8.4.3、文件過濾接口:FileFilter和FilenameFilter,第步:使用File類的list()和listFiles()進行過濾 public class TestFileSearch public static void main(String args) /要求兩個參數(shù):第一個參數(shù)表示目錄,第二參數(shù)表示要過濾的文件 String strDir, strExtension; switch(args.length) case 1: strDir=.; strExtensi

33、on=args0; break; case 2: strDir=args0; strExtension=args1; break; default: System.out.println(需兩個參數(shù)!); return; File f=new File(strDir); ListFilter ls=new ListFilter(strExtension); String str=f.list(ls); for(int i=0; istr.length; i+) System.out.println(stri);,小節(jié)安排,文件和流,8.2.1 輸入字節(jié)流:InputStream,8.3、字符流

34、,8.4、文件,8.2.2 輸出字節(jié)流:OutputStream,8.3.1 輸入字符流:Reader,8.3.2 輸出字符流:Writer,8.3.3 BufferedReader和BufferedWriter,8.1、流的基本概念,8.2、字節(jié)流,8.5、對象序列化,8.6、Java中的亂碼問題,8.3.4 字節(jié)流和字符流的異同,8.5、對象序列化,什么是對象序列化,保存內(nèi)存中某個對象的方法很多,但是Java給你提供一種更好的保存對象狀態(tài)的機制,那就是對象序列化。 java中對象序列化的過程就是將對象寫入字節(jié)流和從字節(jié)流中讀取對象。將對象狀態(tài)轉換成字節(jié)流之后,可以保存到文件、通過管道輸出到

35、另一線程或通過網(wǎng)絡連接將對象數(shù)據(jù)發(fā)送到另一主機,8.5、對象序列化,什么情況下需要序列化,把內(nèi)存中的對象保存到一個文件中或者數(shù)據(jù)庫中 用套接字(socket)在網(wǎng)絡上傳送對象 通過RMI傳輸對象,8.5、對象序列化,實現(xiàn)序列化的步驟,第一步:創(chuàng)建一個FileOutputStream對象,如下: FileOutputStream fs = new FileOutputStream(foo.ser); 第二步:java.io.ObjectOutputStream負責將對象寫入字節(jié)流。因此第二步是利用第一步創(chuàng)建的FileOutputStream對象創(chuàng)新一個ObjectOutputStream對象,如

36、下: ObjectOutputStream os = new ObjectOutputStream(fs); 第三步:將對象寫入 Foo myFoo1=new Foo(); Foo myFoo2=new Foo(); Foo myFoo3=new Foo(); os.writeObject(myFoo1); os.writeObject(myFoo2); os.writeObject(myFoo3); 第四步:關閉ObjectOutputStream os.close(,8.5、對象序列化,序列化對象的條件,被序列化的對象必須是實現(xiàn)java.io.Serializable接口的類對象。java

37、.io.Serializable接口中沒有方法需要實現(xiàn),之所以要implements該接口,只是告訴JVM,該類對象是可被序列化而已,例如: imports java.io.*; class Foo implements Serializable int width; int height; Date todayDate;,8.5、對象序列化,反序列化,反序列化是打開字節(jié)流并重構對象。對象序列化不僅要將基本數(shù)據(jù)類型轉換成字節(jié)表示,有時還要恢復數(shù)據(jù)。反序列化通常使用java.io.ObjectInputStream從字節(jié)流重構對象,例如: FileInputStream in = new Fil

38、eInputStream(foo.ser); ObjectInputStream ois = new ObjectInputStream(in); Foo myFoo1=(Foo)ois.readObject(); Foo myFoo2=(Foo)ois.readObject(); Foo myFoo3=(Foo)ois.readObject(,8.5、對象序列化,import java.io.Serializable; /*Student定義為序列化類*/ public class Student implements Serializable static final long seria

39、lVersionUID = 123456L; String m_name; int m_id; int m_height; public Student( String name, int id, int h ) m_name = name; m_id = id; m_height = h; public void output( ) System.out.println(姓名: + m_name); System.out.println(學號: + m_id); System.out.println(身高: + m_height);,8.5、對象序列化,*將Student對象數(shù)據(jù)寫入obje

40、ct.dat*/ import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class TestWriteObject public static void main(String args ) try ObjectOutputStream f = new ObjectOutputStream( new FileOutputStream(object.dat); Student s = new Student( 張三, 2003001, 172); f.writeObject(s); s.output(

41、 ); f.close( ); catch (Exception e) System.err.println(發(fā)生異常: + e); e.printStackTrace( );,8.5、對象序列化,*從object.dat讀出Student對象數(shù)據(jù)*/ import java.io.FileInputStream; import java.io.ObjectInputStream; public class TestReadObject public static void main(String args ) try ObjectInputStream f = new ObjectInput

42、Stream( new FileInputStream(object.dat); Student s = (Student)(f.readObject( ); s.output( ); f.close( ); catch (Exception e) System.err.println(發(fā)生異常: + e); e.printStackTrace( );,8.5、對象序列化,序列化注意事項,只有對象的數(shù)據(jù)被保存,方法與構造函數(shù)不被序列化。 聲明為transient或static的變量不能被序列化,小節(jié)安排,文件和流,8.2.1 輸入字節(jié)流:InputStream,8.3、字符流,8.4、文件,8

43、.2.2 輸出字節(jié)流:OutputStream,8.3.1 輸入字符流:Reader,8.3.2 輸出字符流:Writer,8.3.3 BufferedReader和BufferedWriter,8.1、流的基本概念,8.2、字節(jié)流,8.5、對象序列化,8.6、Java中的亂碼問題,8.3.4 字節(jié)流和字符流的異同,8.6.1、Java中字符的表達,Java中字 符表達,char,byte,網(wǎng)絡傳輸或存儲 的序列化形式,內(nèi)存形式,一個字節(jié) 8bits,一個Unicode字符 16bits,String,一組char,8.6.1、Java中字符的表達,例如: String ying = 英; c

44、har cy= ying.charAt(0); /取得首字符 /將英轉換成java默認編碼字符(UTF-16) String yingHex = Integer.toHexString(cy); System.out.println(yingHex.toUpperCase();/輸出默認編碼:82 F1 /將英轉換成GBK編碼 byte yingGBBytes = ying.getBytes(GBK); String hex; for(int i=0; iyingGBBytes.length; i+) hex=Integer.toHexString(yingGBBytesi /英的GBK編碼:

45、D3 A2,8.6.2、Unicode簡介,什么是Unicode呢?Unicode,又稱為統(tǒng)一碼、萬國碼或單一碼,它為每種語言中的每個字符設定了統(tǒng)一并且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉換、處理的要求,即為每種語言的每個字符設定一個統(tǒng)一的序數(shù)。 ISO與Unicode獨立發(fā)展,至1992年合并協(xié)同。從Unicode 2.0開始,Unicode采用了與ISO 10646-1相同的字庫和字碼;ISO也承諾,ISO 10646將不會替超出0 x10FFFF的UCS-4編碼賦值,以使二者保持一致,8.6.2、Unicode編碼方式,Unicode編碼基于ISO 10646定義的通用字符集

46、(Universal Character Set,UCS) ,有UCS-2和UCS-4,分別用2個字節(jié)和4個字節(jié)編碼。 UCS-4定義如下,而UCS-4的高位的兩個字節(jié)為0即為UCS-2 Unicode是UCS-4的子集,計劃使用第0分組的17個平面,因此其編碼數(shù)為:17(平面)*256(行)*256(碼位)=1114112 個編碼,編碼范圍為011141121,即00 x10FFFF。實際定義了238605個,分布在平面0、1、2、14(0000 1110)、15(0000 1111)、16(0001 0000),其中15和16平面只是分別定義了兩個專用區(qū),平面0的0 xE000-0 xF8

47、FF為專用區(qū),0 xD800-0 xDFFF為代理區(qū),因此目前真正定義的字符有238605-65534*2-6400-2048=99089個 。 因此,編碼集的取值范圍如下:UCS-2 UnicodeUCS-4,8.6.3、Unicode實現(xiàn)方式,整個Unicode 編碼系統(tǒng)或者說Unicode編碼標準可分為編碼方式和實現(xiàn)方式兩個層次。編碼方式是指Unicode如何對各種語言的每個字符進行編號的;而實現(xiàn)方式則是指同一個字符的Unicode編碼在不同的系統(tǒng)中的程序實現(xiàn)方式,比如漢語中的“字”在Unicode中的編號為23383,那么它在Windows或Mac OS操作系統(tǒng)中分別占幾個字節(jié)?如何存

48、儲與表達?等等,這些問題就是指Unicode的實現(xiàn)方式。 例:漢字“嚴”的Unicode是十六進制數(shù)0 x4E25,轉換成二進制數(shù)足足有15位(100 1110 0010 0101),也就是說這個符號的表示至少需要2個字節(jié)。表示其他更大的符號,可能需要3個字節(jié)或者4個字節(jié),甚至更多。 如何才能區(qū)分Unicode和ASCII? 計算機怎么知道三個字節(jié)表示一個符號,而不是分別表示三個符號呢? 如果Unicode統(tǒng)一規(guī)定,每個符號用三個或四個字節(jié)表示,那么每個英文字母前都必然有二到三個字節(jié)是0,這對于存儲來說是極大的浪費,文本文件的大小會因此大出二三倍,這是無法接受的。 它們造成的結果是:1)出現(xiàn)了

49、Unicode的多種存儲方式,也就是說有許多種不同的二進制格式,可以用來表示Unicode。2)Unicode在很長一段時間內(nèi)無法推廣,直到互聯(lián)網(wǎng)的出現(xiàn),8.6.3、Unicode實現(xiàn)方式,在Unicode中,漢語字對應的編碼是23383。在Unicode中,我們有很多方式將數(shù)字23383表示成程序中的數(shù)據(jù),包括:UTF-8、UTF-16、UTF-32。UTF是UCS Transformation Format的縮寫,可以翻譯成Unicode字符集轉換格式,即怎樣將Unicode定義的數(shù)字轉換成程序數(shù)據(jù)。 例如,漢字對應的數(shù)字是0 x6C49和0 x5B57,而編碼的程序數(shù)據(jù)是: BYTE d

50、ata_utf8 = 0 xE6, 0 xB1, 0 x89, 0 xE5, 0 xAD, 0 x97; / UTF-8 WORD data_utf16 = 0 x6C49, 0 x5B57; / UTF-16編碼 DWORD data_utf32 = 0 x6C49, 0 x5B57; / UTF-32編碼 這里用BYTE、WORD、DWORD分別表示無符號8位整數(shù),無符號16位整數(shù)和無符號32位整數(shù)。UTF-8、UTF-16、UTF-32分別以BYTE、WORD、DWORD作為編碼單位。漢字的UTF-8編碼需要6個字節(jié)。漢字的UTF-16編碼需要兩個WORD,大小是4個字節(jié)。漢字的UTF-

51、32編碼需要兩個DWORD,大小是8個字節(jié),8.6.3、Unicode實現(xiàn)方式:UTF-8,8.6.3、Unicode實現(xiàn)方式:UTF-8,8.6.3、Unicode實現(xiàn)方式:UTF-16,UTF-16編碼以16位無符號整數(shù)為單位。令某個字符的Unicode編碼為U,則用UTF-16實現(xiàn)該編碼的規(guī)則如下: 如果U0 x10000,U的UTF-16編碼就是U對應的16位無符號整數(shù)(為書寫簡便,下文將16位無符號整數(shù)記作WORD)。 如果U0 x10000,我們先計算U=U-0 x10000,然后將U寫成二進制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16編碼(二進制)就

52、是:110110yy yyyyyyyy 110111xx xxxxxxxx,8.6.3、Unicode實現(xiàn)方式:UTF-16,例:已知Unicode編碼,計算其UTF-16編碼 U=0 x20C30 U0 x10000 U=U-0 x10000=0 x10C30=0001 0000 1100 0011 0000 用前10位依次替代模板中的y,用后10位依次替代模板中的x,得:1101100001000011 1101110000110000,即0 xD843 0 xDC30,此為4字節(jié)的UTF-16編碼,8.6.3、Unicode實現(xiàn)方式:UTF-16,根據(jù)規(guī)則,U0 x10000,用四個字節(jié)

53、表示,其中第一個WORD是110110yy yyyyyyyy,其最小為11011000 00000000,最大為11011011 11111111,即0 xD800-0 xDBFF,同理第二個WORD為0 xDC00-0 xDFFF,這2048個編碼在Unicode中為專用,正是使用于此。 也就是說,沒有單個字符的編碼是位于0 xD800-0 xDFFF之間的,如果出現(xiàn)在此范圍內(nèi),說明是由四個字節(jié)構成一個字符;否則是由兩個字節(jié)構成一個字符。 則,給定UTF-16編碼序列,怎么知道這是一個WORD的UTF-16編碼還是兩個WORD的UTF-16編碼呢,8.6.3、Unicode實現(xiàn)方式:UTF-

54、16,高位替代是指這個范圍(0 xD800-0 xDB7F)的碼位是兩個WORD的UTF-16編碼的第一個WORD。 低位替代就是指這個范圍(0 xDC00-0 xDFFF)的碼位是兩個WORD的UTF-16編碼的第二個WORD。 高位專用替代是兩個WORD的UTF-16編碼的第一個WORD在此范圍內(nèi)對應的是Unicode專用區(qū)編碼0 xF0000-0 x10FFFF,它在Unicode中沒有定義,8.6.3、Unicode實現(xiàn)方式:UTF-16,例:從網(wǎng)絡上讀取到一個字節(jié)序列(16進制表示:D9 E2 DD E5),同時假設該字節(jié)序列是UTF-16字符,并使用Big Endian(即高位在低

55、地址,低位在高地址)的字節(jié)序,判斷該序列是一個字符(4字節(jié)編碼)還是兩個字符(2字節(jié)編碼): 每次讀入兩個字節(jié)(先讀入D9 E2),根據(jù)Big Endian規(guī)則,該數(shù)據(jù)為0 xD9E2。我們先檢查一下,這兩個字節(jié)是不是在0 xD800-0 xDB7F范圍內(nèi)?如果是,那表示我們讀到了一個超過兩個字節(jié)的字符,在本例中,0 xD9E2正好是在這個范圍內(nèi),因此,我們就知道這兩個字節(jié)跟接下來的兩個字節(jié)0 xDDE5才能組成一個真正的字符;如果不在0 xD800-0 xDBFF范圍內(nèi),那簡單了,這就是一個雙字節(jié)的字符而已。 由此,本例中的字符即為0 xD9E2DDE5,根據(jù)UTF-16編碼規(guī)則,將其展開為

56、二制位如下: 110110 0111100010 110111 0111100101 再根據(jù)UTF-16編碼規(guī)則,得到U=0111100010 0111100101,再計算U= U+0 x10000=0 x889E5,這就是該字符對應的Unicode碼,8.6.3、Unicode實現(xiàn)方式:UTF-16,根據(jù)UTF-16編碼規(guī)則,若第一個WORD位于高位替代范圍(0 xD800-0 xDB7F),則第二個WORD必然位于低位替代范圍(0 xDC00-0 xDFFF),否則必然導致亂碼; 若第一個WORD位于高位專用替代范圍(0 xDB80-0 xDBFF),則第二個WORD必須位于低位替代范圍(

57、0 xDC00-0 xDFFF),否則必然導致亂碼; 若第一個WORD位于高位專用替代范圍(0 xDB80-0 xDBFF),且第二個WORD位于低位替代范圍(0 xDC00-0 xDFFF),但接受方與發(fā)送方在該范圍內(nèi)的Unicode編碼實現(xiàn)方式不一致,則導致亂碼,8.6.4、Unicode實現(xiàn)方式:UTF-32,UTF-32編碼以32位無符號整數(shù)為單位。Unicode的UTF-32編碼就是其對應的32位無符號整數(shù),8.6.5、字節(jié)序:Little Endian和Big Endian,名稱來自英國作家斯威夫特的格列佛游記:因人們爭論吃雞蛋時究竟是從大頭(Big-Endian)敲開還是從小頭(

58、Little-Endian)敲開前后爆發(fā)了六次戰(zhàn)爭,一個皇帝送了命,另一個皇帝丟了王位。 Little Endian和Big Endian是CPU處理多字節(jié)數(shù)的不同方式。例如“漢”的Unicode編碼是0 x6C49。那么寫到文件里或從網(wǎng)絡發(fā)送出去時(按字節(jié)流)時,究竟是將6C寫在前面,還是將49寫在前面?如果將6C寫在前面,就是Big Endian;如果將49寫在前面,就是Little Endian。 為什么電子郵件常常出現(xiàn)亂碼?就是因為發(fā)信人和收信人使用的編碼方式、字節(jié)序不一樣,8.6.5、字節(jié)序:Little Endian和Big Endian,Unicode規(guī)范中定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做“零寬度非換行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示,正好是兩個字節(jié),而且FF比FE大1。 定義:如果一個序列的頭兩個字節(jié)是FE FF,就表示該文件采用Big

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論