关于磁盘文件的读写
输入/输出(I/O)是每一项计算机语言,必须有的东西。不让人输入数据的话,计算机怎么处理数据呢?在java语言中,I/O的方式是流的方式。流(stream)这是个学习java输入输出的最基本的概念。
流是字节从源到目的的有序序列。一方面是字节,一方面是有序的。流描述的是一个过程,顺序严格。一个需要键盘输入的程序可以用流来做到这一点。两种基本的流是:输入流和输出流。你可以从输入流读,但你不能对它写。要从输入流读取字节,必须有一个与这个流相关联的字符源。
Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java中其他多种多样变化的流均是由它们派生出来的:
l 字节流:传字节的。以8位字节为单位进行读写,以InputStream与OutputStream为基础类
l 字符流: 传字符的。以16位字符为单位进行读写,以Reader与Writer为基础类
l 文件流: 传文件的。属于节点流,对文件读写,传输。里面的类很多。
l 序列化:传对象的。一个对象怎么读啊,只有变成二进制才可以读,这就是序列化。
(一) File类
类File提供了一种与机器无关的方式来描述一个文件对象的属性。下面我们介绍类File中提供的各种方法。
◇ 文件或目录的生成
public File(String path);
public File(String path,String name);//path是路径名,name是文件名
public File(File dir,String name);//dir是路径名,name是文件名
◇ 文件名的处理
String getName( ); //得到一个文件的名称(不包括路径)
String getPath( ); //得到一个文件的路径名
String getAbsolutePath( );//得到一个文件的绝对路径名
String getParent( ); //得到一个文件的上一级目录名
String renameTo(File newName); //将当前文件名更名为给定文件的
完整路径
◇ 文件属性测试
boolean exists( ); //测试当前File对象所指示的文件是否存在
boolean canWrite( );//测试当前文件是否可写
boolean canRead( );//测试当前文件是否可读
boolean isFile( ); //测试当前文件是否是文件(不是目录)
boolean isDirectory( ); //测试当前文件是否是目录
◇ 普通文件信息和工具
long lastModified( );//得到文件最近一次修改的时间
long length( ); //得到文件的长度,以字节为单位
boolean delete( ); //删除当前文件
◇ 目录操作
boolean mkdir( ); //根据当前对象生成一个由该对象指定的路径
String list( ); //列出当前目录下的文件
【例4-1】文件方法测试1
public class FileTest {
public static void main(String[] args) {
File fb = new File("c:\\test","b.txt");//b.txt存在
File fd = new File("c:\\test");//test是一个目录
File fc = new File("c:\\test","c.txt");//c.txt 不存在
System.out.println("b.txt文件是否可读?"+fb.canRead());
System.out.println("b.txt文件的大小"+fb.length());
System.out.println("b.txt文件的绝对路径"+fb.getAbsolutePath());
System.out.println("c:\\test是一个目录吗?"+fd.isDirectory());
if(fc.exists()){
fc.delete();
System.out.println("文件存在,将其删除");
}else{
try{
fc.createNewFile(); //文件不存在,新生成一个
System.out.println("c.txt不存在,新生成一个");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(一) 字节流
Java中每一种字节流的基本功能依赖于基本类InputStream和OutputStream,它们是抽象类,不能直接使用。
1. InputStream类
(1)属于InputStream类的方法有:
l read():从流中读入数据
l skip():跳过流中若干字节数
l available():返回流中可用字节数
l mark():在流中标记一个位置
l reset():返回标记过的位置
l markSupport():是否支持标记和复位操作
l close():关闭流
在InputStream类中,方法read()提供了三种从流中读数据的方法:
l int read():从输入流中读一个字节,形成一个0~255之间的整数返回(是一个抽象方法)。
l int read(byte b[]):读多个字节到数组中,填满整个数组。
l int read(byte b[], int off, int len):从输入流中读取长度为len的数据,写入数组b中从索引off开始的位置,并返回读取得字节数。
对于这三个方法,若返回-1,表明流结束,否则,返回实际读取的字节数。
【例】类FileInputStream读文件
public class FileInputStreamTest {
public static void main(String[] args) {
try { // 创建文件输入流对象
FileInputStream rf = new FileInputStream("c:\\test\\a.txt");
int n = 20;
byte buffer[] = new byte[n];
while ((rf.read(buffer, 0, n) != -1) && (n > 0)){ // 读取输入流
System.out.print(new String(buffer));
}
System.out.println();
rf.close(); // 关闭输入流
} catch (IOException ioe) {
System.out.println(ioe);
} catch (Exception e) {
System.out.println(e);
}
}
}
2. OutputStream类
属于OutputStream类的方法有:
l write(int b):将一个整数输出到流中(只输出低位字节,为抽象方法)
l write(byte b[]):将字节数组中的数据输出到流中
l write(byte b[], int off, int len):将数组b中从off指定的位置开始,长度为len的数据输出到流中
l flush():刷空输出流,并将缓冲区中的数据强制送出
l close():关闭流
【例】类FileOnputStream写文件
public class FileOutputStreamTest {
public static void main(String args[]) {
try {
System.out.print("Input: ");
int count, n = 512;
byte buffer[] = new byte[n];
count = System.in.read(buffer); // 读取标准输入流
FileOutputStream wf = new FileOutputStream("c:\\test\\b.txt");
// 创建文件输出流对象
wf.write(buffer, 0, count); // 写入输出流
wf.close(); // 关闭输出流
System.out.println("Save to b.txt!");
} catch (IOException ioe) {
System.out.println(ioe);
} catch (Exception e) {
System.out.println(e);
}
}
}
(二) 字符流
在程序中一个字符等于两个字节,Java为我们提供了Reader和Writer两个专门操作字符流的类。
1. 字符输入流:Reader
Reader本身也是一个抽象类,同样,如果使用它,我们需要通过其子类来实例化它才可以使用它。
(1)Reader类的常用方法
public abstract void close() throws IOException
public int read() throws IOException
public int read(char cbuf) throws IOException
通过方法我们看到Reader类只提供了一个读入字符的方法
(2)FileReader示例
【例】把文本中的内容读出来,并且显示在控制台上
public class FileReaderTest {
public static void main(String[] args) {
// 声明一个File对象
File file = new File("c:\\test\\a.txt");
// 声明一个Reader类的对象
Reader reader = null;
// 声明一个字符数组
char[] c = new char[1024];
int len = 0;
int temp = 0;
// 通过FileReader子类来实例化Reader对象
try {
reader = new FileReader(file);
while ((temp = reader.read()) != -1) {
c[len] = (char) temp;
len++;
}
// 关闭输入流
reader.close();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
// 把char数组转换成字符串输出
System.out.println(new String(c, 0, len));
}
}
2. 字符输出流:Writer
Writer是一个字符流,它是一个抽象类,所以要使用它,也必须通过其子类来实例化它后才能使用它。
(1)Writer类的常用方法
l public abstract void close() throws IOException关闭输出流
l public void write(String str) throws IOException将字符串输出
l public void write(char cbuf) throws IOException将字符数组输出
l public abstract void flush() throws IOException强制性清空缓存
(2)FileWriter类示例
向一个文本文件中通过字符输出流写入数据
public class FileWriterTest {
public static void main(String[] args) throws Exception {
// 声明一个File对象
File file = new File("c:\\test\\b.txt");
// 声明一个Write对象
Writer writer = null;
// 通过FileWriter类来实例化Writer类的对象并以追加的形式写入
writer = new FileWriter(file, true);
//声明一个要写入的字符串
String str = "字符串形式写入Helloworld";
// 写入文本文件中
writer.write(str);
// 刷新
writer.flush();
// 关闭字符输出流
writer.close();
}
}
(3)字符流与字节流的区别
操作字节流操作时本身不会用到缓冲区,是文件本身直接操作,而字节流在操作时就使用到了缓冲区。
如果我们在操作字符流的时候,不关闭流,我们写入的数据是无法保存的。所以在操作字符流的时候一定要记得关闭流。
字节流在操作的时候是直接与文件本身关联,不使用缓冲区,字节直接存到文件中;字符流在操作的时候是通过缓冲区与文件操作,字符到缓冲区然后再到文件中,所以字符流中存在一个flush()方法来刷新缓冲区。
综合比较来讲,在传输或者在硬盘上保存的内容是以字节的形式存在的,所以字节流的操作较多,但是在操作中文的时候字符流比较好用。
【字符流的例子】,以Reader与Writer为基础类
import java.io.*;
public class CharArrayReaderDemo {
public static void main(String args[]) throws IOException {
String tmp = "abcdefghijklmnopqrstuvwxyz";
int length = tmp.length();
char c[] = new char[length];
tmp.getChars(0, length, c, 0);
CharArrayReader input1 = new CharArrayReader(c);
CharArrayReader input2 = new CharArrayReader(c, 0, 5);
int i;
System.out.println("input1 is:");
while((i = input1.read()) != -1) {
System.out.print((char)i);}
System.out.println();
System.out.println("input2 is:");
while((i = input2.read()) != -1) {
System.out.print((char)i);}
System.out.println();
}}
【/文件流的例子】
import java.io.*;
class FileInputStreamDemo {
public static void main(String args[]) throws Exception {
int size;
InputStream f = new FileInputStream("FileInputStreamDemo.java");
System.out.println("Total Available Bytes: " +
(size = f.available()));
int n = size/40;
System.out.println("First " + n +
" bytes of the file one read() at a time");
for (int i=0; i < n; i++) {
System.out.print((char) f.read());
}
System.out.println("\nStill Available: " + f.available());
System.out.println("Reading the next " + n +
" with one read(b[])");
byte b[] = new byte[n];
if (f.read(b) != n) {
System.err.println("couldn't read " + n + " bytes.");
}
System.out.println(new String(b, 0, n));
System.out.println("\nStill Available: " + (size = f.available()));
System.out.println("Skipping half of remaining bytes with skip()");
f.skip(size/2);
System.out.println("Still Available: " + f.available());
System.out.println(" Reading " + n/2 + " into the end of array");
if (f.read(b, n/2, n/2) != n/2) {
System.err.println("couldn't read " + n/2 + " bytes.");
}
System.out.println(new String(b, 0, b.length));
System.out.println("\nStill Available: " + f.available());
f.close();
}
}
(三) 访问文件(RandomAccessFil e类)
对于InputStream 和OutputStream 来说,它们的实例都是顺序访问流,也就是说,只能对文件进行顺序地读/写。随机访问文件则允许对文件内容进行随机读/写。在java中,类RandomAccessFile 提供了随机访问文件的方法。类 RandomAccessFile的声明为:
public class RandomAccessFile extends Object implements DataInput, DataOutput
接口DataInput 中定义的方法主要包括从流中读取基本类型的数据、读取一行数据、或者读取指定长度的字节数。如:readBoolean( )、readInt( )、readLine( )、readFully( ) 等。
接口DataOutput 中定义的方法主要是向流中写入基本类型的数据、或者写入一定长度的字节数组。如:writeChar( )、writeDouble( )、write( ) 等。
RandomAccessFile是一个很有用的类,可以将字节流写入到磁盘文件中,对应的也可以从磁盘文件中读取出字节流,在API中关于RandomAccessFile的描述如下:
此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。
通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出 EOFException (是一种 IOException )。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException ,而不是 EOFException 。需要特别指出的是,如果流已被关闭,则可能抛出 IOException 。
1、RandomAccessFile类中的方法。
◇ 构造方法:
RandomAccessFile(String name,String mode); //name是文件名,mode
//是打开方式,例如"r"表示只读,"rw"表示可读写,"
RandomAccessFile(File file,String mode); //file是文件对象
◇ 文件指针的操作
long getFilePointer( ); //用于得到当前的文件指针
void seek( long pos ); //用于移动文件指针到指定的位置
int skipBytes( int n ); //使文件指针向前移动指定的n个字节
2、读写文件示例
【例】将字节流写入到磁盘中、
public class RandomAccessFileTest {
public static void main(String[] args) {
testCreateFile();
testReadFile();
}
//写文件
private static void testCreateFile(){
String directory = "c:\\test";
String name = "b.txt";
File f = new File(directory, name);
RandomAccessFile file = null;
try {
file = new RandomAccessFile(f, "rw");
byte[] b = {5,10,15,20};
try {
//如果没有这行,文件也会生成,只是文件为空
file.write(b,0,4);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
if (null!=file){
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//从磁盘读字节流
private static void testReadFile(){
String directory = "c:\\test";
String name = "b.txt";
File f = new File(directory, name);
RandomAccessFile file = null;
try {
file = new RandomAccessFile(f, "rw");
byte[] b = new byte[4];
long len = file.length();
file.read(b);
//设置要读取的字节位置
file.seek(1);
System.out.println(file.readByte()+">>FilePointer>>"+file.getFilePointer());
for (int i=0;i<b.length;i++){
System.out.println(">>>"+b[i]);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if (null!=file){
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}