---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
IO流分为两种:
字节流:传输多媒体文件用字节流,单位是字节。字节流是通用的。
字符流:传输文字字符文件用字符流,里面对应的是各种字符码表来进行翻译。字符流基于字节流。
字节流抽象基类:
InputStream:读
OutputStream:写
字符流抽象基类:
Reader:读
Writer:写
FileWriter:
创建一个FileWriter对象。该对象一被初始化就必须明确被操作的文件。而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。如果需要对已有文件的末尾处进行续写,要用FileWriter fw=new FileWriter("文件",true);这里true表示,进行续写,不覆盖。内容中\n表示换行。但是txt文档中需要\n\r才能换行。
FileWriter fw=new FileWriter("demo.txt");//其实该步就是明确数据要存放的目的地。
fw.write("abcde");//write()方法把字符串写入流中,还没有写进入文件中。
fw.flush();//刷新流对象的缓冲数据,将数据刷到目的文件中
fw.close();//关闭这个流资源。再用需要重新new一个。但是这次关闭,会自动把流中还没有来得及刷新出去的数据给刷新到目的文件中。
FieldReader
FileReader fr=new FileReader("demo.txt");
第一种方式:单个读取字符
//调用读取流对象的read方法。
int ch=fr.read();//每次读取一个字符。而且会自动向下读,读到文件末尾返回-1。返回是int类型的ASCII值.可以利用这个-1作为条件用循环读取文件。
第二种方式:通过字符数组进行读取。
char[]buf=new c har[3];
int num=fr.read(buf);
这里read方法将字符读入buf数组。如果数组长度不够,就能装入几个装几个。当再次执行read时,若是同一数组,则继续向后读字符串,将数组从头覆盖。如果流中字符串读完,将不再每次read,返回int类型值为数组中新进入元素的个数。如果存入数组,返回-1.可以以此-1作为循环条件。buf数组大小一般在定义的时候会定义为1024的整数倍,一个字符占两个字节,1024个字符大小就是2k。
用FileWriter FileReader进行文件的复制。‘两种方式,一种单个字符写,一种用数组。
public class CopyDemo {
public static void main(String[]args) throws IOException{
copy_2();
}
public static void copy_1() throws IOException{
//创建目的地
FileWriter fw=new FileWriter("E:\\Demo_copy.txt");
//与已有文件关联
FileReader fr=new FileReader("demo.txt");
int ch=0;
while((ch=fr.read())!=-1){
fw.write((char)ch);
}
fw.close();
fr.close();
}
public static void copy_2(){
FileWriter fw=null;
FileReader fr=null;
try{
fw=new FileWriter("E:\\Demo_copy.txt");
fr=new FileReader("demo.txt");
char[] ch=new char[1024];
int num=0;
while((num=fr.read(ch))!=-1){
fw.write(ch, 0, num);
}
}
catch(IOException e){
throw new RuntimeException("读写失败");
}
finally{
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
缓冲区的出现是为了提高流操作效率而出现的。创建缓冲区之前,必须先有流对象BufferedWriter。
BufferedWriter bufw=new BufferedWriter(fw);//将流对象作为参数传递给缓冲区的构造函数即可。
记住:只要用到缓冲区就要记得刷新,bufw.flush()
缓冲区中提供了一个跨平台的换行符,bufw.newLine();效果等同于windows系统下的输入\r\n,linux下的\r。
关闭缓冲区,bufw.close() 直接关闭了缓冲区和流对象,所以没有必要再关闭流对象了。
BufferedReader bufr=new BufferedReader(fr);//创建读取流缓冲区
String line=null;
while((line=bufr.readLine())!=null)//BufferedReader的特有方法readLine()返回是String类型的流文件中的一行读到流文件结尾处,返回null
{
...
}
bufr.close();
利用缓冲区进行文件的复制
public class BufferCopyDemo {
public static void main(String[]args){
BufferedReader bufr=null;
BufferedWriter bufw=null;
try {
bufr=new BufferedReader(new FileReader("D:\\workspace\\exam\\src\\com\\itheima\\BufferCopyDemo.java"));
bufw=new BufferedWriter(new FileWriter("E:\\copy_BufferedCopyDemo.txt"));
String s=null;
while((s=bufr.readLine())!=null){
bufw.write(s);
bufw.flush();//记得写一次就刷新一次,免得出现停电这种情况,缓冲区中的数据就都没了
bufw.newLine();
}
} catch (IOException e) {
throw new RuntimeException("读写失败");
}finally{
if(bufr!=null){
try {
bufr.close();
} catch (IOException e) {
throw new RuntimeException("读取失败");
}
try {
bufw.close();
} catch (IOException e) {
throw new RuntimeException("写入失败");
}
}
}
}
}
字节流:
字节流中write方法中字符串需要用.getBytes()方法处理一下,因为字节流中操作的参数必须都是Byte类型的。
字节流write后,不需要刷新缓冲区。原因:其实字符流的底层也是字节流,但是字符流中处理每个字符都需要读取2个字节,所以需要在流中做缓冲处理,再都刷新出来。而字节流是来一个字节就写出去,不需要进行缓存处理了。
FileInputStream fis=new FileInputStream("...");//一个读流
byte[]buf=new byte[fis.available()];//定义一个刚好的缓冲区,不用再数组循环了。available()方法是获得文件
fis.read(buf); //字节个数,一个回车是2个字节\r\n。此方法慎用,因为文件太大将内存溢出
System.out.println(new String(buf));
fis.close;
关于复制MP3文件中read方法中的与运算原因。
/*拷贝一个mp3文件
* */
public class Copymp3 {
static FileInputStream fis=null;
static FileOutputStream fos=null;
static BufferedInputStream bis=null;
static BufferedOutputStream bos = null;
public static void main(String[]args){
long start=System.currentTimeMillis();
copy1();
long end=System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
long start1=System.currentTimeMillis();
copy2();
long end1=System.currentTimeMillis();
System.out.println((end1-start1)+"毫秒");
}
//通过字节流缓冲区完成复制
public static void copy1(){
try {
fis=new FileInputStream("e:\\Again.mp3");
fos=new FileOutputStream("e:\\Again2.mp3");
bis=new BufferedInputStream(fis);
bos=new BufferedOutputStream(fos);
int by=0;
while((by=bis.read())!=-1){
bos.write(by);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(bos!=null)
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
try {
if(bis!=null)
bis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
}
public static void copy2(){//通过自制的字节流缓冲区完成复制
MyBufferedInputStream mybis=null;
BufferedOutputStream mybos=null;
try {
fis=new FileInputStream("e:\\Again.mp3");
fos=new FileOutputStream("e:\\Again3.mp3");
mybis=new MyBufferedInputStream(fis);
bos=new BufferedOutputStream(fos);
int len=0;
while((len=mybis.myRead())!=-1){
bos.write(len);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
try {
if(bos!=null)
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
try {
if(mybis!=null)
mybis.myClose();
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
}
}
public class MyBufferedInputStream {//自定义的缓冲字节流
private InputStream in;
private byte[] buf=new byte[1024*4];
private int pos=0,count=0;//count表示buf数组中还没有被读到的已存的字节数
MyBufferedInputStream(InputStream in){
this.in=in;
}
//一次读一个字节,从缓冲区(字节数组中获取)
public int myRead() throws IOException{
//通过in对象读取硬盘上的数据,并存储到buf中。
if(count==0){
count=in.read(buf);
if(count<0) return -1;//流中没有数据了
pos=0;//每次从硬盘向数组缓存数据数组的指针都要归0
byte b=buf[pos];
count--;
pos++;
return b&255;//b与11111111做与运算,转变为32位的int类型,其中前面24位是0。
}
else if(count>0){
byte b=buf[pos];
count--;
pos++;
return b&0xff;//b与11111111做与运算,转变为32位的int类型,其中前面24位是0
}else return -1;
}
public void myClose() throws IOException{
in.close();
}
}
mp3文件本身就是一个字节流的01代码文件,其开头处很可能是连续8个1,十进制值为-1。read方法读取的是char类型值,返回的却是int类型的值。当read方法中一遇到这8个1,就收录,再类型转换为int型,即前面都补1到32位。这时这32个连续的1十进制值还是-1,read方法返回这个-1,程序判断是文件流扫描到了末尾,就不再循环,流关闭。那么在处理这个问题时,我们,必须想办法把32个1前面的补的这24个1都变为0,那么就要把这32个1和255做与运算。与后出来的int类型十进制为255而不再是-1.
同时可以推断出来,write方法中接收4字节的int类型,执行时把int类型数据中前面的24个bit都砍掉,只留后面的8个bit。等于是将数据又从int类型转换为了byte。详情见Copymp3.java,MyBufferedInputStream.java。
读取键盘录入:
System.out:对应的是标准的输出设备,控制台。System.in:对应的标准输入设备,键盘。字节读取流对象。
转换流:字节流对象转化为字符流
/*通过键盘录入数据,当录入一行数据后,就将该行数据进行打印,如果录入数据是over,那么停止录入*/
public class TransStreamDemo {
static InputStream in=System.in;//声明一个键盘录入对象
static OutputStream out=System.out;//声明一个屏幕输出对象,不用输出语句
public static void main(String[]args) throws IOException{
System.out.println("start:");
/*int b=in.read();
int b1=in.read();
int b2=in.read();
int b3=in.read();
System.out.print(b);
System.out.print(b1);
System.out.print(b2);
System.out.print(b3);*/
/*
int ch=0;
while((ch=in.read())!=-1){
System.out.println(ch);
}*/
//A();
//B();
C();
}
//下面其实也就是字符流BufferedReader中readLine()方法的原理。
public static void A() throws IOException{
StringBuilder sb=new StringBuilder();
while(true){
int ch=in.read();
if(ch=='\r')
continue;
if(ch=='\n'){
String s=sb.toString();
if("over".equals(s))break;
System.out.println(s.toUpperCase());
sb.delete(0, sb.length());
}else
sb.append((char)ch);
}
}
//readLine方法是字符流BufferdeReader类中的方法,而键盘录入的read方法是字节流InputStream的方法。
//需要把字节流转换为字符流,就可以调用readLine方法了
public static void B() throws IOException{
//将字节流对象转成字符流对象,使用转换流。InputStreamReader,它本身是字符流
InputStreamReader isr=new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReader bufr=new BufferedReader(isr);
String s=null;
while((s=bufr.readLine())!=null){
if(s.equals("over"))break;
System.out.println(s.toUpperCase());
}
bufr.close();
}
//字符流转换为字节流
public static void C() throws IOException{
//将字节流对象转成字符流对象,使用转换流InputStreamReader,它本身是字符流
InputStreamReader isr=new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReader bufr=new BufferedReader(isr);
//以上两句可以用一句表达。太残暴了。这句一定要几下来,键盘录入的标准形式!!!
//键盘录入字节流对象->键盘录入字符流对象->键盘录入字符流缓冲区对象
//BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
//将字节输出流对象转换为字符输出流对象,记得字符输出流需要flush()
OutputStreamWriter osw=new OutputStreamWriter(out);
//将字符输出流对象包装为字符输出流缓冲区
BufferedWriter bufw=new BufferedWriter(osw);
//以上两句可以用一句表达.太残暴
//屏幕输出字符流缓冲区对象->屏幕输出字符流对象->屏幕输出字节流对象
//BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
//同样的,输出目的地不是屏幕而是一个文件的话
//BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("文件")));
String s=null;
while((s=bufr.readLine())!=null){
if("over".equals(s))break;
bufw.write(s.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
}
}
上面例子显示了转换流的高效和方便。
流操作的基本规律:
最痛苦的就是流对象太多,不知道用哪一个。
通过三个明确来完成。
1 明确源和目的。
源:输入流。InputStream Reader
目的:输出流 OutputStream Writer
2 操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3 当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
如:将一个文本文件中数据存储到另一个文件中,即复制文件。
源:因为是源 所以使用读取流InputStream Reader
是不是操作文本文件。
是:选择Reader
这样体系就明确了。
接下来明确要使用该体系中的哪个对象。
明确设备:硬盘,即一个文件。
Reader体系中可以操作文件的对象是FileReader
是否需要提高效率:是,加入Reader体系中的缓冲区BufferedReader
FileReader fr=new FileReader("a.txt");
BufferedReader bufr=new BufferedReader(fr);
目的:OutputStream Writer。
是否纯文本
是:Writer
设备:硬盘,即一个文件。
Writer体系中可以操作文件的对象FileWriter
FileWriter fw=new FileWriter("b.txt");
BufferedWriter bufw=new BufferedWriter(fw);
扩展:想要把录入的数据按照指定的编码表(utf-8),将数据存入文件。
目的:OutputStream Writer
是否纯文本 是 Writer。因为牵扯到各种编码、中文,不能用简单的字节流了。
设备:硬盘。一个文件,使用FileWriter。
但是FileWriter是使用的默认编码GBK。
需要指定utf-8的话,只能用转换流,因为它有这个指定编码表的方法。
确定使用OutputStreamWriter.
而该转换流对象要接受一个字节输出流,且这个流可以操作文件,那么就是FileOutputStream
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("文件"),"UTF-8");
需要高效吗?需要
BufferedWriter bufw=new BufferedWriter(osw);
所以,转换流什么时候使用。
字符和字节之间的桥梁,一般,涉及到字符编码转换时需要用到转换流。
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------