




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第八章文件和流图形图像研究所计算机科学与技术、软件学院
浙江工业大学高飞,陆佳炜等。Java程序设计实用教程。北京:清华大学出版社,2021〔ISBN:978-7-302-31695-4)高飞,赵小敏等。Java程序设计实用教程习题集。北京:清华大学出版社,2021〔ISBN:978-7-302-32051-7)高飞教授,博士生导师Tel.RL:://前言本章的目的:什么是流?什么是字节流和字符流?如何访问磁盘上的文件属性?如何将对象保存到磁盘文件中〔对象序列化〕?Java中乱码问题是如何形成的?回忆关键词:用户的输入是邪恶的!Try-catch-finally、throws、throw小节安排文件和流8.2.1输入字节流:InputStream8.3、字符流8.4、文件8.2.2输出字节流:OutputStream8.3.1输入字符流:Reader8.3.2输出字符流:Writer8.3.3BufferedReader和BufferedWriter8.1、流的根本概念8.2、字节流8.5、对象序列化8.6、Java中的乱码问题8.3.4字节流和字符流的异同8.1、流的根本概念8.2、字节流8.2、字节流输入字节流:InputStreamInputStream类常用的public方法方法定义功能说明abstractintread()throwsIOException读一个字节并按int类型返回intread(byte[]
b)throwsIOException将数据读入byte[],返回实际读取的字节数intread(byte[]
b,int
off,int
len)throwsIOException读取最多len个字节到数组b中,返回实际读取数量publiclongskip(long
n)throwsIOException跳过n个字节publicvoidclose()throwsIOException关闭输入流并释放与该流有关联的系统资源8.2、字节流/*从mytext.txt文件读出并显示在屏幕上*/importjava.io.*;publicclassFileIn{publicstaticvoidmain(Stringargs[]){try{FileInputStreamrf=newFileInputStream("H:/java/temp/mytext.txt");//翻开文件intb;while((b=rf.read())!=-1)//用read()方法逐个字节读取 System.out.print((char)b);//转换成char并显示rf.close();}catch(IOExceptionie){System.out.println(ie);}catch(Exceptione){System.out.println(e);}}}8.2、字节流输出字节流:OutputStreamOutputStream类常用的public方法方法定义功能说明abstractvoidwrite(int
b)throwsIOException将一个字节b输出,根据java规定,实际输出的是参数b的低8位,其余24个高位将被忽略。例如,若b=825373492,即十六进制0x31323334,则只输出低8位即0x34,即最后输出为字符'4'voidwrite(byte[]
b)throwsIOException将数组b逐字节输出voidwrite(byte[]
b,int
off,int
len)throwsIOException将数组b中从off开始的len个字节输出8.2、字节流/*以下例如用于说明如何利用FileOutputStream进行文件复制*/importjava.io.*;publicclassTestFileCopy{publicstaticvoidmain(Stringargs[]){try{//复制的源文件TestVector.javaFileInputStreamrf=newFileInputStream("G:/java/TestVector.java");//复制的目的文件TV2.txt,假设不存在,那么会自动创立FileOutputStreamwf=newFileOutputStream("G:/java/TV2.txt");byteb[]=newbyte[512];intcount=-1;//每次读取512个字节,count用于记录实际读取的字节数while((count=rf.read(b,0,512))!=-1)wf.write(b,0,count);rf.close();wf.close();}catch(IOExceptionie){System.out.println(ie.toString());}catch(Exceptione){System.out.println(e.toString());}}}小节安排文件和流8.2.1输入字节流:InputStream8.3、字符流8.4、文件8.2.2输出字节流:OutputStream8.3.1输入字符流:Reader8.3.2输出字符流:Writer8.3.3BufferedReader和BufferedWriter8.1、流的根本概念8.2、字节流8.5、对象序列化8.6、Java中的乱码问题8.3.4字节流和字符流的异同8.3、字符流8.3、字符流输入字符流:ReaderReader类常用的public方法方法定义功能说明intread()throwsIOException读单个字符,以int类型返回intread(char[]cbuf)throwsIOException读字符放入数组cbuf中intread(char[]cbuf,intoffset,intlength)throwsIOException读字符放入数组的指定位置booleanready()throwsIOException测试当前流是否准备好进行读voidclose()关闭流longskip(longn)跳过n个字符8.3、字符流/*读取G:/aillo.txt文件的内容(一行一行读),并将其内容写入G:/jacky.txt中知识点:java读文件、写文件---<以字符流方式>*/importjava.io.*;publicclassTestFileWR{publicstaticvoidmain(String[]args){try{//创立FileReader对象,用来读取字符流FileReaderfr=newFileReader("G:/aillo.txt");//缓冲指定文件的输入BufferedReaderbr=newBufferedReader(fr);//创立FileWriter对象,用来写入字符流FileWriterfw=newFileWriter("G:/jacky.txt");//将缓冲对文件的输出BufferedWriterbw=newBufferedWriter(fw);StringstrLine;//用来保存每次读取的一行8.3、字符流while(br.ready()){strLine=br.readLine();//读取一行bw.write(strLine);//写入文件bw.newLine();System.out.println(strLine);//在屏幕上输出}bw.flush();//刷新该流的缓冲,即将该流输出到目的bw.close();br.close();fw.close();fr.close();}catch(IOExceptione){e.printStackTrace();}}}8.3、字符流输出字符流:WriterWriter类常用的public方法方法定义功能说明intwrite(intc)throwsIOException输出单个字符。要输出的字符c包含在给定整数值的16个低位中,16高位被忽略。例如,若b=825360437,即十六进制0x31320035,则只输出低16位即0x0035(为字符'5'的ASCII码),即最后输出为字符'5'intwrite(char[]cbuf)throwsIOException输出字符数组cbufintwrite(char[]cbuf,intoffset,intlen)throwsIOException将字符数组中从offset开始的len个字符输出intwrite(Stringstr)throwsIOException输出字符串strintwrite(Stringstr,intoffset,intlength)throwsIOException输出字符串str中从offset开始的len个字符abstractvoidclose()关闭输出字符流abstractvoidflush()强行写8.3、字符流/*从键盘输入一行文字,写入文件TestFileOut.txt中*/importjava.io.*;publicclassTestFileOut{publicstaticvoidmain(Stringargs[]){charc[]=newchar[512];byteb[]=newbyte[512];intn,i;try{FileWriterwf=newFileWriter("TestFileOut.txt");//从键盘读入文字并存入字节数组b中n=(b);for(i=0;i<n;i++)c[i]=(char)b[i];wf.write(c);wf.close();}catch(IOExceptione){System.out.println(e);}}}输入"123456ABCDEF"并回车输入"Java是一门优秀的语言!"并回车8.3、字符流/*从键盘输入一行文字〔可输入中文字符〕,写入文件TestFileOut.txt中*/importjava.io.*;publicclassTestFileOutCH{publicstaticvoidmain(Stringargs[]){charc[]=newchar[512];intn,i;try{FileWriterwf=newFileWriter("TestFileOutCH.txt");//利用InputStreamReader正确读取中文("请输入中文:");InputStreamReaderisr=newInputStreamReader(System.in);n=isr.read(c,0,512);//一次性读取512个字符,n表示实际读取的字符数wf.write(c);wf.close();("刚输入的数据为:"+String.valueOf(c,0,n));}catch(IOExceptione){System.out.println(e);}}}8.3、字符流字符缓冲流:BufferedReader和BufferedWriter以缓冲区方式对数据进行输入输出。所谓缓冲区,是指一片临时的内存区域。BufferedReader和BufferedWriter分别拥有8192个字符(16384个字节)的缓冲区。当BufferedReader从源〔文件、网络、键盘或其他进程〕读取字符数据时,会先尽量从源中读入字符数据并置入缓冲区,而之后假设使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据缺乏,才会再从源中读取。使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写入。例如一个文件,通过缓冲区可减少对硬盘的输入/输出动作,以提高文件存取的效率。8.3、字符流/*本程序首先在控制台输入字符(逐行输入),程序将输入的文字存储至指定的文件中,如果要结束程序,输入quit字符串即可。*/importjava.util.*;importjava.io.*;publicclassTestFileBRW{publicstaticvoidmain(String[]args){try{//缓冲System.in输入流//System.in是字节流,通过InputStreamReader将其转换为字符流BufferedReaderbufReader=newBufferedReader(newInputStreamReader(System.in));//缓冲FileWriterBufferedWriterbufWriter=newBufferedWriter(newFileWriter(args[0]));Stringinput=null;//每读一行进行一次写入动作while(!(input=bufReader.readLine()).equals("quit")){bufWriter.write(input);8.3、字符流/*newLine()方法写入与操作系统相依的换行字符,依执行环境当时的OS来决定该输出那种换行字符*/bufWriter.newLine();}bufReader.close();bufWriter.close();}catch(ArrayIndexOutOfBoundsExceptione){("没有指定文件");}catch(IOExceptione){e.printStackTrace();}}}8.3、字符流字节流和字符流的异同本质区别在于byte和char。字节流采用二进制直接传输,用字符流那么牵涉到本地系统的编码问题,在网络通讯中,强烈建议使用byte字节流方式。字节流与字符之间的转化通过
InputStreamReader和OutputStreamWriter来关联,实际上是通过byte[]和String来关联。在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。在从字节流转化为字符流时,也就是从byte[]转化为String时,使用如下构造方法: public
String(byte
bytes[],
String
charsetName) 这个方法中有一个关键的字符集编码参数charsetName,通常我们都省略了,那系统就用操作系统的默认的language。而在字符流转化为字节流时,实际上是String转化为byte[]时,是使用如下方法进行转化: byte[]
String.getBytes(String
charsetName)字符流和字节流是根据处理数据的不同来区分的。字节流按照8位传输,字符流按照16位传输由于字符流使用Unicode字符集,支持多国文字,因此假设流要跨越多种平台传输,应使用字符流。字符流的传输效率比字节流的高。小节安排文件和流8.2.1输入字节流:InputStream8.3、字符流8.4、文件8.2.2输出字节流:OutputStream8.3.1输入字符流:Reader8.3.2输出字符流:Writer8.3.3BufferedReader和BufferedWriter8.1、流的根本概念8.2、字节流8.5、对象序列化8.6、Java中的乱码问题8.3.4字节流和字符流的异同、文件File文件相关属性例如://假设当前目录为G:/Java1,有文件"G:/Java1/TestFilePRO.java"和目录//"G:/Java1/gfei"Filef=newFile("TestFilePRO.java");Filed=newFile("Gfei");
System.out.println("f.getName="+f.getName());//输出TestFilePRO.javaSystem.out.println("f.getPath="+f.getPath());////输出TestFilePRO.java//以下语句输出G:\Java1\TestFilePRO.javaSystem.out.println("f.getAbsolutePath="+f.getAbsolutePath());System.out.println("f.getParent="+f.getParent());//输出nullSystem.out.println("f.length="+f.length());//笔者例如中输出668System.out.println("d.getName="+d.getName());//输出GfeiSystem.out.println("d.getPath="+d.getPath());//输出GfeiSystem.out.println("d.getAbsolutePath="+d.getAbsolutePath());//输出G:\java1\GfeiSystem.out.println("d.getParent="+d.getParent());//输出nullSystem.out.println("d.length="+d.length());//输出0、文件File文件操作例如:Fileds=newFile("subdir");Filedd=newFile("gfei1");Filefs=newFile("TestFileSRC.java");Filefd=newFile("TestFileDEST.txt");ds.renameTo(dd);//改为gfei1fs.renameTo(fd);//改为TestFileDEST.txtpublicbooleanrenameTo(File
dest)//重新命名文件或目录//删除文件或目录。如果是目录,那么该目录必须为空才能删除。publicbooleandelete()、文件目录操作publicbooleanmkdir()//创立目录publicString[]list()//返回一个字符串数组,是给定目录下的文件与子目录publicFile[]listFiles()////返回File对象数组,是给定目录下的文件、文件/*打印某目录下(包含子目录)所有文件和文件大小*/importjava.io.*;publicclassTestFileLIST{publicstaticvoidmain(Stringargs[])throwsIOException{Filefiles=newFile(".");//"."表示当前目录(与TestFileLIST.java所在的同一个目录)listPath(files);}publicstaticvoidlistPath(Filef)throwsIOException{Stringfile_list[]=f.list();for(inti=0;i<file_list.length;i++){Filecf=newFile(f.getPath(),file_list[i]);if(cf.isDirectory()){//判断是否为子目录 listPath(cf);//列举该子目录下的文件}if(cf.isFile()){//判断是否为文件try{//输出文件大小System.out.println(cf.getCanonicalPath()+":"+cf.length());}catch(IOExceptione){e.printStackTrace();}}}}}、随机访问文件类:RandomAccessFileRandomAccessFile类提供了对文件的随机访问方式,即可在文件的任意位置读或写数据而且可以同时进行读和写的操作RandomAccessFile构造方法及常用的方法方法定义功能说明RandomAccessFile(Filefile,Stringmode)throwsFileNotfoundExceptionfile待访问的文件,mode设定访问方式:"r"表示只读,"w"表示写,"rw"表示读写RandomAccessFile(Stringname,Stringmode)throwsFileNotfoundExceptionname是文件名字符串longlength()throwsIOException返回文件的长度,以字节为单位voidseek(longpos)throwsIOException改变文件指针的位置finalintreadInt()throwsIOException读一个整型数据finalvoidwriteInt(intv)throwsIOException写入一个整型数据longgetFilePointer()throwsIOException返回文件指针的位置close()throwsIOException关闭文件、随机访问文件类:RandomAccessFile提供了类似于readInt()的其他方法用于读取byte、boolean、char、double、float、long、short、unsignedbyte、unsignedshort等数据类型的方法,即readByte()、readBoolean()、readChar()、readDouble()、readFloat()、readLong()、readShort()、readUnsignedByte()、readUnsignedShort()等提供了writeByte()、writeBoolean()、writeChar()、writeDouble()、writeFloat()、writeLong()、writeShort()等方法用于写入数据。、随机访问文件类:RandomAccessFile/*向文件中写入10个数据,第i个数据=圆周率*i(i=0,1,2,…,9),然后将第2个(i=2)改为0,最后将10个数据全部输出*/import;import;publicclassTestFileRAF{publicstaticvoidmain(Stringargs[]){try{RandomAccessFilef=new RandomAccessFile("TestFileRAF.txt","rw");//可读写inti;doubled;//写:向文件写入10个数据for(i=0;i<10;i++)f.writeDouble(Math.PI*i);、随机访问文件类:RandomAccessFile//修改:对文件中第2个double数据改为0f.seek(16);//文件指针往前走16个字节(2个double数据)f.writeDouble(0);f.seek(0);//文件指针回到文件首部//读取:将全部数据读出并打印到屏幕中for(i=0;i<10;i++){d=f.readDouble();("["+i+"]:"+d);}f.close();}catch(IOExceptione){("发生异常:"+e);e.printStackTrace();}}}、随机访问文件类:RandomAccessFile、文件过滤接口:FileFilter和FilenameFilterFileFilter和FilenameFilter是为开发者提供在文件系统中进行过滤或者说是搜索所需要文件的功能,均有accept()方法:
//FileFilter:file表示要过滤目录中的文件对象
publicboolean
accept(Filefile);/*FilenameFilter:参数dir是要过滤的目录,
name是目录中的文件名*/publicboolean
accept(Filedir,Stringname);区别:FileFilter提供文件对象的访问方法,而FilenameFilter是按照目录和文件的名称的方式来工作。、文件过滤接口:FileFilter和FilenameFilter实现文件或目录过滤所需步骤声明一个过滤器类并实现FileFilter或FilenameFilter接口中的accept方法。使用File类的list()和listFiles()进行过滤,其参数为第①步中的过滤器类的对象作为参数,就可实现对文件名的过滤。File类的list()和listFiles()方法声明如下:publicString[]list(FilenameFilterfilter)publicFile[]listFiles(FilenameFilterfilter)publicFile[]listFlies(FileFilterfilter)
其中,filter须是第①步中实现了FileFilter或FilenameFilter接口中的accept方法的过滤器类的对象。、文件过滤接口:FileFilter和FilenameFilterimportjava.io.*;//第①步:声明过滤类ListFilter并实现FilenameFilter接口中的accept方法classListFilterimplementsFilenameFilter{privateStringpre=“〞,ext=“〞;//pre表示文件前缀,ext表示文件后缀publicListFilter(Stringfilterstr){inti,j;filterstr=filterstr.toLowerCase();i=filterstr.indexOf("*");j=filterstr.indexOf(".");if(i>0)pre=filterstr.substring(0,i);if(i==-1&j>0)pre=filterstr.substring(0,j);if(j>=0)ext=filterstr.substring(j+1);}、文件过滤接口:FileFilter和FilenameFilter//实现accept方法publicbooleanaccept(Filedir,Stringfilename){booleany=true;try{filename=filename.toLowerCase();y=filename.startsWith(pre)&filename.endsWith(ext);}catch(NullPointerExceptione){}returny;}}、文件过滤接口:FileFilter和FilenameFilter//第②步:使用File类的list()和listFiles()进行过滤publicclassTestFileSearch{publicstaticvoidmain(Stringargs[]){//要求两个参数:第一个参数表示目录,第二参数表示要过滤的文件StringstrDir,strExtension;switch(args.length){case1:strDir=".";strExtension=args[0];break;case2:strDir=args[0];strExtension=args[1];break;default:("需两个参数!");return;} Filef=newFile(strDir);ListFilterls=newListFilter(strExtension);Stringstr[]=f.list(ls);for(inti=0;i<str.length;i++)System.out.println(str[i]);}}小节安排文件和流8.2.1输入字节流:InputStream8.3、字符流8.4、文件8.2.2输出字节流:OutputStream8.3.1输入字符流:Reader8.3.2输出字符流:Writer8.3.3BufferedReader和BufferedWriter8.1、流的根本概念8.2、字节流8.5、对象序列化8.6、Java中的乱码问题8.3.4字节流和字符流的异同8.5、对象序列化什么是对象序列化保存内存中某个对象的方法很多,但是Java给你提供一种更好的保存对象状态的机制,那就是对象序列化。java中对象序列化的过程就是将对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后,可以保存到文件、通过管道输出到另一线程或通过网络连接将对象数据发送到另一主机。8.5、对象序列化什么情况下需要序列化把内存中的对象保存到一个文件中或者数据库中用套接字(socket)在网络上传送对象通过RMI传输对象8.5、对象序列化实现序列化的步骤第一步:创立一个FileOutputStream对象,如下:FileOutputStreamfs=newFileOutputStream("foo.ser");第二步:负责将对象写入字节流。因此第二步是利用第一步创立的FileOutputStream对象创新一个ObjectOutputStream对象,如下:ObjectOutputStreamos=newObjectOutputStream(fs);第三步:将对象写入FoomyFoo1=newFoo();FoomyFoo2=newFoo();FoomyFoo3=newFoo();os.writeObject(myFoo1);os.writeObject(myFoo2);os.writeObject(myFoo3);第四步:关闭ObjectOutputStreamos.close();8.5、对象序列化序列化对象的条件被序列化的对象必须是实现接口的类对象。接口中没有方法需要实现,之所以要implements该接口,只是告诉JVM,该类对象是可被序列化而已。例如:importsjava.io.*;classFooimplementsSerializable{intwidth;intheight;DatetodayDate;……}8.5、对象序列化反序列化反序列化是翻开字节流并重构对象。对象序列化不仅要将根本数据类型转换成字节表示,有时还要恢复数据。反序列化通常使用从字节流重构对象。例如:FileInputStreamin=newFileInputStream("foo.ser");ObjectInputStreamois=newObjectInputStream(in);FoomyFoo1=(Foo)ois.readObject();FoomyFoo2=(Foo)ois.readObject();FoomyFoo3=(Foo)ois.readObject();8.5、对象序列化import;/*Student定义为序列化类*/publicclassStudentimplementsSerializable{staticfinallongserialVersionUID=123456L;Stringm_name;intm_id;intm_height;publicStudent(Stringname,intid,inth){m_name=name;m_id=id;m_height=h;}publicvoidoutput(){("姓名:"+m_name);("学号:"+m_id);("身高:"+m_height);}}8.5、对象序列化/*将Student对象数据写入object.dat*/import;import;publicclassTestWriteObject{publicstaticvoidmain(Stringargs[]){try{ObjectOutputStreamf=newObjectOutputStream( newFileOutputStream("object.dat"));Students=newStudent("张三",2003001,172);f.writeObject(s);s.output();f.close();}catch(Exceptione){("发生异常:"+e);e.printStackTrace();}}}8.5、对象序列化/*从object.dat读出Student对象数据*/import;import;publicclassTestReadObject{publicstaticvoidmain(Stringargs[]){try{ObjectInputStreamf=newObjectInputStream(newFileInputStream("object.dat"));Students=(Student)(f.readObject());s.output();f.close();}catch(Exceptione){("发生异常:"+e);e.printStackTrace();}}}8.5、对象序列化序列化本卷须知只有对象的数据被保存,方法与构造函数不被序列化。声明为transient或static的变量不能被序列化。小节安排文件和流8.2.1输入字节流:InputStream8.3、字符流8.4、文件8.2.2输出字节流:OutputStream8.3.1输入字符流:Reader8.3.2输出字符流:Writer8.3.3BufferedReader和BufferedWriter8.1、流的根本概念8.2、字节流8.5、对象序列化8.6、Java中的乱码问题8.3.4字节流和字符流的异同、Java中字符的表达Java中字符表达charbyte网络传输或存储的序列化形式内存形式一个字节8bits一个Unicode字符16bits
String一组char、Java中字符的表达例如:Stringying="英";charcy=ying.charAt(0);//取得首字符//将"英"转换成java默认编码字符(UTF-16)StringyingHex=Integer.toHexString(cy);System.out.println(yingHex.toUpperCase());//输出默认编码:82F1//将"英"转换成GBK编码byte[]yingGBBytes=ying.getBytes("GBK");Stringhex;for(inti=0;i<yingGBBytes.length;i++){hex=Integer.toHexString(yingGBBytes[i]&0xFF);System.out.print(hex.toUpperCase());//"英"的GBK编码:D3A2}、Unicode简介什么是Unicode呢?Unicode,又称为统一码、万国码或单一码,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求,即为每种语言的每个字符设定一个统一的序数。ISO与Unicode独立开展,至1992年合并协同。从Unicode2.0开始,Unicode采用了与ISO10646-1相同的字库和字码;ISO也承诺,ISO10646将不会替超出0x10FFFF的UCS-4编码赋值,以使二者保持一致。、Unicode编码方式Unicode编码基于ISO10646定义的通用字符集〔UniversalCharacterSet,UCS〕,有UCS-2和UCS-4,分别用2个字节和4个字节编码。UCS-4定义如下,而UCS-4的高位的两个字节为0即为UCS-2
Unicode是UCS-4的子集,方案使用第0分组的17个平面,因此其编码数为:17(平面)*256(行)*256(码位)=1114112个编码,编码范围为0~11141121,即0~0x10FFFF。实际定义了238605个,分布在平面0、1、2、14〔00001110〕、15〔00001111〕、16〔00010000〕,其中15和16平面只是分别定义了两个专用区,平面0的0xE000-0xF8FF为专用区,0xD800-0xDFFF为代理区,因此目前真正定义的字符有238605-65534*2-6400-2048=99089个。因此,编码集的取值范围如下:UCS-2UnicodeUCS-4。、Unicode实现方式整个Unicode编码系统或者说Unicode编码标准可分为编码方式和实现方式两个层次。编码方式是指Unicode如何对各种语言的每个字符进行编号的;而实现方式那么是指同一个字符的Unicode编码在不同的系统中的程序实现方式,比方汉语中的“字〞在Unicode中的编号为23383,那么它在Windows或MacOS操作系统中分别占几个字节?如何存储与表达?等等,这些问题就是指Unicode的实现方式。例:汉字“严〞的Unicode是十六进制数0x4E25,转换成二进制数足足有15位〔100111000100101〕,也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。如何才能区分Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。它们造成的结果是:1〕出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode。2〕Unicode在很长一段时间内无法推广,直到互联网的出现。、Unicode实现方式在Unicode中,汉语"字"对应的编码是23383。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF是"UCSTransformationFormat"的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。例如,"汉字"对应的数字是0x6C49和0x5B57,而编码的程序数据是:BYTEdata_utf8[]={0xE6,0xB1,0x89,0xE5,0xAD,0x97};//UTF-8WORDdata_utf16[]={0x6C49,0x5B57};//UTF-16编码DWORDdata_utf32[]={0x6C49,0x5B57};//UTF-32编码这里用BYTE、WORD、DWORD分别表示无符号8位整数,无符号16位整数和无符号32位整数。UTF-8、UTF-16、UTF-32分别以BYTE、WORD、DWORD作为编码单位。"汉字"的UTF-8编码需要6个字节。"汉字"的UTF-16编码需要两个WORD,大小是4个字节。"汉字"的UTF-32编码需要两个DWORD,大小是8个字节。、Unicode实现方式:UTF-8UTF-8对Unicode的实现方式Unicode符号UTF-8实现方式(二进制)说明00000000-0000007F0xxxxxxx00000080-000007FF110xxxxx10xxxxxx00000800-0000FFFF1110xxxx10xxxxxx10xxxxxx00010000-0010FFFF11110xxx10xxxxxx10xxxxxx10xxxxxx00200000-03FFFFFF111110xx10xxxxxx10xxxxxx10xxxxxx10xxxxxx已经废除04000000-7FFFFFFF1111110x10xxxxxx10xxxxxx10xxxxxx10xxxxxx10xxxxxx、Unicode实现方式:UTF-8、Unicode实现方式:UTF-16UTF-16编码以16位无符号整数为单位。令某个字符的Unicode编码为U,那么用UTF-16实现该编码的规那么如下:如果U<0x10000,U的UTF-16编码就是U对应的16位无符号整数〔为书写简便,下文将16位无符号整数记作WORD〕。如果U≥0x10000,我们先计算U'=U-0x10000,然后将U'写成二进制形式:yyyyyyyyyyxxxxxxxxxx,U的UTF-16编码〔二进制〕就是:110110yyyyyyyyyy110111xxxxxxxxxx。UTF-16的编码规则Unicode编码范围(十六进制)UTF-16实现(二进制)10进制范围字节数00000000-0000FFFFxxxxxxxxxxxxxxxx0-65535200010000-0010FFFF110110yyyyyyyyyy110111xxxxxxxxxx65536-11141114、Unicode实现方式:UTF-16例:Unicode编码,计算其UTF-16编码U=0x20C30∵U≥0x10000∴U'=U-0x10000=0x10C30=00010000110000110000用前10位依次替代模板中的y,用后10位依次替代模板中的x,得:11011000010000111101110000110000,即0xD8430xDC30,此为4字节的UTF-16编码。、Unicode实现方式:UTF-16根据规那么,U≥0x10000,用四个字节表示,其中第一个WORD是110110yyyyyyyyyy,其最小为1101100000000000,最大为1101101111111111,即0xD800-0xDBFF,同理第二个WORD为0xDC00-0xDFFF,这2048个编码在Unicode中为专用,正是使用于此。也就是说,没有单个字符的编码是位于0xD800-0xDFFF之间的,如果出现在此范围内,说明是由四个字节构成一个字符;否那么是由两个字节构成一个字符。那么,给定UTF-16编码序列,怎么知道这是一个WORD的UTF-16编码还是两个WORD的UTF-16编码呢?、Unicode实现方式:UTF-16UTF-16的4字节代理说明UTF-16实现的编码范围英文说明中文说明0xD800-0xDB7FHighSurrogates高位替代0xDB80-0xDBFFHighPrivateUseSurrogates高位专用替代0xDC00-0xDFFFLowSurrogates低位替代高位替代是指这个范围(0xD800-0xDB7F)的码位是两个WORD的UTF-16编码的第一个WORD。低位替代就是指这个范围(0xDC00-0xDFFF)的码位是两个WORD的UTF-16编码的第二个WORD。高位专用替代是两个WORD的UTF-16编码的第一个WORD在此范围内对应的是Unicode专用区编码0xF0000-0x10FFFF,它在Unicode中没有定义、Unicode实现方式:UTF-16例:从网络上读取到一个字节序列(16进制表示:D9E2DDE5),同时假设该字节序列是UTF-16字符,并使用BigEndian(即高位在低地址,低位在高地址)的字节序,判断该序列是一个字符(4字节编码)还是两个字符(2字节编码):①每次读入两个字节(先读入D9E2),根据BigEndian规那么,该数据为0xD9E2。我们先检查一下,这两个字节是不是在0xD800-0xDB7F范围内?如果是,那表示我们读到了一个超过两个字节的字符,在本例中,0xD9E2正好是在这个范围内,因此,我们就知道这两个字节跟接下来的两个字节0xDDE5才能组成一个真正的字符;如果不在0xD800-0xDBFF范围内,那简单了,这就是一个双字节的字符而已。②由此,本例中的字符即为0xD9E2DDE5,根据UTF-16编码规那么,将其展开为二制位如下: 1101100111100010 1101110111100101再根据UTF-16编码规那么,得到U'=01111000100111100101,再计算U=U'+0x10000=0x889E5,这就是该字符对应的Unicode码。、Unicode实现方式:UTF-16根据UTF-16编码规那么,假设第一个WORD位于高位替代范围(0xD800-0xDB7F),那么第二个WORD必然位于低位替代范围(0xDC00-0xDFFF),否那么必然导致乱码;假设第一个WORD位于高位专用替代范围(0xDB80-0xDBFF),那么第二个WORD必须位于低位替代范围(0xDC00-0xDFFF),否那么必然导致乱码;假设第一个WORD位于高位专用替代范围(0xDB80-0xDBFF),且第二个WORD位于低位替代范围(0xDC00-0xDFFF),但接受方与发送方在该范围内的Unicode编码实现方式不一致,那么导致乱码。、Unicode实现方式:UTF-32UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。、字节序:LittleEndian和BigEndian名称来自英国作家斯威夫特的?格列佛游记?:因人们争论吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。LittleEndian和BigEndian是CPU处理多字节数的不同方式。例如“汉〞的Unicode编码是0x6C49。那么写到文件里或从网络发送出去时(按字节流)时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是BigEndian;如果将49写在前面,就是LittleEndian。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式、字节序不一样。、字节序:LittleEndian和BigEndianUnicode标准中定义,每一个文件的最前面分别参加一个表示编码顺序的字符,这个字符的名字叫做“零宽度非换行空格〞〔ZEROWIDTHNO-BREAKSPACE〕,用FEFF表示,正好是两个字节,而且FF比FE大1。定义:如果一个序列的头两个字节是FEFF,就表示该文件采用BigEndian方式;如果是FFFE,就表示该文件采用LittleEndian方式。根据字节序的不同,Unicode编码可实现为UTF-16LE、UTF-16BE、UTF-32LE、UTF-32BE字节序举例Unicode编码UTF-16LEUTF-16BEUTF32-LEUTF32-BE0x6C49496C6C49496C000000006C490x20C3043D830DCD843DC30300C020000020C30、字节序:LittleEndian和BigEndian怎么判断字节流(文件流、网络数据流)的字节序呢?Unicode标准建议用BOM〔ByteOrderMark〕
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论