一.File文件类
1.1File文件类简介
File文件类是I/O流的基础,数据的写入/读取都是对文件的操作,所以得先去了解文件,再去了解IO流,最后使用IO流完成对数据的写入/读取操作。
1.2File文件类的介绍
在Java中,File就代表一个文件类,这里的文件既包含了普通文件,又包含了文件目录
1.3File文件类的作用
可实现对文件的基本操作(创建文件、删除文件、获取文件长度、更改文件的名字等等),都可以通过File文件类去完成,因为File文件类提供了很多操作文件的方法。
1.4File文件类的常用方法
1.4.1 createNewFile()
在指定的盘符路径下,创建一个新文件
public class Demo1 {
public static void main(String[] args) {
File file = new File("D://学习/file.txt");
//文件可能为空,就是路径下并没有这个文件,可能会报错,所以要处理异常
try {
boolean flag = file.createNewFile();
if (flag){
System.out.println("创建成功");
}else{
System.out.println("创建失败");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
创建成功
1.4.2 exists()
判断文件是否存在
public class Demo2 {
public static void main(String[] args) {
File file = new File("D://学习/file.txt");
//判断文件是否存在
boolean flag = file.exists();
if (flag){
System.out.println("文件存在");
}else {
System.out.println("文件不存在");
}
}
}
我们刚才已经创建过了,显然是存在的
1.4.3 mkdirs()
在指定的盘符路径下,创建层级文件夹
public class Demo3 {
public static void main(String[] args) {
File file = new File("D://学习/a/b/c/d");
boolean flag = file.mkdirs();
if (flag){
System.out.println("层级文件夹创建成功");
}else{
System.out.println("层级文件夹创建失败");
}
}
}
多了a文件夹
a中还有b(后面的都成功了)
1.4.4 mkdir
在指定的盘符路径下,创建文件夹
public class Demo4 {
public static void main(String[] args) {
/* File file = new File("D://学习/b/b/c/d");*/
//不能创建层级文件夹
File file = new File("D://学习/b");
boolean flag = file.mkdir();
if (flag) {
System.out.println("文件夹创建成功");
} else {
System.out.println("文件夹创建失败");
}
}
}
创建成功
1.4.5 isFile()
判断指定的文件是否是普通的文件
public class Demo5 {
public static void main(String[] args) {
File file1 = new File("D://学习/a");
File file2 = new File("D://学习/file.txt");
//isFile()返回值是boolean,判断是否是普通文件
System.out.println(file1.isFile());
System.out.println(file2.isFile());
}
}
可以看出file.txt是普通文件,层级目录a并不是普通文件
1.4.6 isDirectory()
判断指定的文件是否是层级文件目录
public class Demo6 {
public static void main(String[] args) {
File file1 = new File("D://学习/b");
File file2 = new File("D://学习/a");
File file3 = new File("D://学习/file.txt");
//isDirectory()返回值boolean类型
System.out.println(file1.isDirectory());
System.out.println(file2.isDirectory());
System.out.println(file3.isDirectory());
}
}
层级文件夹和空文件夹都算层级文件夹
1.4.7 delete()
删除指定路径下普通的文件和空文件夹(非层级目录)
public class Demo7 {
public static void main(String[] args) {
//层级目录不能删除
File file2 = new File("D://学习/a");
File file3 = new File("D://学习/file.txt");
file2.delete();
file3.delete();
}
}
file.txt已经被删除
1.4.8 length()
在指定路径下,获取文件内容的大小
public class Demo8 {
public static void main(String[] args) {
File file = new File("D://学习/file.txt");
//返回值是long类型
long l = file.length();
System.out.println(l);
}
}
字节为:3
输出结果:3
1.4.9 renameTo(File dest)
修改文件的名字
public class Demo9 {
public static void main(String[] args) {
File file = new File("D://学习/file.txt");
//返回值为boolean
boolean flag = file.renameTo(new File("D://学习/123.txt"));
System.out.println(flag);
}
}
修改成功
1.4.10 listFiles()
获取指定路径的下一层路径
public class Demo10 {
public static void main(String[] args) {
File file = new File("D://学习/a");
File[] files = file.listFiles();
//输出a下层所有文件
for (int i = 0; i < files.length ; i++){
System.out.println(files[i]);
}
}
}
所以它的返回值是一个File类型的数组
1.5File 文件类的练习
1.5.1 文件存在与否的默认值问题
1.5.2 关于File类的构造方法的
1.6 FIle文件类的高级应用
递归删除文件
delete()无法直接删除层级目录,所以要想删除层级目录需要用到递归
public class Demo2 {
public static void delete1(File file){
File[] files = file.listFiles();
if (files != null && files.length != 0){
delete1(files[0]);
}
file.delete();
}
public static void main(String[] args) {
File file = new File("D://学习/a");
delete1(file);
}
}
该代码有一个弊端,每次只能删除第一个文件,如果一个文件夹里面有好几个文件无法全部删除
优化代码
public class Demo1 {
//定义一个方法
public static void delete(File file){
File[] files = file.listFiles();
if (files != null){
for (File f : files) {
delete(f);
}
}
file.delete();
}
public static void main(String[] args) {
//递归删除
File file = new File("D://学习/a");
delete(file);
}
}
总结:
二.Java中的流
2.1 流的概念
在Java中,利用程序完成对数据的读、写(存储)操作所使用的工具就是流。
2.2 解读
通过程序向文件写入数据
通过程序读取文件中的数据
2.3 流的划分
按照方向划分:
流是有方向的:
输入流(对数据的读操作),利用写好的程序,把文件中的数据读取出来(文件中数据已经存在,通过程序去读取,最终显示出来)
输出流(对数据的写操作),利用写好的程序,把程序中指定的数据写到文件中(文件中还没有数据,通过程序把指定好的数据写入文件)
按照类型来划分:
2.4 字节流
2.4.1字节输出流:
import java.io.File;
import java.io.FileOutputStream;
public class ByteOutputStream {
public static void main(String[] args) throws Exception {
//1.创建对象
//路径如果写错了,会报错
//FileNotFoundException: D:\a\file.txt (系统找不到指定的路径。)
//写入时要满足这个文件不存在,则会创建该文件,但是要保证路径正确
FileOutputStream fos = new FileOutputStream("D://学习/a/file.txt");
FileOutputStream fos1 = new FileOutputStream(new File("D://学习/a/file.txt"));
//两种定义方式都可以
//2.写入数据
//虽然是97,写进去的是a
fos.write(97);
//3.释放资源
fos.close();
}
}
数字对应的ASCII符号
三种写入方式:
public class ByteOutputStream1 {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("D://学习/a/file.txt");
/*
* 三种写入方式:
* void write(int b) 一次写一个字节数据
void write(byte[] b) 一次写一个字节数组数据
void write(byte[] b, int off, int len) 一次写一个字节数组的部分数据
参数一:数组
参数二:起始索引
参数三:个数
*
* */
fos.write(97);
//写入字节数组
byte[] b = {97,98,99,100};
fos.write(b);
//写入字节数组的一部分
fos.write(b,1,2);
//关闭资源
fos.close();
}
}
换行和续写:
写入数据时每次数据都会重置,为了保存数据,可以进行续写
public class ByteOutputStream2 {
public static void main(String[] args) throws Exception {
//打开续写开关
FileOutputStream fos = new FileOutputStream("D://学习/a/file.txt",true);
/*
* 换行:\r\n
* 写其中一个也行
*
* 续写:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件
* */
//一个write代表一次写入操作
fos.write(97);
String s = "\r\n";
byte[] b1 = s.getBytes();
fos.write(b1);
//写入字节数组
byte[] b2 = {97,98,99,100};
fos.write(b2);
//写入字节数组的一部分
fos.write(b2,1,2);
//关闭资源
fos.close();
}
}
2.4.2字节输入流:
public class ByteInputStream {
public static void main(String[] args) throws Exception {
/*
*字节输入流FileInputStream
* 读取文件中的数据
*
* */
//1.创建对象
FileInputStream fis = new FileInputStream("D://学习/a/file.txt");
//2.读取数据
//read()返回值为int类型
//读取到的是对应的十进制数字
int b1 = fis.read();
System.out.println(/*(char)*/b1);
int b2 = fis.read();
System.out.println(/*(char)*/b2);
int b3 = fis.read();
System.out.println(/*(char)*/b3);
int b4 = fis.read();
System.out.println(/*(char)*/b4);
int b5 = fis.read();
System.out.println(/*(char)*/b5);
int b6 = fis.read();
System.out.println(/*(char)*/b6);//-1
//3.释放资源
fis.close();
}
}
读取到了a之后,13和10代表换行
一共就读取了6个,十分不方便
优化:
public class ByteInputStream1 {
public static void main(String[] args) throws Exception {
//读取的时候,如果没有这个文件则会直接报错
FileInputStream fis = new FileInputStream("D://学习/a/file.txt");
//该方法可以获取能读到的所有字节的数量
int available = fis.available();
System.out.println(available);
/*
* 三种读取方式,和字节输出流一样
* void read(int b) 一次写一个字节数据
void read(byte[] b) 一次写一个字节数组数据
void read(byte[] b, int off, int len) 一次写一个字节数组的部分数据
参数一:数组
参数二:起始索引
参数三:个数
*
* */
//使用字节数组将能读取到的字节存起来
byte[] b = new byte[available];
//这里使用的是第二种,一次读取一个字节数组,效率更高
fis.read(b);
//读取完之后,会把读取到的东西赋值给b数组
//将得到的字节数据转换成字符数据
String value = new String(b);
System.out.println("读到的数据为:" + value);
//3.释放资源
fis.close();
}
}
循环遍历读取数据
public class ByteInputStream3 {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("D://学习/file.txt");
//while循环读取,read()返回值为int类型,如果为-1,则是读取不到
while ((fis.read()) != -1) {
System.out.println((char)fis.read());
}
//3.释放资源
fis.close();
}
}
2.5字节流实现拷贝操作
图片的拷贝
public class Demo {
public static void main(String[] args) throws Exception{
long start = System.currentTimeMillis();
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\1.jpg");
FileOutputStream fos = new FileOutputStream("D:\\2.jpg");
//2.拷贝
//核心思想:边读边写
int b;
while((b = fis.read()) != -1){
fos.write(b);
}
//3.释放资源
//规则:先开的最后关闭
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
如果文件过大,一个字节一个字节的拷贝速度太慢了
public class Demo1 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\1.jpg");
FileOutputStream fos = new FileOutputStream("D:\\2.jpg");
//2.拷贝
int len;
//一次拷贝一个字节数组,这和后面的字符流有点儿像,提高效率
byte[] bytes = new byte[1024 * 1024 * 5];
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
//3.释放资源
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
2.6 字符集问题
不同编码集,转换形式都不一样
一旦编码和解码的方式不统一,就会出现乱码问题
乱码问题的解决
public class Demo {
public static void main(String[] args) throws UnsupportedEncodingException {
/**
Java中编码的方法
public byte[] getBytes() 使用默认方式进行编码
public byte[] getBytes(String charsetName) 使用指定方式进行编码
编码:将认识的内容变成你不认识的内容(由字符串到字节)
Java中解码的方法
String(byte[] bytes) 使用默认方式进行解码
String(byte[] bytes, String charsetName) 使用指定方式进行解码
解码:将不认识的内容变成你认识的内容(由字节到字符串-字节电报破译)
*/
//1.编码
String str = "ai你哟";
byte[] bytes1 = str.getBytes();
System.out.println(Arrays.toString(bytes1));// public byte[] getBytes() 使用默认方式进行编码
byte[] bytes2 = str.getBytes("GBK");//public byte[] getBytes(String charsetName) 使用指定方式进行编码
System.out.println(Arrays.toString(bytes2));
//2.解码
String str2 = new String(bytes1); //String(byte[] bytes) 使用默认方式进行解码
System.out.println(str2);
//String str3 = new String(bytes1,"GBK"/*编码和解码不一致*/);
// String(byte[] bytes, String charsetName) 使用指定方式进行解码
/* String str3 = new String(bytes1,"UTF-8");*/
String str3 = new String(bytes2,"UTF-8");
System.out.println(str3);
}
}
三.字符流
3.1字符输入流
public class Demo {
public static void main(String[] args) throws Exception{
/**
* 需求:利用字符流完成指定路径下数据的读取
第一步:创建对象
public FileReader(File file) 创建字符输入流关联本地文件
public FileReader(String pathname) 创建字符输入流关联本地文件
第二步:读取数据
public int read() 读取数据,读到末尾返回-1
public int read(char[] buffer) 读取多个数据,读到末尾返回-1
第三步:释放资源
public void close() 释放资源/关流
*/
//1.创建对象并关联本地文件
FileReader fr = new FileReader("D:\\file.txt");
//2.读取数据 read()
//字符流的底层也是字节流,默认也是一个字节一个字节的读取的。
//如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节
//read()细节:
//1.read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
//2.在读取之后,方法的底层还会进行解码并转成十进制。
// 最终把这个十进制作为返回值
// 这个十进制的数据也表示在字符集上的数字
// 英文:文件里面二进制数据 0110 0001
// read方法进行读取,解码并转成十进制97
// 中文:文件里面的二进制数据 11100110 10110001 10001001
// read方法进行读取,解码并转成十进制27721
// 我想看到中文汉字,就是把这些十进制数据,再进行强转就可以了
int ch;
while((ch = fr.read()) != -1){
/* System.out.print(ch);//输出数字的值*/
System.out.print((char)ch);//将数字转化为字母
}
//3.释放资源
fr.close();
}
}
字符输入流底层已将字符集转换好了,所以字符集可以不管编码
也可以按照数组读取提高效率
public class Demo2 {
public static void main(String[] args) throws Exception{
//1.创建对象
FileReader fr = new FileReader("D:\\a.txt");
//2.读取数据
char[] chars = new char[2];
int len;
//read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
//空参的read + 强转类型转换
while((len = fr.read(chars)) != -1){
//把数组中的数据变成字符串再进行打印
System.out.print(new String(chars,0,len));
}
//3.释放资源
fr.close();
}
}
3.2字符输出流
public class Demo1 {
public static void main(String[] args) throws IOException {
/*
第一步:创建对象
public FileWriter(File file) 创建字符输出流关联本地文件
public FileWriter(String pathname) 创建字符输出流关联本地文件
public FileWriter(File file, boolean append) 创建字符输出流关联本地文件,续写
public FileWriter(String pathname, boolean append) 创建字符输出流关联本地文件,续写
第二步:读取数据
void write(int c) 写出一个字符
void write(String str) 写出一个字符串
void write(String str, int off, int len) 写出一个字符串的一部分
void write(char[] cbuf) 写出一个字符数组
void write(char[] cbuf, int off, int len) 写出字符数组的一部分
第三步:释放资源
public void close() 释放资源/关流
*/
FileWriter fw = new FileWriter("D:\\a.txt",true);
fw.write(25105);//我
fw.write("你好啊???");
char[] chars = {'a','b','c','我'};
fw.write(chars);
fw.close();
}
}
写入数据
四.字符流原理:
4.1字符输入流原理
每次都尽可能的填满缓冲区
让第8913个数据为b
public class Demo {
public static void main(String[] args) throws Exception{
/*//1.创建对象
FileWriter fileWriter = new FileWriter(new File("D://a.txt"));
for (int i = 0; i < 8193; i++) {
//2.写入数据
fileWriter.write("a");
}
//3.关闭桥梁
fileWriter.close();*/
FileReader fileReader = new FileReader(new File("D://a.txt"));
for (int i = 0; i < 8192; i++) {
//2.读入数据
int read = fileReader.read();
System.out.println((char)read);
}
System.out.println("=============");
//在8192之外的数据开始,会把之前的缓冲区数组从头开始赋值
fileReader.read();
fileReader.close();
}
}
在8192之外的数据开始,会把之前的缓冲区数组从头开始赋值
4.2字符输出流原理
public class Demo {
public static void main(String[] args) throws Exception{
FileWriter fw = new FileWriter(new File("D://b.txt"));
for (int i = 0; i < 8192; i++) {
fw.write(97);
}
fw.flush();
//flush是刷新缓冲区的作用
//哈哈能被写入
fw.write("哈哈");
fw.close();
//这里会报错,因为通道已经关闭了,这里再继续写的话就会报错
fw.write("呵呵");
}
}
五.字节流和字符流的应用
5.1文件的拷贝
将a文件夹拷贝给b
public class Test01 {
public static void main(String[] args) throws IOException {
//拷贝一个文件夹,考虑子文件夹
//1.创建对象表示数据源
File src = new File("D:\\学习\\src");
//2.创建对象表示目的地
File dest = new File("D:\\学习\\dest");
File dest1 = new File(dest,"哈哈哈");
dest1.mkdirs();
//3.调用方法开始拷贝
copydir(src,dest);
}
/*
* 作用:拷贝文件夹
* 参数一:数据源
* 参数二:目的地
*
* */
private static void copydir(File src, File dest) throws IOException {
dest.mkdirs();
//递归
//1.进入数据源
File[] files = src.listFiles();
//2.遍历数组
for (File file : files) {
if(file.isFile()){
//3.判断文件,拷贝
FileInputStream fis = new FileInputStream(file);
//new File(dest,file.getName()) 在dest的子文件夹下创建file.getName()的名称的子文件夹
FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}else {
//4.判断文件夹,递归
copydir(file, new File(dest,file.getName()));
}
}
}
}
5.2文件的加密
public class Demo {
public static void main(String[] args) throws Exception{
//可以利用^完成加密、解密,相同为0,不同为1
//两个反过来就是解密
FileInputStream fileInputStream = new FileInputStream("D:\\2.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("D:\\3.jpg");
//加密
int num ;
//num = fileInputStream.read()每次都要循环
while((num = fileInputStream.read()) != -1){
fileOutputStream.write(num ^ 2);
}
fileOutputStream.close();
fileInputStream.close();
}
}
5.3先读,之后排序,再写入
public class Demo {
public static void main(String[] args) throws Exception{
//先读数据
FileReader fileReader = new FileReader(new File("D://c.txt"));
//创建StringBuilder对象
StringBuilder sb = new StringBuilder();
/* String s = "";*/
int num;
while((num = fileReader.read()) != -1){
/*System.out.print((char)num);*/
/*s = s + (char)num ;*/
sb.append((char)num);
}
/*String[] split = s.split("-");*/
//将StringBuilder变成字符串
String str = sb.toString();
String[] split = str.split("-");
ArrayList<Integer> arrayList = new ArrayList<>();
for (String s : split){
arrayList.add(Integer.parseInt(s));
}
//集合排序
Collections.sort(arrayList);
System.out.println(arrayList);
//写入数据
FileWriter fileWriter = new FileWriter(new File("D://c.txt"));
for (int i = 0; i < arrayList.size(); i++){
if (i != arrayList.size() - 1){
//不能直接写Integer,要先变一下类型
fileWriter.write(arrayList.get(i) + "-");
}else {
fileWriter.write(arrayList.get(i) + "");
}
}
fileWriter.close();
fileReader.close();
}
}