IO/输入输出
大多数应用程序都需要与外部设备进行数据交换,最常见的外部设备包含磁盘文件系统和网络,IO就是指应用程序对这些设备的数据输入与输出的操作,在程序中,键盘被当作一种特殊的输入文件,显示器被当作输出文件使用。Java语言定义了许多类专门负责各种方式的输入输出,这些类都被放在java.io包中。
File类
File类是IO包中惟一代表磁盘文件本身信息的类,注意不是代表文件中的内容,而是代表文件的信息,如文件名、修改时间、可读等信息内容。File类定义了一些与平台无关的方法来操纵文件,通过调用File类提花的各种方法,我们能够创建、删除文件,重命名文件,判断文件的读写权限及文件是否存在,设置和查询文件的最近修改时间。
在Java中,目录也被当作File使用,只是多了一些目录特有的功能——可以用list方法列出目录中所有子目录和文件名。在Unix下的路径分隔符为(/),在Dos下的路径分隔符为(\),Java可以正确处理Unix和Dos的路径分隔符,即使我们在Windows环境下使用(/)作为路径分隔符,Java仍然能够正确处理。
下面的例子演示了File类用法,判断某个文件是否存在,存在则删除,不存在则创建:
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
class TestFile {
public static void main(String[] args) {
File f = new File("D:\\text1\\text2\\text.txt");//这里的目录结构必须已经存在
//此处可尝试:text、text1\\text2、text.txt
//如果用new File("aa","1.txt"),则aa为1.txt文件的parent目录,但是aa目录必须已经存在
if(f.exists()) { //判断文件或目录是否存在
f.delete(); //删除文件或目录
} else {
try{
f.createNewFile(); //创建文件
// f.mkdirs(); //创建目录
} catch(Exception e) {e.printStackTrace();}
}
System.out.println(f.exists()? "文件或目录已存在" : "文件或目录不存在");
System.out.println("指定的路径名为:" + f);
System.out.println("指定的文件名或目录名为:" + f.getName());
System.out.println("指定的文件路径为:" + f.getPath());
System.out.println("指定的文件绝对路径为:" + f.getAbsolutePath());
System.out.println("指定的父目录为:" + f.getParent());
System.out.println(f.isAbsolute()? "是绝对路径" : "不是绝对路径");
System.out.println(f.isDirectory()? "这是一个目录" : "这不是一个目录");
System.out.println(f.isFile()? "这是一个文件" : "这不是一个文件");
System.out.println(f.isHidden()? "这是一个隐藏文件" : "这不是一个隐藏文件");
System.out.println(f.canExecute()? "这是可执行文件" : "这不是一个可执行文件");
System.out.println(f.canRead()? "这是可读文件" : "这不是一个可读文件");
System.out.println(f.canWrite()? "这是可写文件" : "这不是一个可写文件");
if(f.exists()){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
Date d = new Date(f.lastModified());//lastModified为返回文件的最后修改时间,值为long型
String lastModified =sdf.format(d);
System.out.println("文件最后一次修改时间为:" + lastModified);
}
}
}
注意:delete方法删除由File对象的路径所表示的磁盘文件或目录。如果删除的对象是目录,该目录中的内容必须为空。
上面代码只是对文件本身的属性进行操作,而不能访问文件的内容,即不能够从文件中读取数据或往文件里写数据。
RandomAccessFile类
RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类,它提供了众多的文件访问方法。RandomAccessFile类支持“随机访问”方式,可以跳转到文件的任意位置处读写数据。在你访问一个文件的时候,不想把文件从头读到尾,并希望像访问一个数据库一样地访问一个文本文件,使用RandomAccessFile类就是你的最佳选择。
RandomAccessFile对象类有个位置指示器,指向当前读写处的位置,文件的开始位置为0,当读写n个字节后,文件指示器将指向这n个字节后的下一个字节处。刚打开文件时,文件指示器指向文件的开头处,我们可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。
RandomAccessFile类提供了众多的文件访问方法
RandomAccessFile类支持“随机访问”方式
RandomAccessFile类在随机读写等长记录格式的文件时有很大的优势
RandomAccessFile类仅限于操作文件,不能访问其他IO设备,如网络、内存映象等。
两种构造方法:
new RandomAccessFile(f,”rw”);//读写方式
new RandomAccessFile(f,”r”);//只读方式
注:当我们的程序以读写的方式打开一个文件时,如果这个文件不存在,程序会为你创建
编程实例:往文件中写入三名员工的信息,每个员工含有姓名和年龄两个字段,然后按照第二名、第一名、第三名的先后顺序读取出员工信息。
我们需要设计一个类来封装员工信息。一个员工信息就是文件中的一条记录,我们必须保证每条记录在文件中的大小相同,也就是每个员工的姓名字段在文件中的长度是一样的,我们才能够确定每条记录在文件中的具体位置。假设name中有8个字符,少于8个则补空格(”\u0000”),多于8个则去掉后面多余的部分。由于年龄是整数型,不管这个数有多大,只要它不超过整型数的范围,在内存中都是占4个字节大小。
字符串对象有一个length方法用来返回字符串对象的字符个数。如:
“abc”.length( );//返回3
“戴振良”.length( );//返回3
注意1:英文字符虽然占两个字节,但是他们的unicode码只占一个字节,而中文字符却占两个字符。byte只占一个字节,所以把一个英文字符转换为byte型时数据不会丢失,如:(byte)’a’, 而把一个中文字符转换为byte就会丢失数据,如:int b=‘戴’; //b=25140 int b=(byte)‘戴’; //b=52
注意2: String a=null; a=a+"你好"; 此时a="null你好";所以可以把a初始化为空字符串” ”
Employee.java
public class Employee
{
public String name = null;
public int age = 0;
public static final int LEN = 8;
public Employee(String name, int age)
{
if(name.length()>LEN)//字符串对象的length方法用于返回字符串对象的长度(字符个数)
{
name=name.substring(0,LEN);
}
else
{
while(name.length()<LEN)
{
name+="\u0000";
}
}
this.name=name;
this.age=age;
}
}
RandomFileTest.java
import java.io.*;
public class RandomFileTest
{
public static void main(String[] args) throws Exception //在这里加了throws Exception后就不需要捕获异常了
{
Employee e1=new Employee("张三",23);
Employee e2=new Employee("lisi",256);
Employee e3=new Employee("wangwu",25);
RandomAccessFile ra=new RandomAccessFile("employee.txt","rw");
//getBytes()方法用于把该字符串对象的所有字符转换为Byte数组,因为中文字符太多,要用两个字节来表示,而一个字节就能表示完所有的英文字符,当读取英文字符是,则读一个字符会转换成一个字节,而读一个中文字符的时候会转换成两个字节,
//write(int b)向此文件写入指定的字节。
//write(byte[] b)将b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。
//writeInt方法:按四个字节将 int 写入该文件,先写高字节。
//ra.write(e1.name.getBytes());
//writeChar(int v) 按双字节值将 char 写入该文件,先写高字节
//writeChars(String s) 按字符序列将一个字符串写入该文件。
ra.writeChars(e1.name); //写进来的字符串以Unicode码表示,每个字符占两个字节
ra.writeInt(e1.age);
//ra.write(e2.name.getBytes());
ra.writeChars(e2.name);
ra.writeInt(e2.age);
//ra.write(e3.name.getBytes());
ra.writeChars(e3.name);
ra.writeInt(e3.age);
ra.close();//关闭文件
//int len=0;
//byte[] buf=new byte[Employee.LEN];
String strName="";
RandomAccessFile raf=new RandomAccessFile("employee.txt","r");
//skipBytes(int n) //尝试跳过输入的 n 个字节以丢弃跳过的字节。
raf.skipBytes(Employee.LEN*2+4);//跳过12个字节,从第13个字节开始读写操作
//len=raf.read(buf);//将最多 buf.length 个数据字节从此文件读入 buf 数组。
//buf参数是将数据读入的缓冲区,该方法返回的是读入缓冲区的总字节数(int型),如果由于已到达此文件的末尾而不再有数据,则返回 -1。
//strName=new String(buf,0,len);
for(int i=0;i<Employee.LEN;i++)
{ //readChar()从此文件读取一个字符。
strName+=raf.readChar();
}
System.out.println(strName.trim()+":"+raf.readInt());
strName="";
//trim方法:删除字符串对象中字符串前面、后面的那些不可显示的字符
//read()无参数方法:从此文件中读取一个数据字节。以整数形式返回此字节,范围在 0 到 255 (0x00-0x0ff)。如果尚无输入可用,将阻塞此方法。
//seek(long pos) //设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
raf.seek(0);//定位到文件中的第0个字节处
//len=raf.read(buf);
//strName=new String(buf,0,len);
for(int i=0;i<Employee.LEN;i++)
{
strName+=raf.readChar();
}
System.out.println(strName.trim()+":"+raf.readInt());//读取完这条语句,指示器已经指示到了第二条记录的开始处了
strName="";
raf.skipBytes(Employee.LEN*2+4);
//len=raf.read(buf);
//strName=new String(buf,0,len);
for(int i=0;i<Employee.LEN;i++)
{
strName+=raf.readChar();
}
System.out.println(strName.trim()+":"+raf.readInt());
raf.close();
}
}