文件操作是Java I/O中常用且重要的一类操作,数据在磁盘中的唯一最小描述就是文件,上层应用程序只能通过文件来操作磁盘上的数据。在Java中,文件通过File类来描述,表示一个真实存在的文件或者是一个包含多个文件的目录。Java.io.File类能够完成对文件的基本操作,如创建、删除等操作,也可以完成对目录的创建这些操作。
首先来看File中两个重要的变量:
private String path; private transient int prefixLength;
在File中有多个构造函数,但是不难发现这些构造函数会有一个共同点,那就是为如上的两个变量赋值,如:
public File(String pathname) { if (pathname == null) { throw new NullPointerException(); } this.path = fs.normalize(pathname); this.prefixLength = fs.prefixLength(this.path); }normalize()方法主要的作用是规范路径的写法,所以String path="D:\\a.cmd.txt"、String path="/D:/a.cmd.txt"和String path="D:\\\\\\\a.cmd.txt"都是合法的。
prefixLength是由transient关键字修饰,为了安全问题,所以并不希望把类里面所有的东西都能存储(因为那样,别人可以通过序列化知道类里面的内容),即不会随类一起序列化到本地,所以当还原后,这个关键字定义的变量也就不再存在。当调用normalize()方法将路径规范化后,调用prefixLength()方法计算前缀长度,方便计算路径的父路径或者计算路径中包含的文件名等操作。如:
public String getParent() { int index = path.lastIndexOf(separatorChar); if (index < prefixLength) { if ((prefixLength > 0) && (path.length() > prefixLength)) return path.substring(0, prefixLength); return null; } return path.substring(0, index); }
测试如下:
String path="D:\\\\a/cmd.txt"; File f=new File(path); System.out.println("path="+f.getPath());// path=D:\a\cmd.txt System.out.println(f.getName());// cmd.txt System.out.println(f.getParent()); // D:\a可以看到,路径被正确规范化,并且正确得到了文件名和父路径等信息。
其实对于文件的操作非常多,有兴趣的可以自己根据如下的一些例子去查看源代码,下面来介绍一些常见的操作。
1、列出指定目录下的文件或目录
//只能列出一层目录 public class ListMyDir { public static void main(String[] args) { String fileName = "C:" + File.separator + "data"; File f = new File(fileName); File[] fs = f.listFiles(); for (int i = 0; i < fs.length; i++) { System.out.println(fs[i].getName()); } } }
2、列出指定目录下的所有目录或文件(目录下的文件或目录也会列出)
public void print(File f) { if (f != null) { if (f.isDirectory()) { File[] fileArray = f.listFiles(); if (fileArray != null) { for (int i = 0; i < fileArray.length; i++) { print(fileArray[i]);// 递归调用 } } } else { System.out.println(f); } } }
public void readFile(String fileName) { File srcFile = new File(fileName); InputStream in = null; try { in = new FileInputStream(srcFile); byte[] b = new byte[(int) srcFile.length()]; for (int i = 0; i < b.length; i++) { b[i] = (byte) in.read(); } System.out.println(new String(b)); } catch (Exception e) { e.printStackTrace(); } finally { try { if (in != null) { in.close(); in = null; } } catch (Exception e) { } } }
public void writeWithByte() { String fileName = "D:" + File.separator + "hello.txt"; OutputStream out = null; File f = new File(fileName); try { out = new FileOutputStream(f, true); String str = " [Publicity ministry of ShangHai Municipal committee of CPC]"; byte[] b = str.getBytes(); out.write(b); } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); out = null; } } catch (Exception e) { } } } public void writeWithByteArray() { String fileName = "D:" + File.separator + "hello.txt"; OutputStream out = null; File f = new File(fileName); try { out = new FileOutputStream(f, true); String str = " [hello with byte yi ge ge xie]"; byte[] b = str.getBytes(); for (int i = 0; i < b.length; i++) { out.write(b[i]); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); out = null; } } catch (Exception e) { } } }5、实现文件的复制
public void copy(String src, String des) { File srcFile = new File(src); File desFile = new File(des); InputStream in = null; OutputStream out = null; try { in = new FileInputStream(srcFile); out = new FileOutputStream(desFile); byte[] b = new byte[(int) srcFile.length()]; for (int i = 0; i < b.length; i++) { b[i] = (byte) in.read(); } out.write(b); System.out.println("copied [" + srcFile.getName() + "] with " + srcFile.length()); } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); out = null; } } catch (Exception e) { } try { if (in != null) { in.close(); in = null; } } catch (Exception e) { } } }或者使用缓存实现文件的复制,如下:
public void copy(String src, String des) { File srcFile = new File(src); File desFile = new File(des); BufferedInputStream bin = null; BufferedOutputStream bout = null; try { bin = new BufferedInputStream(new FileInputStream(srcFile)); bout = new BufferedOutputStream(new FileOutputStream(desFile)); byte[] b = new byte[1024]; while (bin.read(b) != -1) { bout.write(b); } bout.flush(); System.out.println("copied [" + srcFile.getName() + "] with " + srcFile.length()); } catch (Exception e) { e.printStackTrace(); } finally { try { if (bout != null) { bout.close(); bout = null; } } catch (Exception e) { } try { if (bin != null) { bin.close(); bin = null; } } catch (Exception e) { } } }
可能在写入或读取文件的时候,通常还需要更加灵活的操作,如向一个文件中的开始处添加字符串,读取一个文件末尾的指定数量的字符等等,这时候就需要通过RandomAccessFile类中的方法进行操作了。这主要是通过一个类似于数组指针来实现的,可以将一个文件中存储的所有内容放到一个数组中,然后通过指定来读取到特定的部分,常见的这类方法如:
seek(long pos) // Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs.
getFilePointer() // Returns the current offset in this file.可以看到,能够灵活地操作file-pointer来读取或写入内容。
举个例子:
public class RandomAccess { public void writeToFile() { String fileName = "D:" + File.separator + "hello.txt"; RandomAccessFile randomIO = null; try { File f = new File(fileName); randomIO = new RandomAccessFile(f, "rw"); randomIO.writeBytes("asdsad"); randomIO.writeInt(12); randomIO.writeBoolean(true); randomIO.writeChar(‘A‘); randomIO.writeFloat(1.21f); randomIO.writeDouble(12.123); } catch (Exception e) { e.printStackTrace(); } finally { try { if (randomIO != null) { randomIO.close(); randomIO = null; } } catch (Exception e) { } } } public static void main(String[] args) { RandomAccess randomA = new RandomAccess(); randomA.writeToFile(); } }
传统的文件操作只能实现一些简单的输入、输出、创建、删除等操作,如果要进行更加复杂的操作就无能为力了。例如,读取一个非常大的日志文件会造成I/O阻塞、对文件的属性支持力度不够、不支持目录树导航的类或方法等等,而这些操作往往对一个文件的操作来说非常重要,所以Java 7提供了亲的I/O API,即NIO.2。
由于新的NIO中文件或目录使用的对象为Path,所以为了兼容新的类,File中提供了toPath()方法,源代码如下:
public Path toPath() {// 在Java1.7中,主要对文件的操作就是Path对象,提供了一个兼容 Path result = filePath; if (result == null) { synchronized (this) { result = filePath; if (result == null) { result = FileSystems.getDefault().getPath(path); filePath = result; } } } return result; }可以直接将已经存在的File对象转换为NIO中的Path对象进行操作。
seek(long pos)
Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs.
|