【JavaEE】文件io
目录
文件类型
File概述
属性
构造方法
常用方法
Reader
Writer
InputStream
OutputStream
字节流转字符流
通过Scanner读取InputStream
通过PrintWriter转换outputstream
示例
文件类型
从编程的角度看,文件类型主要就是两大类
- 文本(文件中保存的数据,都是字符串,保存的内容都是合法的字符)
- 二进制(文件中保存的数据,仅仅是二进制数据,不要求保存的内容是合法的字符)
File概述
属性
修饰符及类型 | 属性 | 说明 |
---|---|---|
static String | pathSeparator | 依赖于系统的路径分隔符,String类型表示 |
static char | pathSeparator | 依赖于系统的路径分隔符,char类型表示 |
D:\
如上所示,“\”就是一个路径分隔符
构造方法
签名 | 说明 |
File(File parent,String child) | 根据父目录+孩子文件路径,创建一个新的File实例 |
File(String pathname) | 根据文件路径创建你一个新的File实例,路径可以是绝对路径或者相对路径 |
File(String parent,String chile) | 根据父目录+孩子我呢见路径,创建一个新的File实例,父目录用路径表示 |
常用方法
修饰符及返回类型 | 方法签名 | 说明 |
---|---|---|
String | getParent() | 返回File对象的父目录文件路径 |
String | getName() | 返回File对象的纯文本名称 |
String | getPath() | 返回File对象的文件路径 |
String | getAbsolutePath() | 返回File对象的绝对路径 |
String | getCanonicalPath() | 返回File对象修饰过的绝对路径 |
boolean | exists() | 判断File对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断File对象代表的文件是否是一个目录 |
boolean | isFile() | 判断File对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据File对象,自动创建一个空文件,成功创建返回true |
boolean | delete() | 根据File对象,删除该文件 |
void | deleteOnExit() | 根据File对象,标注文件将被删除,删除动作会到JVM运行结束时才删除 |
String[] | list() | 返回File对象代表的目录下所有文件名 |
File[] | listFiles() | 返回File对象代表的目录下的所有文件,以File对象表示 |
boolean | mkdir() | 创建File对象代表的目录 |
boolean | mkdirs() | 创建File对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为平时的剪切、粘贴操作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
在标准库中,提供的读写文件的流对象,有很多个类,可以归结为两个大的类别中:
1、字节流(对应着二进制文件),每次读/写的最小单位,都是“字节”
- InputStream
- OutputStream
2、字符流(对应着文本文件) ,每次读/写的最小单位,是“字符”,一个字符可能对应多个字节,跟当前字符集编码方式有关,本质上,是针对字节流进行了又一层封装
- Reader
- Writer
上述四种是抽象类,不能直接new实例
- 无参数read:一次读取一个字符
- 一个参数read:一次读取若干个字符,会把参数指定的buf数组给填充满
- 三个参数read:一次读取若干个字符,会把参数执行的cbuf数组中,从off这个位置开始,到len这么长的范围尽量填满
使用示例:
Reader
一次read一个字符
try(Reader reader = new FileReader("D:/data.txt");){
while (true) {
int c = reader.read();
if (c == -1) {
//读取完毕
break;
}
char ch = (char) c;
System.out.println(ch);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
read方法一次读取一个字符,返回数字,当返回值为-1时,代表读取到文件末尾
一次read多个字符
Reader reader=new FileReader("D:/data.txt");
try{
while(true){
char[] ch=new char[1024];
int n= reader.read(ch);
if(n==-1)
{
//读取完毕
break;
}
System.out.println("读取长度:"+n);
System.out.println(Arrays.toString(ch));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
这样写虽然能够解决中途抛异常,导致reader不能正常释放,但是不够优雅,我们可以采取try with resources方法 ,Reader reader=new FileReader("D:/data.txt")在出了作用域后,会自动调用close方法
try(Reader reader=new FileReader("D:/data.txt")){
while(true){
char[] ch=new char[1024];
int n= reader.read(ch);
if(n==-1)
{
//读取完毕
break;
}
System.out.println("读取长度:"+n);
System.out.println(Arrays.toString(ch));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
Writer
try(Writer writer=new FileWriter("D:/data.txt"))
{
writer.write("你好你好");
} catch (IOException e) {
throw new RuntimeException(e);
}
Writer写入文件,默认情况下,会将原来的数据清空掉,如果不想清空,需要在构造函数中再传入一个参数 ,这样就能实现追加写
Writer writer=new FileWriter("D:/data.txt",true)
utf8编码,一个中文字符是3个字节,在Java标准库内部:
- 如果是只使用char,此时使用的字符集,固定就是unicode;
- 如果是使用String,此时就会自动的把每个字符的unicode转换utf8
InputStream
字节流与字符流不同的是,传入的数组是byte类型的,read返回值是int,其实是byte,0-255之间的值,-1表示到达文件末尾
try(InputStream inputStream=new FileInputStream("D:/data.txt"))
{
byte[] buffer=new byte[1024];
int n=inputStream.read(buffer);
System.out.println("n = "+n);
for(int i=0;i<n;i++)
{
System.out.printf("%x\n",buffer[i]);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
OutputStream
OutputStream和Writer类似,打开一个文件,默认就会清空文件的原有内容
try(OutputStream outputStream=new FileOutputStream("d:/data.txt"))
{
String str="你好";
outputStream.write(str.getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
若要使用追加的方式写入,可以在构造方法的第二个参数中传入true
OutputStream outputStream=new FileOutputStream("d:/data.txt",true)
字节流转字符流
当提供的是字节流对象,但实际数据内容是文本数据,我们可以将字节流转换成字符流
通过Scanner读取InputStream
try(InputStream inputStream=new FileInputStream("d:/data.txt"))
{
Scanner scanner=new Scanner(inputStream);
String str=scanner.next();
System.out.println(str);
} catch (IOException e) {
throw new RuntimeException(e);
}
通过PrintWriter转换outputstream
try(OutputStream outputStream=new FileOutputStream("d:/data.txt"))
{
PrintWriter writer=new PrintWriter(outputStream);
writer.println("hello");
} catch (IOException e) {
throw new RuntimeException(e);
}
PrintWriter这样的类,在进行写入的时候,不一定是直接写硬盘,而是先把数据写入到一个内存构成的缓冲区中,当我们写入缓冲区之后,如果还没来得及把缓冲区的数据写入硬盘中,进程就结束了,此时数据就丢失了,为了能够确保数据确实被写入硬盘,就应该在合适的时机,使用flush方法手动刷新缓冲区,因此上述的代码需要修改一下:
try(OutputStream outputStream=new FileOutputStream("d:/data.txt"))
{
PrintWriter writer=new PrintWriter(outputStream);
writer.println("hello");
writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
示例
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包括目录),并且后续询问用户是否要删除该文件
public static void main(String[] args) throws FileNotFoundException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入搜索路径:");
String direcory = scanner.next();
File sourceFile = new File(direcory);
if (!sourceFile.isDirectory()) {
System.out.println("搜索路径有误!");
return;
}
System.out.println("请输入要删除文件的关键词:");
String word=scanner.next();
scanDir(sourceFile,word);
}
public static void scanDir(File rootPath, String word)
{
File[] files = rootPath.listFiles();
if(files==null)
return;
for (File file : files) {
System.out.println("当前扫描的文件: " + file.getAbsolutePath());
if (file.isFile()) {
checkDelete(file,word);
} else {
scanDir(file,word);
}
}
}
public static void checkDelete(File rootPath, String word)
{
if(!rootPath.getName().contains(word))
{
//不必删除,直接方法结束
return ;
}
//需要删除
Scanner scanner = new Scanner(System.in);
System.out.println("是否需要删除(Y/N):");
String choice=scanner.next();
if(choice.equals("Y")||choice.equals("N"))
{
rootPath.delete();
System.out.println("删除完毕!");
}
}