Java 7の非同期I/O第1編-従来ファイルI/O操作

8410 ワード

ファイル操作はJava I/Oでよく使われる重要な操作であり、ディスク内のデータの唯一の最小記述はファイルであり、上位アプリケーションはファイルを通じてディスク上のデータを操作するしかない.Javaでは、ファイルはFileクラスで記述され、実際に存在するファイルまたは複数のファイルを含むディレクトリを表します.Java.io.Fileクラスは、ファイルの作成、削除などの基本的な操作を完了したり、ディレクトリの作成を完了したりすることができます.
まず、Fileの2つの重要な変数を見てみましょう.
 private String path;
 private transient int prefixLength;

Fileには複数のコンストラクション関数がありますが、これらのコンストラクション関数には、次のような2つの変数に値を割り当てる共通点があることがわかります.
 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);
			}
		}
	}

3、外部から一つのファイルを読み込む
	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) {	}
		}
	}	

4、外部ファイルへの出力
	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) {	}
		}
	}

ファイルの書き込みや読み取りの際には、通常、1つのファイルの先頭に文字列を追加したり、1つのファイルの末尾に指定された数の文字を読み出したりするなど、より柔軟な操作が必要になる場合があります.この場合は、RandomAccessFileクラスの方法で操作する必要があります.これは主に配列ポインタと同様に実現され、1つのファイルに格納されているすべてのコンテンツを1つの配列に配置し、指定によって特定の部分に読み込むことができます.一般的な方法は、次のようです.
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がブロックする、ファイルの属性に対するサポート力が足りない、ディレクトリツリーのナビゲーションをサポートしないクラスや方法などがあるが、これらの操作は往々にして1つのファイルの操作にとって非常に重要であるため、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.