目录
本质介绍File方法的网页
文件 (Java Platform SE 8 ) (oracle.com)
使用Java操作文件(File),主要操作结构
getAbsolutePath() 方法 保留一模一样的输入路径
getCanonicalPath() 方法 保留有意义,最简洁的路径
如果想得到标准的父目录方法:
为什么这里尾部用File的方法而不是之前的Path 一个返回类型是 File 一个是 String
原因:
如果目录为空则打印就为空,如果不是目录,则打印null
注释:如果为null就不用用foreach打印了,用toString
深度优先遍历
// 1. 深度优先的遍历
private static void traversalDepthFirst(File dir) throws Exception {
// 1. 找到这个目录下的所有孩子
File[] files = dir.listFiles();
if (files == null) {
return;
}
// 2. 针对每个孩子,判断是目录还是文件
for (File file : files) {
if (file.isDirectory()) {
// 如果确定是个目录,则继续递归去遍历处理
System.out.println("[D] " + file.getCanonicalPath());
traversalDepthFirst(file);
} else {
System.out.println("[F] " + file.getCanonicalPath());
}
}
}
层序遍历
// 2. 广度优先的遍历
private static void traversalBroadFirst(File dir) throws Exception {
// 使用队列进行层序优先的遍历
Queue<File> queue = new LinkedList<>();
queue.offer(dir);
while (!queue.isEmpty()) {
File poll = queue.poll();
if (poll.isDirectory()) {
System.out.println("[D] " + poll.getCanonicalPath());
} else {
System.out.println("[F] " + poll.getCanonicalPath());
}
if (poll.isDirectory()) {
File[] files = poll.listFiles();//取出当前目录下的所有孩子添加到队列中
if (files == null) {
continue;
}
for (File file : files) {
queue.offer(file);
}
}
}
}
重点方法
跟之前弄的创建文件区别
注释:如果目录不存在,可以用mkdirs()
src后面的目录都是程序创建的,而且是只会创建目录,就算你输入的是hello.txt 也只是目录名称叫这个而已
注释:修改后的路径是别的地方也可以,renameTo 相当于 剪切效果
File类常见方法总结
JDK只提供针对节点的操作,其他都是在此基础上衍生的。
相对路径
不是一个时间段,甚至不是一个进程里面
如果输入的是相对路径,让flie打印绝对路径会出现什么?
因为前面的这部分是这个项目的路径,默认就是这个。
如果我们手动改变的话,输出的路径也就会被改变。
在什么路径下运行,相对路径前面就打印什么路径
第三个是工作目录
注释:只有相对路径才考虑启动位置,绝对路径是不用考虑。
---------至此File 就讲完了
关于 内容 的读写
1.关于读
1).直接读取 (以二进制数据的方式读取,表现在代码中byte为单位)
2)文本读取
举例:
数着是26字符,显示却是28是因为 换行也算
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
public class Domo3 {
public static void main(String[] args)throws Exception {
InputStream is = new FileInputStream("hello.txt");
// FileInputStream 可以赋值给 InputStream,是因为 有继承关系 && FileInputStream 是 InputStream 的下级类
// 准备好一个水桶
byte[] buf = new byte[1024]; // 1024 代表能接 1024 滴(字节)水,我们准备好桶的容量
// 拿着准备好的桶去水龙头接水
int n = is.read(buf);
// 这里的 n 代表这次真正接到了多少滴(字节)水
// n 一定小于等于 1024 && n >= 0
// System.out.println(n); // 28
// 真正的数据放在 buf 从 [0, 28)
byte[] bytes = Arrays.copyOf(buf, n);
for (byte b : bytes) {
System.out.printf("%02x\n", b);
}
System.out.println("-----------");
System.out.println(n);
is.close();
}
}
一共28个数据 以2进制打印 采用的是ASCLL码值
接水代码
import java.io.FileInputStream;
import java.io.InputStream;
public class Domo4 {
public static void main(String[] args)throws Exception {
InputStream is = new FileInputStream("hello.txt");
while(true){
int data = is.read();//一滴一滴劫
if(data == -1){
// 所有数据都被读完了
break;
}
byte b = (byte) data;
System.out.printf("%02x --- %c\n",b ,b);
}
is.close();//资源回收
}
}
一次一桶一桶的接水
import java.io.FileInputStream;
import java.io.InputStream;
public class Domo5 {
public static void main(String[] args)throws Exception {
InputStream is = new FileInputStream("hello.txt");
byte[] buf = new byte[5];
while (true){
int n = is.read(buf);//n返回值是这次读取到的有效数据个数
// n == 0 只是本次没数据,以后还有
// n == -1 本次没数据,以后也没数据了
if(n == -1){
//代表所有数据读完
break;
}
for (int i = 0; i < n; i++) {
byte b = buf[i];
System.out.printf("%02x ---- %c\n",b,b);
}
}
is.close();
}
}
try-with-resources 语法
try的语法糖 - 为了我们方便书写整洁
我们之前写的JDBC也可以简便的写
上面讲的要点:
处理文本数据(字符数据)
查表法
每个数字都对应一个字符
ASCLL 和 Unicode
ASCLL 一个字节: 实际使用中就直接使用即可
Unicode 4个字节 :如果真的每个字都用4个字节,就有很多空间浪费,所以就产生了不同的编码规则
我们现在用的大部分是采样 UTF-8 这样的编码规则,一个字符占用的空间大小看输入的字符是什么类型再做决定
乱码的问题
解读过程:当我们存入一个字符 '我' 时,对应的unicode 是 25105(都是假设的),然后以16进制进行保存,当我们读取时。假设我们事先并不知道这个数据是以什么方式保存的,然后我们采用了UTF - 8 编码格式进行解读,得出 19371然后去unicode里找对应的值,这样就会造成大量的错误信息。一般来说乱码是很难查的。
举例:
import java.io.FileInputStream;
import java.io.InputStream;
public class Domo7 {
public static void main(String[] args) throws Exception {
try (InputStream is = new FileInputStream("./hello.txt")) {
byte[] buf = new byte[1024];
int n = is.read(buf);
// System.out.println(n);
// for (int i = 0; i < n; i++) {
// System.out.printf("%02x ", buf[i]);
// }
String s = new String(buf, 0, n, "UTF-8");//正常读写
String s1 = new String(buf, 0, n, "GBK");//错误读写
System.out.println(s);
System.out.println("--------------");
System.out.println(s1);
}
}
}
不一样的读写格式,就会造成乱码
小知识:
我们平常写的 Scanner sc = new Scanner(System.in);
而下面这个Scanner 方法就可以让我们自己选择读取的字符集是哪种
public static void main(String[] args) throws Exception {
try (InputStream is = new FileInputStream("./hello.txt")) {
try (Scanner scanner = new Scanner(is, "UTF-8")) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine(); // 默认去掉了换行符
System.out.println("|" + line + "|");
}
}
}
}
源头是:InputStream
所以以后Scanner里面其实可以写 InputStream 类型
小知识:终止 scanner.hasNext() 永久停止
输入 ctrl + d
写OutputStream 输出
类比到计算机内容,想读取内存里面的内容并不是一个一个字符读取的,而是先在内存中分配到了一块空间(缓冲区),把你想要的内容先存到缓冲区中,到时候一次去读,这样可以减少读写的次数,增加效率
注释:读取内存数据到外面称 刷盘
进行刷盘的时刻:
注释:一般关闭之前,统一做一次手动刷盘
确保缓冲区(buffer)里面没有残留的数据
举例:进行刷盘操作
// OutputStream -> FileOutputStream
// 所以很少用 file.createNewFile()
// 如果文件之前不存在,则会进行创建(创建可能失败: 1. 权限、2. 路径上的目录还不存在)
// 如果文件之前存在,会清空之前的文件内容,重新写入
创建了个world.txt的文件
import java.io.FileOutputStream;
import java.io.OutputStream;
public class Domo9 {
public static void main(String[] args) throws Exception {
// OutputStream -> FileOutputStream
// 所以很少用 file.createNewFile()
// 如果文件之前不存在,则会进行创建(创建可能失败: 1. 权限、2. 路径上的目录还不存在)
// 如果文件之前存在,会清空之前的文件内容,重新写入
try (OutputStream os = new FileOutputStream("./world.txt")) {
os.write(0x20);
os.write(0x0d); // '\r'
os.write(0x0a); // '\n'
// 空格// 0xe6 0x88 0x91 是 "我" 的 UTF-8 编码的字节序列
// os.write(0xe6);
// os.write(0x88);
// os.write(0x91);
os.write(0x65);
os.write(0x65);
os.write(0x65);
os.flush(); // 确保把缓冲区内可能遗留的数据全部写入 Output 设备中
}
}
}
注释:上面的字符都是一个一个进行写入
一次性写入多个方法---用数组 或者也可以只写入数组里面的部分内容
public class Domo10 {
public static void main(String[] args) throws Exception {
try (OutputStream os = new FileOutputStream("./world.txt")) {
byte[] buf = { 0x65, 0x65, 0x20, 0x65, 0x0d, 0x0a, (byte)0xe6, (byte)0x88, (byte)0x91 };
// os.write(buf); // 整个 buf 的数据全部写入
os.write(buf, 2, 4); // 整个 buf 的数据全部写入
os.flush();
}
}
}
上面发现用字符来输入太麻烦了,其实可以用String类
Writer抽象类 和 它的 实现类 OutputStreamWriter
实现: write里面就可以写入字符串了
PrintWriter实现类就更加方便了,里面有方法println 和 printf ...等等我们比较熟悉
总结