IO流(三)之IO包其他功能流及字符编码详解
来源:互联网 发布:超星网络选修课答案 编辑:程序博客网 时间:2024/04/30 20:38
打印流(PrintStream和PrintWriter)
该流提供了打印方法,可以将各种数据类型的数据都原样打印
字节打印流:PrintStream
构造函数可以接受的参数类型
a) File对象 File
b) 字符串路径。String
c) 字节输出流。OutputStream
字符打印流:PrintWriter
构造函数可以接受的参数类型
a) File对象 File
b) 字符串路径。String
c) 字节输出流。OutputStream
d) 字符输出流.Writer
我们开发常用的是printWriter,那么printWriter有什么常用的方法呢?
a) 特殊构造方法:PrintWriter(OutputStream out, booleanautoFlush):如果为true,可以自动刷新println,printf,format
b) voidprintln(String s);打印
我们用这些方法来演示下PrintWriter的基本方法:
import java.io.*;
class PrintWriterDemo
{
publicstatic void main(String[] args) throws IOException
{
method();
}
publicstatic void method() throws IOException
{
BufferedReaderbr=
newBufferedReader(new InputStreamReader(System.in));
PrintWriterpw=new PrintWriter(new FileWriter("a.txt"),true);
Stringline=null;
while((line=br.readLine())!=null)
{
if("over".equals(line))
{
break;
}
pw.println(line.toUpperCase());//
//pw.flush();
}
br.close();
pw.close();
}
}
合并流(SequenceInputStream)
我们首先将一个mp3文件切割成一个个碎片,这里就涉及到IO流的应用,我们把切割代码演示如下:
importjava.io.*;
classSpilteDemo
{
public static void main(String[] args)throws IOException
{
spite();
}
public static void spite()throwsIOException
{
FileInputStream fis=newFileInputStream("1.mp3");
FileOutputStream fos=null;
byte[] buf=newbyte[1024*1024];
int len=0;
int count=1;
while((len=fis.read(buf))!=-1){
//每次循环我们都创建一个写入流,保存到递增的文件中
fos=newFileOutputStream("c://splitest//"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
}
fis.close();
}
}
然而我们有时需要将文件合并到一起,那么我们怎么办呢?我们这时就需要一个IO流的合并流
合并流(SequenceInputStream)即将多个流合并成一个流,即多个文件合并成一个文件
构造方法:SequenceInputStream(Enumeration<? extendsInputStream> e):接收一个Enumeration类型参数,需要用到集合
我们通过代码简单演示下如何将之前MP3文件合并在一起:
importjava.io.*;
importjava.util.*;
classSequenceInputStreamDemo
{
public static void main(String[] args)throws IOException
{
//只有Vector才有Enumeration
Vector<FileInputStream>v=new Vector<FileInputStream>();
v.add(new FileInputStream("c://splitest//1.part"));
v.add(newFileInputStream("c://splitest//2.part"));
v.add(newFileInputStream("c://splitest//3.part"));
v.add(newFileInputStream("c://splitest//4.part"));
v.add(newFileInputStream("c://splitest//5.part"));
v.add(newFileInputStream("c://splitest//6.part"));
v.add(newFileInputStream("c://splitest//7.part"));
v.add(newFileInputStream("c://splitest//8.part"));
Enumeration<FileInputStream>e=v.elements();
//创建一个合并流,且构造方法传入的必须是Enumeration类型
SequenceInputStream sis=newSequenceInputStream(e);
//将多个文件的数据传入到4.txt
BufferedOutputStream bw=
new BufferedOutputStream(newFileOutputStream("c://mp3//1.mp3"));
int len=0;
while((len=sis.read())!=-1){
bw.write(len);
}
sis.close();
bw.close();
}
}
对象流(对象序列化 ObjectStream)
对象流是可以将对象以序列化的方式存入到一个文件里,对象流有非常重要的两个类,该两个类是成对出现的。
ObjectOutputStream:将对象存储到一个文件中
存储方法:void writeObject(Object obj)
ObjectInputStream:操作输出对象流一保存的数据,通常和ObjectOutputStream成对使用。
读取方法:void readObject();
我们基本演示下将Person对象存入到文件中:
importjava.io.*;
classObjectDemo
{
public static void main(String[] args)throws Exception
{
writeObj();//要先写再读
readObj();
}
public static void readObj()throwsException
{
ObjectInputStream ois=newObjectInputStream(new FileInputStream("object.txt"));
Person p=(Person)ois.readObject();
System.out.println(p);
ois.close();
}
public static void writeObj()throwsIOException
{
ObjectOutputStream oos=newObjectOutputStream(new FileOutputStream("object.txt"));
oos.writeObject(newPerson("zhangsan",13));
oos.close();
}
}
classPerson implements Serializable//必须要实现这个接口,是对象序列化
{
String name;
int age;
Person(String name,int age)
{
this.age=age;
this.name=name;
}
public String toString()
{
return name+"::"+age;
}
}
注意:
a) 当我们该掉Person任何一个成员属性时,再读取就会出错,是因为java内部有个叫UID的长整型的编号将Person中的属性编号,无论改变成员变量值还访问权限,都会报错,读取失败,如果想改变而又不读取失败,可以加上这段代码:ANY-ACCESS-MODIFIERstatic final long serialVersionUID = 42L;红色的是任何访问修饰符。、
b) 我们想存储的对象的类必须要实现一个接口Serializable,是其对象序列化
c) 要存储的对象类中成员属性不能是static修饰的,这样无法序列化,当然我们想让成员变量无法序列化也可在其加上transient
管道流(PipedStream)
管道流是IO中一个非常特殊的流,他是和多线程联系在一起的。管道流输入输出是连接在一起的。
输入流:
PipedInputStream
输出流:
PipedOutputStream
管道流的输入输出连接是通过两种方法实现:
a) 构造一个无参数的构造方法,使用connect()方法去连接输入和输出
b) 构造一个有参数的构造方法,传入的是自己的输入或者输出流
我们基本演示下管道流的应用:
import java.io.*;
class Read implements Runnable
{
PipedInputStreampis;
Read(PipedInputStreampis){
this.pis=pis;
}
publicvoid run()
{
try
{
byte[]buf=new byte[1024];
System.out.println("没有数据,线程没有阻塞");
intlen=pis.read(buf);
System.out.println("读取数据中。。阻塞结束");
Stringstr=new String(buf,0,len);
System.out.println(str);//打印读取到的数据
pis.close();
}
catch(Exception e )
{
thrownew RuntimeException("管道流输入失败");
}
}
}
class Write implements Runnable
{
PipedOutputStreampos;
Write(PipedOutputStreampos){
this.pos=pos;
}
publicvoid run()
{
try{
System.out.println("开始写入数据,等待6秒后");
Thread.sleep(6000);
pos.write("haha,pioedis come".getBytes());
pos.close();
}catch(Exceptione){
thrownew RuntimeException("管道流输出失败");
}
}
}
class PipedIODemo
{
publicstatic void main(String[] args) throws Exception
{
PipedInputStreampis=new PipedInputStream();
PipedOutputStreampos=new PipedOutputStream();
pis.connect(pos);//连接输入和输出
newThread(new Read(pis)).start();//开启读取线程
newThread(new Write(pos)).start();//开启写入线程
}
}
随机访问流(RandomAccessFile)
该类是随机访问文件的一个类,该类不能算是IO体系中子类,而是直接继承自Object,但是它是IO包中的成员,因为他具备读和写功能。其内部封装了一个数组,而且通过指针对数组的元素进行操作。我们可以通过getFIlePointer获取指针位置。同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。但它有一个局限性就是通过构造方法可以看出,该类只能操作文件,而且操作文件还有模式。
模式:
a) r:以只读方式打开
b) rw:打开以便读取和写入
c) rws:打开以便读取和写入
d) rwd:打开以便读取和写入
如果模式为只读r,不会创建文件,会去读取一个已存在文件,如果该文件不存在,则会出现文件找不到(FileNotFoundException)异常。
如果模式为rw,操作的文件不存在,会自动创建,如果存在,不会覆盖。
而且该对象的构造函数要操作的文件不存在,会自动创建。如果存在,不会覆盖。
我们基本演示下该类的用法:
import java.io.*;
class RandomAccessFileDemo
{
publicstatic void main(String[] args) throws IOException
{
writeFile_2();
//readFile();
}
publicstatic void readFile()throws IOException
{
RandomAccessFileraf=new RandomAccessFile("ran.txt","r");
byte[]buf=new byte[4];
//调整对象中的指针
//raf.seek(8);
//跳过指定的字节数
raf.skipBytes(8);
raf.read(buf);
Stringname=new String(buf);
System.out.println("name::"+name);
intage=raf.readInt();
System.out.println("age::"+age);
}
publicstatic void writeFile()throws IOException
{
RandomAccessFileraf=new RandomAccessFile("ran.txt","rw");
raf.write("张三".getBytes());
//写入Int类型,以免超出无法保存,一般开发够用了
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
/*
写入文件数据的随机性
*/
publicstatic void writeFile_2()throws IOException
{
RandomAccessFileraf=new RandomAccessFile("ran.txt","rw");
//可以随机控制要写入的字节位置
raf.seek(8*0);
raf.write("周期".getBytes());
raf.writeInt(101);
raf.close();
}
}
总结:该类三种特殊的地方:首先它有有一个模式控制文件的读写操作,然后它有一个seek方法控制写入字节的位置,最后他的写入读取有多种基本类型,我们常用的是writeInt方法。该类经常用在文件的下载,我们要重点掌握。
基本数据类型流(DataStream)
其用于操作基本数据类型的流
输入流:
DataInputStream
输出流
DataOutputStream
我们通过代码演示,基本的了解下DataStream的用法:
importjava.io.*;
classDataStreamDemo
{
public static void main(String[] args)throws IOException
{
//writeData();
readData();
}
public static voidwriteData()throws IOException
{
DataOutputStream dos=newDataOutputStream(new FileOutputStream("data.txt"));
//我们按照什么顺序写入什么基本类型数据
dos.writeInt(289);
dos.writeBoolean(true);
dos.writeDouble(988.26);
dos.close();
}
public static void readData()throwsIOException
{
DataInputStream dis=newDataInputStream(new FileInputStream("data.txt"));
//我们就按照什么顺序读取什么基本类型。必须按顺序,否则取出来乱码
int num=dis.readInt();
Boolean b=dis.readBoolean();
Double d=dis.readDouble();
System.out.println(num);
System.out.println(b);
System.out.println(d);
}
}
注意这两个类个分别有特殊的方法是专门用来输入和读取UTF的方法,我们只能对应的去取文件的数据。这两个方法分别是readUTF(),writeUTF()。我们演示下这两个方法的用法:
importjava.io.*;
classDataStreamDemo
{
public static void main(String[] args)throws IOException
{
//writeUTFData();
readUTFData();
}
public static void writeUTFData()throwsIOException
{
DataOutputStream dos=newDataOutputStream(new FileOutputStream("utfdata.txt"));
dos.writeUTF("你好");//writeUTF只能解析中文
dos.close();
}
public static void readUTFData()throwsIOException
{
DataInputStream dis=newDataInputStream(new FileInputStream("utfdata.txt"));
String s=dis.readUTF();
System.out.println(s);
}
}
基本数据类型数组流(ByteArrayStream)
ByteArrayStream是用来操作字节数组的流对象
ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组。
构造方法:ByteArrayInputStream(byte[]buf)
ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地、
构造方法:ByteArrayOutputStream()
常用的方法:
a) int size:返回缓冲区的当前数组大小
b) String toString:将缓冲区内容转换为字符串
c) void writeTo(OutputStream out):将byte数组全部内容存到指定输入流文件中,这段代码需要抛IOException。
因为是两个流对象都操作的数组,并没有使用系统资源,所以不用进行close关闭,且没有涉及到文件或者键盘等操作,所以不用抛IOException。
这个类就是用流的读写思想操作数组
我们来通过代码基本演示下这个流:
import java.io.*;
class ArrayStreamDemo
{
publicstatic void main(String[] args)
{
//数据源
ByteArrayInputStreambais=new ByteArrayInputStream("ABCDEFG".getBytes());
//数据目的
ByteArrayOutputStreambaos=new ByteArrayOutputStream();
inta=0;
while((a=bais.read())!=-1)
{
baos.write(a);
}
System.out.println(baos.size());//缓冲区的大小
System.out.println(baos.toString());//唯一取出数组的方法
}
}
总结:我们不仅有操作字节数组的,还有其他的基本类型数组操作,他们基本用法是一致的:
操作字符数组
CharArrayReader与CharArrayWrite
操作字符串
StringReader与StringWriter
字符编码
我们常用的编码表是UTF—8和GBK,UTF—-8是国际码表,一个汉字占三个字节,GBk是中国的码表,一个汉字占两个字节。
字节转换字符只能用IO流两个类
a) InputStreamReader和outputStreamWriter
b) PrintStream和PrintWriter这是打印流,不涉及读取。
我们通过以下代码,测试下UTF-8和GBK之间的区别
import java.io.*;
class UnicodeDemo
{
publicstatic void main(String[] args) throws IOException
{
//method_write();
method_read();
}
publicstatic void method_read()throws IOException
{
InputStreamReaderisr=new InputStreamReader(newFileInputStream("gbk.txt"),"utf-8");
char[]buf=new char[10];
intlen=isr.read(buf);
Strings=new String(buf,0,len);
System.out.println(s);
}
publicstatic void method_write()throws IOException
{
OutputStreamWriterosw=
newOutputStreamWriter(newFileOutputStream("utf.txt"),"utf-8");//(gbk.txt),"gbk"
osw.write("你好");
osw.close();
}
}
我们通过代码打印“你好“分析
a) 创建的两个文件,看到UTF-8占6个字节,GBK占三个字节
b) 不同编码方式读取文件给的结果不一样,当拿GBK的字符用UTF—8解析,打印为??
当拿UTF-8的字节用GBK解析,打印为浣犲ソ
字符的编码解码
编码:字符串变成字节数组
字符串编程字节数组,即:String--àbyte[];使用str.getBytes(String charsetName);方法
解码:字节数组变成字符串
字节数组变成字符串,即byte[]--àString;使用new String(byte[] b,String charsetName)
我们通过代码来看看代码的编码解码:
importjava.util.*;
classEncodeDemo
{
public static void main(String[] args) throwsException
{
String s="你好";
//使用utf-8编码
byte[] b=s.getBytes("utf-8");
//使用此方法可以将数组换成字符串打印
System.out.println(Arrays.toString(b));
String str=new String(b,"utf-8");
System.out.println("str="+str);
}
}
当我们使用Iso8859-1解码的时候,打印会出现乱码,我们可以再编码再解码,就可以打印正确的中文。这其实就是Tomcat的一种乱码的解决方式,它默认的就是iso8859-1。注意我们不能用UTF-8这样去做。
以下是乱码解决演示:
importjava.util.*;
classEncodeDemo
{
public static void main(String[] args) throwsException
{
String s="你好";
//使用gbk编码
byte[] b=s.getBytes("gbk");
//使用iso8859-1解码,出现乱码
String str=newString(b,"iso8859-1");
System.out.println("str="+str);
//一旦遇到乱码,我们可以再用ISO8859-1编码,再用gbk解码
byte[] b2=str.getBytes("iso8859-1");
String s2=newString(b2,"gbk");
System.out.println("s2="+s2);
}
}
特殊字:联通,当我们用windows记事本写上联通,保存后再打开就会出现乱码。这是什么原因呢?这其实跟UTF-8解析内部有关,当遇到0,或者110和10或者1110和10这三种,记事本会默认使用utf-8去打开,而联通这个字刚好是110和10开头,所以系统默认用utf-8解析,出来就成了乱码。我们使用代码来看看联通这个字的二进制。
importjava.util.*;
classUnicodeDemo2
{
public static void main(String[] args) throwsException
{
String s="联通";
byte[]by=s.getBytes("gbk");
for (byte b:by)
{
System.out.println(Integer.toBinaryString(b&255));
}
}
}
我们看到打印结果是:110000011010101011001101 10101000
根据结果,我们就知道了,原来系统把联通使用了utf-8去使用了。
- IO流(三)之IO包其他功能流及字符编码详解
- IO流(4)io包其他功能流对象
- Java基础:IO包中的其他类及字符编码
- IO流4(IO中的其他流、编码)
- IO(三)字符流
- IO包中的其他类2和字符编码
- java的IO流之字符编码
- IO流(四)--IO包中的其他类
- 其他对象和IO流(三)
- 黑马程序员--IO(File类、Properties、IO中其他的一些常用流、字符编码)
- IO流简记+ 字符编码
- java-IO流-字符编码
- IO流之编码
- IO之字符流
- IO之字符流
- IO之字符流
- IO流(三)字符流
- 字符流---IO学习笔记(三)
- sed 高级用法
- DNS基础及域名系统架构
- vfs dcache函数
- HTML5 的Drawing Path
- shell编程-循环语句
- IO流(三)之IO包其他功能流及字符编码详解
- EXTjS4自定义控件
- 推荐系统
- 如何复制百度文库中的文章
- C++虚函数表解析
- 如何查看和修改操作系统字符集
- POJ 2991 Crane(成段更新)
- ASCII码表完整版
- git 提交与基本问题的解决方法