欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java I/O

程序员文章站 2022-06-14 14:47:11
...

1、File类

  File类能新建、删除、重命名文件或目录,如果需要访问文件内容则使用I/O流:

import java.io.File;
import java.nio.file.Files;

public class Test
{
	public static void main(String[] args) throws Exception
	{
		File file = new File(".");//.表示当前路径,它是运行Java虚拟机时的路径,即使用"java"命令执行程序时所在的目录。
		
		String str = file.getName(); // .
		str = file.getPath(); // .
		str = file.getAbsolutePath(); // D:\java_project\Test\sources\.
		str = file.getCanonicalPath(); // D:\java_project\Test\sources
		str = file.getParent(); // null
		
		File fileAbs = file.getAbsoluteFile();
		str = fileAbs.getName(); // .
		str = fileAbs.getPath(); // D:\java_project\Test\sources\.
		str = fileAbs.getAbsolutePath(); // D:\java_project\Test\sources\.
		str = fileAbs.getCanonicalPath(); // D:\java_project\Test\sources
		str = fileAbs.getParent(); // D:\java_project\Test\sources
		
		
		file = new File("test");
		
		str = file.getName(); // test
		str = file.getPath(); // test
		str = file.getAbsolutePath(); // D:\java_project\Test\sources\test
		str = file.getCanonicalPath(); // D:\java_project\Test\sources\test
		str = file.getParent(); // null
		
		fileAbs = file.getAbsoluteFile();
		str = fileAbs.getName(); // test
		str = fileAbs.getPath(); // D:\java_project\Test\sources\test
		str = fileAbs.getAbsolutePath(); // D:\java_project\Test\sources\test
		str = fileAbs.getCanonicalPath(); // D:\java_project\Test\sources\test
		str = fileAbs.getParent(); // D:\java_project\Test\sources

		
		//重命名文件
		File file = new File("E:/test1.txt");
		File fileNew = new File("E:/test2.txt");
		file.renameTo(fileNew);
		
		//移动文件并重命名文件
		File file = new File("E:/test2.txt");
		File fileTo = new File("F:/source/test3.txt");
		file.renameTo(fileTo);
		
		//复制并重命名文件
		File file = new File("E:/test2.txt");
		File fileDest = new File("F:/source/test3.txt");
		Files.copy(file.toPath(), fileDest.toPath());
		
		//注册删除钩子:当java虚拟机退出时删除指定文件
		File file = new File("E:/test.txt");
		file.deleteOnExit();
	}
}

  File类中其它常用方法:

    exists():判断文件或目录是否存在。
    length():获取文件大小。
    delete():删除文件或目录。
    createNewFile():创建文件,如果该文件不存在的话。
    mkdir():创建目录。
    list()/listFiles():获得所有子文件和目录。

其中list()的一个版本支持接收一个FilenameFilter接口,FilenameFilter接口里包含一个accept()方法,该方法会对File的子目录或文件进行迭代,返回true的话list()才会列出该子目录或文件。FilenameFilter接口里只有一个抽象方法,因此该接口是一个函数式接口,可以使用lambda表达式创建实现该接口的对象:

public class Test
{
	public static void main(String[] args)
	{
		File file = new File(".");

		// 使用Lambda表达式实现文件过滤器,如果文件名以.java结尾,或者文件对应一个路径,返回true
		String[] nameList = file.list((dir, name) -> name.endsWith(".java") || new File(name).isDirectory());
		for(String name : nameList)
		{
			System.out.println(name);
		}
	}
}

2、I/O流

  java的I/O流体系如下图所示: 

Java I/O

  上图中粗字体表示节点流,必须直接与指定物理节点(文件、数组、管道、字符串)关联,处理流则可以包装一个节点流来使用。

  以File开头的为读写文件流、ByteArray或CharArray开头的读写二进制数组或字符数组流、Piped为读写管道,String开头的为读写字符串流。

  InputStream、Reader是输入流抽象基类,OutputStream、Writer是输出流抽象基类,这里的输入、输出指的是从内存考虑,即数据从文件输入到内存buffer、数据从内存buffer输出到文件。

  字节流以InputStream、OutputStream为基类,数据单元为一个字节,字符流以Reader、Writer为基类,数据单元为两个字节。

  因为I/O流类实现了AutoClosebale接口,因此也可以像JDBC那样通过自动关闭资源的try块来自动关闭I/O流。

  以下为使用FileInputStream来读取文件的示例:

public class FileStreamTest
{
	public void readData() throws IOException
	{
		FileInputStream fis = new FileInputStream("test.java");

		byte[] bbuf = new byte[1024];
		int hasRead = 0;
		while ((hasRead = fis.read(bbuf)) > 0 ) //读取1024个单位的数据到buffer
		{
			//ToDo:
		}
		
		fis.close();
	}
	public void writeString() throws IOException
	{
		try(
			FileWriter fw = new FileWriter("test.txt"))
		{
			fw.write("测试数据\r\n");
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
}

  以下为使用StringReader和StringWriter来读写String的示例:

import java.io.*;

public class StringNodeTest
{
	public static void main(String[] args)
	{
		// 采用循环读取的访问读取字符串
		String src = "从明天起,做一个幸福的人\n"
			+ "喂马,劈柴,周游世界\n"
			+ "从明天起,关心粮食和蔬菜\n"
			+ "我有一所房子,面朝大海,春暖花开\n"
			+ "从明天起,和每一个亲人通信\n"
			+ "告诉他们我的幸福\n";
		char[] buffer = new char[32];
		int hasRead = 0;
		try(
			StringReader sr = new StringReader(src))
		{
			while((hasRead = sr.read(buffer)) > 0)
			{
				System.out.print(new String(buffer ,0 , hasRead));
			}
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
		
		// 调用StringWriter的方法执行输出
		try(
			// 创建StringWriter时,因为String是不可变的字符串对象,所以实际上以一个StringBuffer
			//作为输出节点,指定的20就是StringBuffer的初始长度
			StringWriter sw = new StringWriter(20)) 
		{
			sw.write("有一个美丽的新世界,\n");
			sw.write("她在远方等我,\n");
			sw.write("哪里有天真的孩子,\n");
			sw.write("还有姑娘的酒窝\n");
			System.out.println("----下面是sw的字符串节点里的内容----");
			
			System.out.println(sw.toString());// 使用toString()方法返回StringWriter的字符串节点的内容
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
		}
	}
}

  InputStreamReader、OutputStreamWrite可以将包装的字节流转换成字符输入流、字符输出流。BufferedReader、BufferedWriter流具有缓冲功能,它可以一次读取/写入一行文本。下面代码将标准输入System.in(System.in是字节流类的实例)通过InputStreamReader转换成字符输入流,再将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容:

import java.io.*;

public class KeyinTest
{
	public static void main(String[] args)
	{
		try(
			InputStreamReader reader = new InputStreamReader(System.in);// 将Sytem.in对象转换成字符输入对象Reader
			BufferedReader br = new BufferedReader(reader))// 将普通Reader包装成BufferedReader
		{
			String line = null;
			// 采用循环方式来一行一行的读取
			while ((line = br.readLine()) != null)
			{
				// 如果读取的字符串为"exit",程序退出
				if (line.equals("exit"))
				{
					System.exit(1);
				}
		
				System.out.println("输入内容为:" + line);// 打印读取的内容
			}
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
}

  PushbackInputStream和PushbackReader是推回输入流,可以调用其unread()方法将字节或字符数组中数据推回到“推回缓冲区”中,然后调用read()方法读取数据的时候会先从“推回缓冲区”中读取数据。

3、处理流

  按照流的角色可以分为节点流(低级流)和处理流(高级流、包装流),如下所示,节点流必须与指定的物理节点(文件、数组、管道 、字符串)关联,处理流可以包装一个节点流来使用。

Java I/O

  使用处理流进行I/O操作更加方便快捷:无需理会I/O节点是磁盘、网络还是其他I/O设备,只要将这些节点包装成处理流,就可以使用相同的I/O代码来读写不同的I/O设备。也就是说程序通过处理流来执行I/O功能,让节点流与底层I/O设备交互,程序员只需关心处理流的操作。比如下面使用PrintStream处理流来包装FileOutputStream,PrintStream的输出功能非常强大,它包含的成员方法可以很方便的进行输出操作,System.out的类型就是PrintStream:

package xu;
import java.io.*;

public class Test
{
	public static void main(String[] args) throws Exception
	{
		try(
			FileOutputStream fos = new FileOutputStream("test.txt");
			PrintStream ps = new PrintStream(fos))
		{
			ps.println("普通字符串"); //输出字符串
			ps.println(new Test()); //输出对象
			ps.println(99); //输出int
			ps.format("test%d", 100); //格式化输出
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
}

4、重定向标准输入/输出

  Java的标准输入System.in是从键盘读取输入,标准输出System.out是输出到显示器,可以通过System的静态方法setErr()、setIn()、setOut()来重定向标准错误输出流、标准输入流、标准输出流。

  下面程序重定向了标准输出流System.out到文件输出,而不是向显示器输出:

import java.io.*;

public class RedirectOut
{
	public static void main(String[] args)
	{
		try(
			//创建PrintStream输出流
			PrintStream ps = new PrintStream(new FileOutputStream("out.txt")))
		{
			System.setOut(ps);// 将标准输出重定向到ps输出流
			System.out.println("普通字符串");// 向标准输出输出一个字符串
			System.out.println(new RedirectOut());// 向标准输出输出一个对象
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
		}
	}
}

  下面程序重定向祖标准出入流System.in到文件输入,而不是从键盘输入:

import java.util.*;
import java.io.*;

public class RedirectIn
{
	public static void main(String[] args)
	{
		try(
			//创建FileInputStream输入流
			FileInputStream fis = new FileInputStream("RedirectIn.java"))
		{
			System.setIn(fis);// 将标准输入重定向到fis输入流
			Scanner sc = new Scanner(System.in);// 使用System.in创建Scanner对象,用于获取标准输入
			sc.useDelimiter("\n");// 只把回车作为分隔符
			
			// 判断是否还有下一个输入项
			while(sc.hasNext())
			{
				// 输出输入项
				System.out.println("键盘输入的内容是:" + sc.next());
			}
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
		}
	}
}

5、读写其它进程的数据

  使用Runtime对象的exec()方法可以运行其他进程,该方法产生一个Process对象,该对象代表启动的子进程。调用Process的以下三个方法可以与启动的子进程进行通信:

    getErrorStream():获取子进程的标准错误流
    getInputStream():获取子进程的标准输入流
    getOutputStream():获取子进程的标准输出流

  下面的代码运行了子进程javac命令程序,获取到其标准错误流,该标准错误流对与子进程来说是输出流,而对于本程序则是输入流,将其转换成字符输入流InputStreamReader后包装成BufferedReader就可以来读取其数据并显示出来:

import java.io.*;

public class ReadFromProcess
{
	public static void main(String[] args)
		throws IOException
	{
		// 运行javac命令,返回运行该命令的子进程
		Process p = Runtime.getRuntime().exec("javac");
		try(
			// 以p进程的错误流创建BufferedReader对象
			BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream())))
		{
			String buff = null;
			// 采取循环方式来读取p进程的错误输出
			while((buff = br.readLine()) != null)
			{
				System.out.println(buff);
			}
		}
	}
}

  下面的代码通过Process的getOutputStream获取到子进程“ReadStandard”的标准输入流,对于本程序来说就是获取到了向子进程输出数据的流,本程序通过该输出流向子进程输入数据,子进程中将标准输入流的内容写入到out.txt文件中:

import java.io.*;
import java.util.*;

public class WriteToProcess
{
	public static void main(String[] args)
		throws IOException
	{
		// 运行java ReadStandard命令,返回运行该命令的子进程
		Process p = Runtime.getRuntime().exec("java ReadStandard");
		try(
			// 以p进程的输出流创建PrintStream对象
			// 这个输出流对本程序是输出流,对p进程则是输入流
			PrintStream ps = new PrintStream(p.getOutputStream()))
		{
			// 向ReadStandard程序写入内容,这些内容将被ReadStandard读取
			ps.println("普通字符串");
			ps.println(new WriteToProcess());
		}
	}
}

// ReadStandard类,该类可以接受标准输入,并将标准输入写入到out.txt文件。
class ReadStandard
{
	public static void main(String[] args)
	{
		try(
			// 使用System.in创建Scanner对象,用于获取标准输入
			Scanner sc = new Scanner(System.in);
			PrintStream ps = new PrintStream(new FileOutputStream("out.txt")))
		{
			sc.useDelimiter("\n");// 只把回车作为分隔符
			
			// 判断是否还有下一个输入项
			while(sc.hasNext())
			{
				ps.println("键盘输入的内容是:" + sc.next());// 输出输入项
			}
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
}

6、RandomAccessFile

  RandomAccessFile可以读、写文件,并提供移动文件指针的功能,getFilePointer()成员可以获得当前文件指针,seek()用来移动文件指针。创建RandomAccessFile时需要指定一个mode参数,其取值可以为:

  "r":只读,写的话会抛出IOException异常。
  "rw":读写,如果文件不存在则创建该文件。
  "rwd":读写,且对文件内容的每个更新都同步写入到底层存储设置。
  "rws":读写,且对文件内容和元数据的每个更新都同步写入到底层存储设置。

import java.io.*;

public class AppendContent
{
	public static void main(String[] args)
	{
		try(
			//以读、写方式打开一个RandomAccessFile对象
			RandomAccessFile raf = new RandomAccessFile("out.txt" , "rw"))
		{
			//将记录指针移动到out.txt文件的最后并写入数据
			raf.seek(raf.length());
			raf.write("追加的内容!\r\n".getBytes());
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
		}
	}
}