您的位置:首页 > 编程语言 > Java开发

[学习笔记]Java IO之字符流

2014-12-20 13:43 411 查看

概述

字符流是专门用于处理文本文件的流,其中包括了文本编码相关转换工作。字符流只可以处理文本字符数据。每个字符流都包括一种编码方式,可以使用系统默认,也可以自行设定。

编码

1.编码表

生活中语言文字和计算机中对应的数据表

2.常见编码表

ASCII:包括字母和常见符号,1字节编码,首位为0。ISO8859-1:包括拉丁字母,1字节编码,首位为1。GB2312:简体中文码表,包括6~7千汉子及符号,2字节编码,2个字节首位均为1。GBK:中文码表,包括GB2312全部编码,约2万个汉子和符号,2字节编码,第一个字节首位为1,第二个字节任意。GB18030:中文码表,包括约7万个汉字及少数民族文字,1,2,4字节编码。Unicode:国际标准码表,包括大部分世界语言文字,2字节编码。char类型即为Unicode编码,而String类型为系统默认编码。UTF-8:基于Unicode的变长字符编码,每个字节头均有编码信息,国际标准。

3.案例

按字节数分割文本文件,避免出现多字节编码的割裂。
packageio.charstream;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.IOException;
publicclassText_Code{
publicstaticvoidmain(String[]args){
//按字节截取字符串内容,避免出现编码割裂
Filefile=newFile("temp\\split.txt");
Stringstr=null;
for(inti=0;i<file.length();i++){
for(intj=i+1;j<=file.length();j++){
try{
str=splitString(file,i,j);//截取文件字符,含头不含尾
System.out.println(str);
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
}
publicstaticStringsplitString(Filefile,inti,intj)throwsIOException{
FileInputStreamfis=newFileInputStream(file);
byte[]buffer=newbyte[1024];
fis.read(buffer);
fis.close();
/*
*判断某字节是否为双字节编码:
*由于默认的GBK编码中,可能为单字节编码,也可能是双字节编码。
*单字节字符对应整型为正,双字节字符第一个字节(后称为头字节)对应整型为负。
*所以判断该字节是否为双字节字符的第二个字节(后称为尾字节)的关键,在于在该字节之前距离该字节最近的头字节。
*因为连续负数字节的第一个字节一定是头字节,而连续负数的个数如果是偶数,则该字节刚好是尾子节。否则反之。
*如果该字节为头字节,则左包含右舍弃。
*/
intpos=i,len=j-i;
if(position(buffer,i)<0){
//如果截取下标位于尾字节,则下标向前移动一个字节,长度自增
pos=i-1;
len++;
}
if(position(buffer,j-1)>0){
//由于含头不含尾,所以判断j-1
//如果截取上标位于头字节,则上标向前移动一个字节,长度自减
len--;
}
returnnewString(buffer,pos,len);
}
//判断该字节为单字节,头字节还是尾字节
publicstaticintposition(byte[]buffer,inti){
intpoint=buffer[i];
if(point>0){
//该字节为正,则可能为单字节或尾字节,取决于之前连续负数的个数
if(negCount(buffer,i-1)%2==0){
return0;//单字节
}else{
return-1;//尾子节
}
}else{
//该字节为负,则可能为头字节或尾字节,取决于之前连续负数的个数(包括自己)
if(negCount(buffer,i)%2==0){
return-1;//尾子节
}else{
return1;//头字节
}
}
}
//获取该字节前(包括该字节)有多少个连续负数字节
publicstaticintnegCount(byte[]buffer,inti){
intsum=0;
while(i>=0&&buffer[i]<0){
sum++;
i--;
}
returnsum;
}
}

文件字符流(FileReader/FileWriter)

1.概述

目标:文件,内容:字符流,方向:输入/输出。能够很方便的处理文本文件,但也只能处理文本文件。同字节流类似,可以读写单个字符,也可以使用字符数组读写多个字符。FileReader/FileWriter使用的是系统默认编码。FileReader/FileWriter自带默认缓冲区。方法均继承自父类(InputStreamReader/OutputStreamWriter)。FileReader/FileWriter是其父类InputStreamReader/OutputStreamWriter的简易版本。

2.构造器

FileReader:FileReader(Filefile)FileReader(StringfileName)FileWriter:FileWriter(Filefile)FileWriter(Filefile,booleanappend)FileWriter(StringfileName)FileWriter(StringfileName,booleanappend)

3.常用方法

FileReader:voidclose()StringgetEncoding()intread()intread(char[]cbuf,intoffset,intlength)FileWriter:voidclose()voidflush()StringgetEncoding()voidwrite(char[]cbuf,intoff,intlen)voidwrite(intc)voidwrite(Stringstr,intoff,intlen)

4.注意

FileReader/FileWriter内置缓冲区和自定义缓冲区的区别?实际上,FileReader和FileWriter类加上使用自定义字符缓冲区,也就是两层缓冲,分别是OS层缓冲和APP层缓冲,第一层将数据从硬盘上读到内存中,第二层将第一层缓冲中的数据转换为字符。

5.示例

读取文件

publicstaticStringreadFile(){
StringBuilderstr=newStringBuilder();
char[]buffer=newchar[1024];
intlen=-1;
FileReaderfr=null;
try{
//使用自定义缓冲区读取文件
fr=newFileReader("temp\\char.txt");
while((len=fr.read(buffer))!=-1){
str.append(buffer,0,len);
}
}catch(IOExceptione){
System.out.println("读取文件失败!");
e.printStackTrace();
}finally{
//释放文件
if(fr!=null){
try{
fr.close();
}catch(IOExceptione){
System.out.println("释放文件失败!");
e.printStackTrace();
thrownewRuntimeException();
}
}
}
returnstr.toString();
}

写入文件

publicstaticvoidwriteFile(Stringstr){
FileWriterfw=null;
try{
//写入文件
fw=newFileWriter("temp\\char_out.txt");
fw.write(str.toCharArray());
}catch(IOExceptione){
System.out.println("写入文件失败!");
e.printStackTrace();
}finally{
//释放文件
if(fw!=null){
try{
fw.close();
}catch(IOExceptione){
System.out.println("释放文件失败!");
e.printStackTrace();
thrownewRuntimeException();
}
}
}
}

字符转换流(InputStreamReader/OutputStreamWriter)

1.概述

目标:字节流,内容:字符流,方向:输入/输出。InputStreamReader/OutputStreamWriter是字节流和字符流之间的桥梁,扮演转换的角色,其本质是字符流。InputStreamReader/OutputStreamWriter也是一个装饰类,对字节流的装饰,提供按指定编码识别字符功能。

2.构造器

InputStreamReader:InputStreamReader(InputStreamin)InputStreamReader(InputStreamin,Charsetcs)InputStreamReader(InputStreamin,CharsetDecoderdec)InputStreamReader(InputStreamin,StringcharsetName)OutputStreamWriter:OutputStreamWriter(OutputStreamout)OutputStreamWriter(OutputStreamout,Charsetcs)OutputStreamWriter(OutputStreamout,CharsetEncoderenc)OutputStreamWriter(OutputStreamout,StringcharsetName)

3.常用方法

InputStreamReader:voidclose()StringgetEncoding()intread()intread(char[]cbuf,intoffset,intlength)OutputStreamWriter:voidclose()voidflush()StringgetEncoding()voidwrite(char[]cbuf,intoff,intlen)voidwrite(intc)voidwrite(Stringstr,intoff,intlen)

4.示例

按指定编码写入
publicstaticvoidwriteFile_UTF8(Stringstr){
OutputStreamWriterosw=null;
try{
osw=newOutputStreamWriter(newFileOutputStream("temp\\char_utf8.txt"),"UTF-8");
osw.write(str.toCharArray());
}catch(UnsupportedEncodingExceptione){
//不支持的字符编码
e.printStackTrace();
}catch(FileNotFoundExceptione){
//文件没有找到
e.printStackTrace();
}catch(IOExceptione){
//文件写入失败
e.printStackTrace();
}finally{
if(osw!=null){
try{
osw.close();
}catch(IOExceptione){
//文件关闭失败
e.printStackTrace();
thrownewRuntimeException("文件关闭失败!");
}
}
}
}

缓冲字符流(BufferedReader/BufferedWriter)

1.概述

BufferedReader/BufferedWriter是字符流的装饰类,用于提供缓冲功能,类似于字节流的BufferedInputStream/BufferedOutputStram。BufferedReader/BufferedWriter可以指定缓冲区的大小。BufferedReader/BufferedWriter提供了按行读取写入功能,其他的字符流是不具备的。通过用合适的BufferedReader替代每个DataInputStream,可以对将DataInputStream用于文字输入的程序进行本地化。为了达到最高效率,推荐考虑在BufferedReader/BufferedWriter内包装InputStreamReader/OutputStreamWriter。BufferedWriter提供了newLine方法,能够写入当前系统支持的换行符。

2.构造器

BufferedReader:BufferedReader(Readerin)BufferedReader(Readerin,intsz)BufferedWriter:BufferedWriter(Writerout)BufferedWriter(Writerout,intsz)

3.常用方法

BufferedReader:voidclose()intread()intread(char[]cbuf,intoff,intlen)StringreadLine()longskip(longn)BufferedWriter:voidclose()voidflush()voidnewLine()voidwrite(char[]cbuf,intoff,intlen)voidwrite(intc)voidwrite(Strings,intoff,intlen)

4.示例

packageio.charstream;
importjava.io.BufferedReader;
importjava.io.BufferedWriter;
importjava.io.FileReader;
importjava.io.FileWriter;
importjava.io.IOException;
publicclassCharBufferIODemo{
publicstaticvoidmain(String[]args)throwsIOException{
Stringstr=null;
//读取文件
BufferedReaderbr=newBufferedReader(newFileReader("temp\\char.txt"));
while((str=br.readLine())!=null){
System.out.println(str);
}
br.close();
//复制文件
copyFileUseBuffer("temp\\char.txt");
}
publicstaticvoidcopyFileUseBuffer(Stringstring)throwsIOException{
BufferedReaderbr=newBufferedReader(newFileReader(string));
//将文件扩展名前添加_copy来命名复制的文件
string=newStringBuilder(string).insert(string.lastIndexOf('.'),"_copy").toString();
BufferedWriterbw=newBufferedWriter(newFileWriter(string));
Stringstr=null;
while((str=br.readLine())!=null){
bw.write(str);
bw.newLine();
}
br.close();
bw.close();
}
}

案例

使用模板方法构建复杂度检测类

GetComplexity.java
packagetools;
publicabstractclassGetComplexity{
//模板方法,实现具体算法骨架,不确定的部分由子类定义。
privatefinalRuntimes_runtime=Runtime.getRuntime();
privateStringname=null;
publicGetComplexity(){
super();
}
publicGetComplexity(Stringname){
super();
this.name=name;
}
publicfinallong[]start(){
//获取起始时间
longstartTime=System.nanoTime();
runGC();
//获取起始内存消耗
longstartMemory=s_runtime.totalMemory()-s_runtime.freeMemory();
run();
//获取实际时间和内存消耗
long[]use=
{System.nanoTime()-startTime,
s_runtime.totalMemory()-s_runtime.freeMemory()-startMemory};
if(isPrinted()){
if(name==null){
System.out.println(this.getClass().getName()+":");
}else{
System.out.println(this.name+":");
}
System.out.printf("EstimatedTimeis%.3fms.\n",(double)use[0]/1000000);
System.out.printf("UsedMemoryis%.3fKB.\n",(double)use[1]/1000);
}
returnuse;
}
privatevoidrunGC(){
for(inti=0;i<4;i++){
System.gc();
}
}
publicabstractvoidrun();
//这是一个hook,可以由子类扩展功能。
publicbooleanisPrinted(){
returntrue;
}
}

1.使用各种方法复制一个大的单行文本文件,并比较复杂度

各种方法复制文本文件的工具类CharFileCopy.java
packageio.charstream;
importjava.io.BufferedReader;
importjava.io.BufferedWriter;
importjava.io.File;
importjava.io.FileReader;
importjava.io.FileWriter;
importjava.io.IOException;
publicclassCharFileCopy{
/**
*使用FileReader/Writer复制文本
*
*@paramold
*@paramnewFile
*@throwsIOException
*/
publicstaticvoidcopyCharFile(Fileold,FilenewFile)throwsIOException{
FileReaderfr=newFileReader(old);
FileWriterfw=newFileWriter(newFile);
intch=-1;
while((ch=fr.read())!=-1){
fw.write(ch);
}
fr.close();
fw.close();
}
/**
*使用自定义缓冲区的FileReader/Writer复制文本
*
*@paramold
*@paramnewFile
*@throwsIOException
*/
publicstaticvoidcopyCharFile_UseArr(Fileold,FilenewFile)throwsIOException{
FileReaderfr=newFileReader(old);
FileWriterfw=newFileWriter(newFile);
intlen=-1;
char[]buf=newchar[1024];
while((len=fr.read(buf))!=-1){
fw.write(buf,0,len);
}
fr.close();
fw.close();
}
/**
*使用BufferedReader/Writer复制文本
*
*@paramold
*@paramnewFile
*@throwsIOException
*/
publicstaticvoidbufCopyCharFile(Fileold,FilenewFile)throwsIOException{
BufferedReaderbr=newBufferedReader(newFileReader(old));
BufferedWriterbw=newBufferedWriter(newFileWriter(newFile));
intch=-1;
while((ch=br.read())!=-1){
bw.write(ch);
}
br.close();
bw.close();
}
/**
*使用BufferedReader/Writer按行复制文本
*
*@paramold
*@paramnewFile
*@throwsIOException
*/
publicstaticvoidbufCopyCharFile_UseLine(Fileold,FilenewFile)throwsIOException{
BufferedReaderbr=newBufferedReader(newFileReader(old));
BufferedWriterbw=newBufferedWriter(newFileWriter(newFile));
Stringline=null;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
}
br.close();
bw.close();
}
/**
*使用BufferedReader/Writer按数组复制文本
*
*@paramold
*@paramnewFile
*@throwsIOException
*/
publicstaticvoidbufCopyCharFile_UseArr(Fileold,FilenewFile)throwsIOException{
BufferedReaderbr=newBufferedReader(newFileReader(old));
BufferedWriterbw=newBufferedWriter(newFileWriter(newFile));
char[]buf=newchar[1024];
intlen=-1;
while((len=br.read(buf))!=-1){
bw.write(buf,0,len);
}
br.close();
bw.close();
}
}
主程序将各种方式复制文本的方法封装成不同类,这些类继承自GetComplexity以获得检测复杂度的功能。CharFileCopyDemo.java
packageio.charstream;
importjava.io.File;
importjava.io.IOException;
importtools.GetComplexity;
publicclassCharFileCopyDemo{
publicstaticvoidmain(String[]args){
Filesrc=newFile("temp\\singlelinechar.txt");
Filedest1=newFile("temp\\CopyCharFile\\char_copy.txt");
Filedest2=newFile("temp\\CopyCharFile\\char_copy_arr.txt");
Filedest3=newFile("temp\\CopyCharFile\\char_bufcopy.txt");
Filedest4=newFile("temp\\CopyCharFile\\char_bufcopy_line.txt");
Filedest5=newFile("temp\\CopyCharFile\\char_bufcopy_arr.txt");
newCopyCharFile("使用FileReader/Writer复制文本",src,dest1).start();
newCopyCharFile_UseArr("使用自定义缓冲区的FileReader/Writer复制文本",src,dest2).start();
newBufCopyCharFile("使用BufferedReader/Writer复制文本",src,dest3).start();
newBufCopyCharFile_UseLine("使用BufferedReader/Writer按行复制文本",src,dest4).start();
newBufCopyCharFile_UseArr("使用BufferedReader/Writer按数组复制文本",src,dest5).start();
}
}
classCopyCharFileextendsGetComplexity{
privateFilesrc=null;
privateFiledest=null;
publicCopyCharFile(Stringname,Filesrc,Filedest){
super(name);
this.src=src;
this.dest=dest;
}
@Override
publicvoidrun(){
try{
CharFileCopy.copyCharFile(src,dest);
}catch(IOExceptione){
e.printStackTrace();
}
}
}
classCopyCharFile_UseArrextendsGetComplexity{
privateFilesrc=null;
privateFiledest=null;
publicCopyCharFile_UseArr(Stringname,Filesrc,Filedest){
super(name);
this.src=src;
this.dest=dest;
}
@Override
publicvoidrun(){
try{
CharFileCopy.copyCharFile_UseArr(src,dest);
}catch(IOExceptione){
e.printStackTrace();
}
}
}
classBufCopyCharFileextendsGetComplexity{
privateFilesrc=null;
privateFiledest=null;
publicBufCopyCharFile(Stringname,Filesrc,Filedest){
super(name);
this.src=src;
this.dest=dest;
}
@Override
publicvoidrun(){
try{
CharFileCopy.bufCopyCharFile(src,dest);
}catch(IOExceptione){
e.printStackTrace();
}
}
}
classBufCopyCharFile_UseLineextendsGetComplexity{
privateFilesrc=null;
privateFiledest=null;
publicBufCopyCharFile_UseLine(Stringname,Filesrc,Filedest){
super(name);
this.src=src;
this.dest=dest;
}
@Override
publicvoidrun(){
try{
CharFileCopy.bufCopyCharFile_UseLine(src,dest);
}catch(IOExceptione){
e.printStackTrace();
}
}
}
classBufCopyCharFile_UseArrextendsGetComplexity{
privateFilesrc=null;
privateFiledest=null;
publicBufCopyCharFile_UseArr(Stringname,Filesrc,Filedest){
super(name);
this.src=src;
this.dest=dest;
}
@Override
publicvoidrun(){
try{
CharFileCopy.bufCopyCharFile_UseArr(src,dest);
}catch(IOExceptione){
e.printStackTrace();
}
}
}

运行结果

[align=left]使用FileReader/Writer复制文本:[/align][align=left]EstimatedTimeis1707.23ms.[/align][align=left]UsedMemoryis37464.54KB.[/align][align=left]使用自定义缓冲区的FileReader/Writer复制文本:[/align][align=left]EstimatedTimeis229.43ms.[/align][align=left]UsedMemoryis3247.94KB.[/align][align=left]使用BufferedReader/Writer复制文本:[/align][align=left]EstimatedTimeis950.88ms.[/align][align=left]UsedMemoryis1623.88KB.[/align][align=left]使用BufferedReader/Writer按行复制文本:[/align][align=left]EstimatedTimeis309.89ms.[/align][align=left]UsedMemoryis178302.42KB.[/align][align=left]使用BufferedReader/Writer按数组复制文本:[/align][align=left]EstimatedTimeis238.92ms.[/align][align=left]UsedMemoryis1077.44KB.[/align]

结论

不使用数组来复制文件将消耗较多时间。按行复制对于单行较长的文件将消耗较多空间。最优化的方案就是使用带缓冲功能并自定义数组进行复制,即双层缓冲。

2.获取文本文件中某字符串出现次数

packageio.charstream;
importjava.io.BufferedReader;
importjava.io.File;
importjava.io.FileReader;
importjava.io.IOException;
importtools.GetComplexity;
importtools.StringTools;
publicclassText_GetCount{
publicstaticvoidmain(String[]args){
newGetCountByBuffer("按自定义缓冲区查找字符串").start();
newGetCountByLine("按行查找字符串").start();
}
}
/**
*通过带缓冲字符流按行读取,获取文件中某字段出现次数
*继承自GetComplexity,使得可以获取该算法所消耗的时间和内存
*/
classGetCountByLineextendsGetComplexity{
publicGetCountByLine(){
super();
}
publicGetCountByLine(Stringname){
super(name);
}
publicvoidrun(){
try{
Filefile=newFile("temp\\singlelinechar.txt");
intcount=getCountByLine(file,"个人文档");
System.out.println("“个人文档”出现次数:"+count);
}catch(IOExceptione){
e.printStackTrace();
}
}
publicintgetCountByLine(Filefile,Stringtarget)throwsIOException{
BufferedReaderbr=newBufferedReader(newFileReader(file));
Stringline=null;
intsum=0;
//使用字符流循环获取每一行的字符串,并获取其中出现特定字段的次数,并累加
//由于可能出现文件中某行内容太多,所以该方法可能会出现占用内存过大的问题
while((line=br.readLine())!=null){
sum+=StringTools.getCount(line,target);
}
br.close();
returnsum;
}
}
/**
*通过字符流以及自定义缓冲区,获取文件中某字段出现次数
*由于该方法缓冲区大小是固定的,所以避免了某一行过长照成消耗内存过大的情况
*继承自GetComplexity,使得可以获取该算法所消耗的时间和内存
*/
classGetCountByBufferextendsGetComplexity{
publicGetCountByBuffer(){
super();
}
publicGetCountByBuffer(Stringname){
super(name);
}
//自定义缓冲区大小为1024*2字节
privatefinalintBUFFER_SIZE=1024;
publicvoidrun(){
Filefile=newFile("temp\\singlelinechar.txt");
try{
intcount=getCount(file,"个人文档");
System.out.println("“个人文档”出现次数:"+count);
}catch(IOExceptione){
e.printStackTrace();
}
}
publicintgetCount(Filefile,Stringtarget)throwsIOException{
FileReaderfr=newFileReader(file);
char[]buf=newchar[BUFFER_SIZE];
intlen=-1;
intsum=0;
//首先判断文件是否为空,是则返回,否则继续
if((len=fr.read(buf))==-1){
fr.close();
return0;
}
//第一次读取,将文件内容读取到缓冲区中,并累加特定字段出现的次数
sum+=StringTools.getCount(newString(buf,0,len),target);
//由于特定字段不止一个字符,可能出现特定字段被缓冲区截断的情况,以至于可能会漏记次数
//所以这里将最后可能被截断的字符串保留(长度最长为target.length()-1),复制到缓冲区最前面
System.arraycopy(buf,BUFFER_SIZE-target.length()+1,buf,0,target.length()-1);
//下一次读取将覆盖缓冲区剩余空间(长度为BUFFER_SIZE-target.length()+1),并循环判断累加次数。
while((len=fr.read(buf,target.length()-1,BUFFER_SIZE-target.length()+1))!=-1){
sum+=StringTools.getCount(newString(buf,0,len+target.length()-1),target);
System.arraycopy(buf,BUFFER_SIZE-target.length()+1,buf,0,target.length()-1);
}
fr.close();
returnsum;
}
}

运行结果

[align=left]“个人文档”出现次数:113496[/align][align=left]按自定义缓冲区查找字符串:[/align][align=left]EstimatedTimeis157.69ms.[/align][align=left]UsedMemoryis5902.80KB.[/align][align=left]“个人文档”出现次数:113496[/align][align=left]按行查找字符串:[/align][align=left]EstimatedTimeis188.58ms.[/align][align=left]UsedMemoryis178165.50KB.[/align]

3.自行设计BufferedReader类

packageio.charstream;
importjava.io.FileReader;
importjava.io.IOException;
importjava.io.Reader;
classMyBufferedReader{
privateReaderr;
privatestaticfinalintSIZE=1024;
//缓冲区指针
privateintpos=0;
//缓冲区字符数
privateintcount=0;
//缓冲区定义
privatechar[]buffer=newchar[SIZE];
publicMyBufferedReader(Readerr){
super();
this.r=r;
}
/**
*将数据从流读入缓冲区
*
*@throwsIOException
*/
privatevoidfill()throwsIOException{
count=r.read(buffer);
pos=0;
}
/**
*读取单个字符
*
*@return
*@throwsIOException
*/
publicintread()throwsIOException{
if(pos>=count){
fill();
}
if(count==-1){
return-1;
}
return(int)buffer[pos++];
}
/**
*读取一行数据,换行符为"\n"
*
*@return
*@throwsIOException
*/
publicStringreadLine()throwsIOException{
StringBuildersb=newStringBuilder();
intch;
while((ch=read())!=-1){
if(ch=='\r'){
continue;
}elseif(ch=='\n'){
returnsb.toString();
}else{
sb.append((char)ch);
}
}
if(sb.length()!=0){
returnsb.toString();
}else{
returnnull;
}
}
/**
*关闭流
*@throwsIOException
*/
publicvoidclose()throwsIOException{
r.close();
}
}
publicclassCharMyBufferedDemo{
publicstaticvoidmain(String[]args){
MyBufferedReadermbr=null;
Stringstr=null;
try{
mbr=newMyBufferedReader(newFileReader("temp\\char.txt"));
while((str=mbr.readLine())!=null){
System.out.println(str);
}
mbr.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: