1 简介
Java的输入输出流使用统一的类库访问方法,解决了程序和外设(如文件、网络、屏幕、键盘等)间的数据交换问题。
这些流类可以按照不同的标准分为不同的类集:
如果按照处理数据信息的方向,可以分为输入流和输出流。所谓的输入和输出是相对于程序而言的,也就是说,输入流是指从外部向程序输入数据信息,输入流是指从程序向外部输出数据信息。
如果按照处理的数据信息类型,主要分为两部分,一为字节流类,二为字符流类。其中字节流类的基类是InputStream与OutputStream,它们都是抽象类,用于二进制信息的处理;字符流类的基类是Reader与Writer,也都是抽象类,用于对16位字符的处理提供简单的做法。
如果按照类本身的功能是否独立可以分为数据流类和处理流类,前者处理真实的数据信息(如字符串、文件、管道等),而后者处理诸如缓冲或者字符编码等,只能通过操纵数据流类来处理数据。
2、文件输入和输出
文件输入和输出仅仅是众多流处理功能的一个,当然也是比较重要的一个
2、1 基于字节流的文件处理
2、1、1 文件字节输入流
基类为InputStream,在创建时自动打开数据流,利用close()显式关闭。如果没有正常关闭或者flush,文件信息将会丢失。
常用的方法有:
int read():从输入流中读入一个字节,并且返回0到255之间的整数。如果到达流尾,则返回为-1。
long skip(long):指定跳过多少字节,返回实际跳过的字节数量(有时因为提前到达文件尾从而实际跳过的字节数小于指定的数值)。如果为负,则没有跳过任何字节。
下面的程序演示了文件长度的输出:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileInputStream fis=new FileInputStream("Hello.txt");
System.out.println(fis.available());
}
}
也可以使用File类对象获取文件长度,如:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
File f=new File("Hello.txt");
System.out.println(f.length());
}
}
当然,这两种方式并不一样,如下面的例子显示了available和length()的区别:
import java.io.*;
import java.util.*;
public class exec
{
public static void main(String[] args)throws IOException
{
byte[] bt=new byte[4];
File f=new File("Hello.txt");
System.out.println(f.length());
FileInputStream fio = new FileInputStream(f);
int i=fio.read(bt);
String temp=new String(bt);
System.out.println(fio.available());
System.out.println(temp);
fio.close();
}
}
说明:available实际含义为文件可以访问但还尚未访问的字节数,由于刚打开文件,所以和文件长度相等,而如果进行了一些文件访问,这个值就会变化,所以真正求文件长度的方法是应该使用File类
下例是显示文件全部内容:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileInputStream fis=new FileInputStream("Hello.txt");
byte[] barray=new byte[fis.available()];
fis.read(barray);
String str=new String(barray);
System.out.println(str);
}
}
注意:read方法只能一次一个字节的读取,所以必须通过字节数组来接受文件信息,并且这个数组应该和文件一样长
2、1、2 文件字节输出流
基类为OutputStream,在创建时自动打开数据流,利用close()显式关闭。常用的方法有:
void write(int):将指定字节写入输出流,高于八比特的位将会被忽略
void flush():强制写出任何缓冲中的输出字节
如下面的程序将字符‘0’写入文件:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileOutputStream fos=new FileOutputStream("hello.txt",true);
fos.write(0x30);
fos.close();
}
}
注意:
1)如果文件不存在,FileOutputStream会创建文件,并将数据写入文件
2)FileOutputStream构造函数中的true表示追加方式,否则为替换方式
由于此类只支持字节的输出,所以对于其他类型的数据只有在转换为字节后才能向文件输出,如下例将一个字符串写入文件
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileOutputStream fos=new FileOutputStream("hello.txt",true);
fos.write("Hello".getBytes());
fos.close();
}
}
2、2 基于字符流的文件处理
2、2、1 文件字符输入流
基类为Reader,面向Unicode字符,能够读取任何字符从而实现了国际化。和InputStream和OutputStream一样,在创建时自动打开数据流,利用close()显式关闭
如使用字符方式读取文件的一个字符:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("Hello.txt");
System.out.println((char)fr.read());
}
}
使用字符方式读取文件全部信息的例子:
import java.io.*;
import java.util.*;
public class exec
{
public static void main(String[] args)throws IOException
{
File file = new File("Hello.txt");
int size = (int)file.length();
FileReader in = new FileReader(file);
char[] data = new char[size];
in.read(data, 0, size);
System.out.println(new String(data));
in.close();
}
}
2、2、2 文件字符输入流
基类为Writer,面向Unicode字符,能够写入任何字符从而实现了国际化。和Read、InputStream和OutputStream一样,在创建时自动打开数据流,利用close()显式关闭
如下面的程序向文件输出字符串:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileWriter fw=new FileWriter("Hello.txt");
fw.write("hello!");
fw.close();
}
}
2、3 在基于字节的流类和基于字符的流类进行转换
下面考虑一个程序,可以从键盘输入信息,以回车结束,保存到文件,再读取显示在屏幕上:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
byte[] inarray=new byte[10];
int index=0;
while(true)
{
int ch=System.in.read();
if(ch==13)
break;
else
{
inarray[index]=(byte)ch;
index++;
}
}
FileOutputStream fos=new FileOutputStream("hello.txt");
for(int i=0;i<index;i++)
fos.write(inarray);
fos.close();
File f=new File("Hello.txt");
FileInputStream fis=new FileInputStream("Hello.txt");
for(index=0;index<f.length();index++)
System.out.print((char)fis.read());
}
}
注意:这个程序存在两个问题,一是代码相当繁琐,二是对于输入的中文信息无法正确显示在屏幕上。此时可以考虑使用使用基于字符的处理方式,但是很多情况下,我们只能获得基于字节的流对象,这时就可以使用InputStreamReader将基于字节的输入流转换为基于字符的输入流,使用OutputStreamWriter将基于字节的输出流转换为基于字符的输出流,同时这些类还支持字符集的转换
上述代码修改为:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
InputStreamReader isr=new InputStreamReader(System.in,"GBK");
char[] inarray=new char[1000];
isr.read(inarray);
FileOutputStream fos=new FileOutputStream("hello.txt");
OutputStreamWriter osw=new OutputStreamWriter(fos,"GBK");
for(int i=0;i<inarray.length;i++)
osw.write(inarray);
osw.close();
osw=new OutputStreamWriter(System.out,"GBK");
for(int i=0;i<inarray.length;i++)
osw.write(inarray);
osw.close();
}
}
2、4 功能流类的使用
单纯使用上述流类并不有时十分方便,如上述的流类,只能按照逐字节或者整个字节数组进行输入,有时需要按照按行输入(以回车键为结束标记)就不是非常方便了。一些常用的功能流类可以解决这些问题
2、4、1 缓冲流类
包含BufferedReader和BufferedWriter,提供了很多很好用的方法,如newLine()和readLine()方法等
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileOutputStream fos=new FileOutputStream("Hello.txt");
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos));
bw.write("Hello!");
bw.newLine();
bw.write("Bye!");
bw.close();
FileInputStream fis=new FileInputStream("Hello.txt");
BufferedReader br=new BufferedReader(new InputStreamReader(fis));
System.out.println(br.readLine());
}
}
利用这些方法可以方便的实现文本文件的拷贝,如:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileInputStream fis=new FileInputStream("Hello.txt");
BufferedReader br=new BufferedReader(new InputStreamReader(fis));
FileOutputStream fos=new FileOutputStream("Hello.bak");
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos));
while(true)
{
String line=br.readLine();
if(line==null)
break;
else
{
bw.write(line+"\n");
}
}
br.close();
bw.close();
}
}
2、4、2 PrintWriter
包含一些使生成格式化输出变得更简单的方法,如println()方法等
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileOutputStream fos=new FileOutputStream("Hello.txt");
PrintWriter pw=new PrintWriter(fos);
pw.println("Hello!"+1);
pw.println(true);
pw.close();
}
}
3、流类在异种设备上的使用
不同的硬件设备具有不同的输入和输出方式,然而使用流类可以将具体的操作细节屏蔽掉,通过流类对象可以使用一种统一的方式来对这些设备进行访问
如下面的程序能够读入一个文件中的一行,存放在另一个文件中:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
FileInputStream fis=new FileInputStream("Hello.txt");
BufferedReader br=new BufferedReader(new InputStreamReader(fis));
FileOutputStream fos=new FileOutputStream("Hello.bak");
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos));
String line=br.readLine();
bw.write(line+"\n");
br.close();
bw.close();
}
}
再如下面这个相似的程序能够从键盘读入一行,存放在另一个文件中:
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in,"GBK"));
FileOutputStream fos=new FileOutputStream("Hello.bak");
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos));
String line=br.readLine();
bw.write(line+"\n");
br.close();
bw.close();
}
}
甚至在网络上的通信中,也有相似的代码,如下面的程序片段能够获取网络服务器传来的一行文本信息:
BufferedReader is=new BufferedReader( new InputStreamReader(socket.getInputStream()));
String readline;
readline=sin.readLine();
4 File类
提供定位本地文件系统、描述文件和目录的功能,其中的诸多方法相当有用。
import java.io.*;
import java.util.*;
public class exec
{
public static void main(String[] args)throws IOException
{
File fl=new File("files","Hello.txt");
System.out.println(fl.getName());
System.out.println(fl.getPath());
System.out.println(fl.getAbsolutePath());
System.out.println(new Date(fl.lastModified()));
System.out.println(fl.exists());
System.out.println(fl.isDirectory());
System.out.println(fl.isFile());
if(fl.canRead())
System.out.println("Can Read!");
else
System.out.println("Can not Read!");
if(fl.canWrite())
System.out.println("Can Write!");
else
System.out.println("Can not Write!");
}
}
上述程序在没有Hello.txt文件的时候,输出为
Hello.txt
files\Hello.txt
C:\Documents and Settings\Administrator\桌面\共享资源\files\Hello.txt
Thu Jan 01 08:00:00 CST 1970
false
false
false
Can not Read!
Can not Write!
上述程序在有Hello.txt文件的时候,输出为
Hello.txt
files\Hello.txt
C:\Documents and Settings\Administrator\桌面\共享资源\files\Hello.txt
Sat Sep 17 07:59:22 CST 2005
true
false
true
Can Read!
Can Write!
上述程序在有Hello.txt文件并且为只读属性的时候,输出为
Hello.txt
files\Hello.txt
C:\Documents and Settings\Administrator\桌面\共享资源\files\Hello.txt
Sat Sep 17 07:59:22 CST 2005
true
false
true
Can Read!
Can not Write!
通过File类可以遍历目录文件
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
File f=new File(System.getProperty("user.dir"));
File files[]=f.listFiles();
for(int i=0;i<files.length;i++)
{
System.out.print(files.getName());
if(files.isDirectory())
System.out.println("\t<dir>");
else
System.out.println("\t"+files.length());
}
}
}
进一步遍历下级子目录中的文件
import java.io.*;
public class exec
{
public static void main(String[] args)throws IOException
{
seeIt(System.getProperty("user.dir"));
}
public static void seeIt(String str)
{
File f=new File(str);
File files[]=f.listFiles();
for(int i=0;i<files.length;i++)
{
System.out.print(files.getName());
if(files.isDirectory())
{
System.out.println("\t<dir>");
seeIt(files.getPath());
}
else
System.out.println("\t"+files.length());
}
}
}
[此贴子已经被作者于2010-12-12 08:04:00编辑过]