• 文件系统相关知识与IO


    硬盘及内存的区别

    在系统中,我们的文件都是存储在硬盘上的,我们经常听到“硬盘”和“内存”两词,两者虽然都是用于存储数据,但有区别:

    • 内存的读取速度快,硬盘读取速度慢
    • 内存的存储空间小,硬盘的存储空间大
    • 若断电后内存中的数据将会消失,硬盘中的数据不会消失
    • 内存的制作成本更高,硬盘的制作成本更低一些 

    文件相关知识

    文件的获取

    文件是存储在硬盘中的,我们要获取文件,就需要通过java中的File类,当然,在正式学习前,应该再了解一下相关知识

    绝对路径和相对路径

    绝对路径:某文件被包含的所有路径,以盘符(也就是D盘,C盘等)为开头的就是绝对路径。如我想要打开D盘test文件夹内的的test1文件夹内的test2文件夹内的一个“hello.txt”文件:D:\test\test1\test2

     相对路径:文件以某文件为参考物,相对在哪个路径上。

    注意:虽然平时windows系统上的打开某文件夹用的是 “\” 反斜杠,但其实普遍情况下大家在表示打开文件夹时使用的是 “/” 正斜杠,只是当年最初的时候DOS的产品经理临时做了一个违背祖宗的决定——将大家平时都在使用的正斜杠/改为反斜杠\。产品上线后大家都在说不行,使用才改为了正斜杠反斜杠都可以,但我们平时在java中使用打开什么路径一般还是使用正斜杠“/”,否则如果我们要使用反斜杠,要经过反义才能使用,例如在编译器中使用正斜杠版本:D:/test/test1/test2;若使用反斜杠版本则变为了:D:\\test\\test1\\test2;

    工作目录

    工作目录就是我们java程序默认在上面进行一系列操作的地方,就是我们项目所在的位置。要知道目前工作目录位置,我们可以这么操作:

     通常对工作路径的使用,我们要结合相对路径,一般从工作路径开始,我们需要用“ ./ ”来开头。

    File srcFile = new File("./src/test");  //打开目前工作路径的src文件夹中的test文件夹

    文件获取

    为了找到文件,我们可以使用Java中的File类,来进行获取文件名字,判断是否为文件或是文件夹等操作。

    1. import java.io.File;
    2. import java.io.IOException;
    3. /**
    4. * Created with IntelliJ IDEA.
    5. * Description: 在这里练习IO文件操作
    6. * User: Lenovo
    7. * Date: 2022-11-02
    8. * Time: 20:43
    9. */
    10. public class demo1 {
    11. public static void main(String[] args) throws IOException {
    12. // File file = new File("F:\\java练习专用\\java\\20221102\\src/hello.txt");
    13. File file = new File("src/test"); //要看目前的工作路径
    14. System.out.println(file.exists()); //判断该文件是否存在
    15. System.out.println(file.getName()); //获取名字
    16. System.out.println(file.getPath()); //获取相对路径
    17. System.out.println(file.getCanonicalPath()); //获取优化后的绝对路径
    18. System.out.println(file.getAbsolutePath()); //获取绝对路径
    19. File[] arr = file.listFiles(); //将目前文件里面的所有文件提取出来
    20. }
    21. }

    IO操作

    我们平常所说的IO,也就是输入输出流,全称为InputputStream和OutputStream。

    因为文件都是存储在硬盘中的,因为我们无法直接对硬盘进行操作,所以要对文件进行读写操作,我们需要通过InputStream类和OutputStream类在内存中间接地对文件进行读和写的操作,具体示意图如下:

     像inputStream这类操作硬盘数据的东西我们统称为“Handler”

    IO细分

    在IO中,我们也分为字符流和字节流

     InputStream字节流

    InputStream类由于是抽象类,必须得由其子类来实例化,所以我们创建一个InputStream示例应该用FileinputStream类。

    InputStream inputStream = new FileInputStream("src/hello.txt");

    inputStream类的最主要方法为read(),read有三个版本

     read()版本:全部读完

    read(byte[] b)版本:读多少个字节

    read(byte[] b, int off, int len)版本:从第几个字节开始读,读len个字节

    我们要特别注意,在读文件之后,要注意关闭文件,也就是在操作完该文件后,我们要使用close()函数

    这是因为在进程的PCB中,会有一个属性,名字为“文件描述符表”,该表是一个顺序表,里面放着的就是一个个这样子的文件类型,其下标对应成为“文件描述符”。

    我们每创建一个文件类型,就会占文件描述符表的一个位置,而放满之后就文件描述符表就不会再一次扩容了,若我们要创建一个新的文件IO类型来操作,会失败,所以在每次使用完后,要注意使用close()函数来关闭该文件,也就是将该文件在文件描述符表中删除。

    InputStream的使用

    关于InputStream类型的使用,我们可以到Scanner类等,详情看下面代码:

    1. import java.io.*;
    2. public class demo4 {
    3. public static void main(String[] args) throws IOException {
    4. // File file = new File("src/hello.txt");
    5. InputStream inputStream = new FileInputStream("src/hello.txt");
    6. byte[] arr = new byte[1024];
    7. int len = inputStream.read(arr);
    8. String s = new String(arr,0,len,"utf-8");
    9. for (int i = 0; i < len; i++) {
    10. System.out.println(arr[i]);
    11. }
    12. System.out.println(s);
    13. inputStream.close();
    14. }
    15. }
    1. import java.io.*;
    2. import java.util.Scanner;
    3. /**
    4. * Created with IntelliJ IDEA.
    5. * Description:
    6. * User: Lenovo
    7. * Date: 2022-11-02
    8. * Time: 21:39
    9. */
    10. public class demo5 {
    11. public static void main(String[] args) throws IOException {
    12. //使用该方法可以直接在try中完成后将inoutStream关闭
    13. try(InputStream inputStream= new FileInputStream("src/hello.txt")){
    14. byte[] arr = new byte[1024];
    15. Scanner scan = new Scanner(inputStream);
    16. String s = new String(scan.next());
    17. System.out.println(s);
    18. }catch(IOException e) {
    19. }
    20. }
    21. }

    注意,在try中创建流对象可以在try内的代码块完成后自动关闭,我们也可以通过分号在try内创建多个流对象,或者try嵌套来创建多个流对象。

    OutputStream字节流

    在我们每次写完文件后,记得加一个.flush()函数,这是因为在操作系统中IO通常有些费时间,所以为了减少IO,系统会先将我们要输入的数据放入“缓冲区”先,等这个缓冲区满了,再写入,但是这样子有可能导致我们的一部分数据未写入,所以每次在写完数据后最好都使用flush。

    OutputStream的使用方法其实和InputStream的使用方法差不多,详细使用看下面的代码:

    1. import java.io.FileOutputStream;
    2. import java.io.IOException;
    3. import java.io.OutputStream;
    4. public class demo6 {
    5. public static void main(String[] args) throws IOException {
    6. try(OutputStream os = new FileOutputStream("src/hello.txt")){
    7. String s = "郑小绿";
    8. byte[] arr = s.getBytes("utf-8");
    9. os.write(arr);
    10. os.flush();
    11. }
    12. }
    13. }
    1. import java.io.FileOutputStream;
    2. import java.io.IOException;
    3. import java.io.OutputStream;
    4. public class demo6 {
    5. public static void main(String[] args) throws IOException {
    6. try(OutputStream os = new FileOutputStream("src/hello.txt")){
    7. String s = "郑小绿";
    8. // byte[] arr = s.getBytes("utf-8");
    9. byte[] arr = {'a','b','c','d'};
    10. os.write(arr);
    11. os.flush();
    12. }
    13. }
    14. }
    1. import java.io.FileOutputStream;
    2. import java.io.IOException;
    3. import java.io.OutputStream;
    4. public class demo6 {
    5. public static void main(String[] args) throws IOException {
    6. try(OutputStream os = new FileOutputStream("src/hello.txt")){
    7. String s = "郑小绿";
    8. // byte[] arr = s.getBytes("utf-8");
    9. byte[] arr = {'a','b','c','d'};
    10. os.write(arr,1,2);
    11. os.flush();
    12. }
    13. }
    14. }

    Reader字符流读文件

    除了InputStream字节流读文件外,我们还可以使用Reader来读取字符文件。具体使用方法如下:

    1. import java.io.*;
    2. import java.util.Scanner;
    3. /**
    4. * Created with IntelliJ IDEA.
    5. * Description:
    6. * User: Lenovo
    7. * Date: 2022-11-02
    8. * Time: 21:39
    9. */
    10. public class demo5 {
    11. public static void main(String[] args) throws IOException {
    12. //使用1
    13. Reader reader = new FileReader("src/hello.txt");
    14. char[] arr = new char[1024];
    15. int len = reader.read(arr);
    16. System.out.println(len);
    17. reader.close(); //要记得关闭文件
    18. //使用2
    19. // Scanner scan = new Scanner(reader);
    20. // String s = scan.next();
    21. // System.out.println(s);
    22. // reader.close(); //要记得关闭文件
    23. //使用3
    24. // Reader reader = new FileReader("src/hello.txt");
    25. // char[] arr = new char[1024];
    26. // int len = reader.read(arr);
    27. // String s = new String(arr,0,len);
    28. // for (int i = 0; i < len; i++) {
    29. // System.out.println(arr[i]);
    30. // }
    31. // System.out.println(s);
    32. // reader.close(); //要记得关闭文件
    33. //使用4
    34. // Scanner scan = new Scanner(reader);
    35. // String s = scan.next();
    36. // System.out.println(s);
    37. // reader.close(); //要记得关闭文件
    38. }
    39. }

    Writer字符流写文件

    和OutPutStream差不多

    1. import java.io.FileOutputStream;
    2. import java.io.IOException;
    3. import java.io.OutputStream;
    4. public class demo6 {
    5. public static void main(String[] args) throws IOException {
    6. try(OutputStream os = new FileOutputStream("src/hello.txt")){
    7. String s = "郑小绿";
    8. // byte[] arr = s.getBytes("utf-8");
    9. byte[] arr = {'a','b','c','d'};
    10. os.write(arr,1,2);
    11. os.flush();
    12. }
    13. }
    14. }

    PrintWriter

    在这里着重介绍PrintWriter,在创建完该类后,我们可以像平常使用System.out.println来写入文件,具体使用如下代码:

    1. import java.io.*;
    2. public class demo7 {
    3. public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
    4. OutputStream os = new FileOutputStream("src/hello.txt");
    5. OutputStreamWriter outputStreamWriter = new OutputStreamWriter(os,"utf-8");
    6. PrintWriter printWriter = new PrintWriter(outputStreamWriter);
    7. printWriter.print("郑小绿 ");
    8. printWriter.println("超6");
    9. printWriter.println("宇宙无敌牛");
    10. printWriter.flush();
    11. }
    12. }

    对读写文件的实操

    实操①:找到指定文件并删除

    要求:找到某文件,并让用户选择是否删除

    代码:

    1. import java.io.File;
    2. import java.io.FileInputStream;
    3. import java.io.IOException;
    4. import java.io.InputStream;
    5. import java.util.Scanner;
    6. /**
    7. * Created with IntelliJ IDEA.
    8. * Description:
    9. * User: Lenovo
    10. * Date: 2022-11-03
    11. * Time: 0:23
    12. */
    13. public class demo8 {
    14. public static void scanFile(File srcFile,String del) {
    15. for (File f: srcFile.listFiles()) {
    16. if(f==null){
    17. return;
    18. }
    19. if(f.isDirectory()){
    20. scanFile(f,del);
    21. }
    22. if(f.isFile()){
    23. try {
    24. System.out.println(f.getCanonicalPath());
    25. tryDel(f,del);
    26. } catch (IOException e) {
    27. e.printStackTrace();
    28. }
    29. }
    30. }
    31. }
    32. public static void tryDel(File f,String del) {
    33. if(f.getName().contains(del)){
    34. System.out.println("已发现该文件,是否删除?Y/N");
    35. Scanner scan = new Scanner(System.in);
    36. String choose = scan.next();
    37. if(choose.equals("Y")){
    38. f.delete();
    39. }
    40. }
    41. }
    42. public static void main(String[] args) throws IOException {
    43. System.out.println("请输入你要查询的路径:");
    44. Scanner scan = new Scanner(System.in);
    45. String srcFile = scan.next();
    46. File file = new File(srcFile);
    47. if(!file.exists()){
    48. System.out.println("输入的路径不正确或不存在");
    49. return;
    50. }
    51. System.out.println("请输入你要删除的文件名字:");
    52. String delFile = scan.next();
    53. scanFile(file,delFile);
    54. }
    55. }

    实操②:对文件进行复制

    要求:选择某文件,然后复制到某指定路径内

    代码:

    1. import java.io.*;
    2. import java.util.Scanner;
    3. /**
    4. * Created with IntelliJ IDEA.
    5. * Description:
    6. * User: Lenovo
    7. * Date: 2022-11-03
    8. * Time: 9:53
    9. */
    10. public class demo9 {
    11. public static void main(String[] args) throws IOException {
    12. System.out.println("请输入你要复制的文件路径:");
    13. Scanner scan = new Scanner(System.in);
    14. String src = scan.next();
    15. File srcFile = new File(src);
    16. if(!srcFile.exists()){
    17. System.out.println("该路径错误或不存在");
    18. return;
    19. }
    20. if(srcFile.isDirectory()){
    21. System.out.println("输入的路径为目录,错误");
    22. return;
    23. }
    24. System.out.println("请输入你需要复制到的路径(包含名字):");
    25. String copyPath = scan.next();
    26. try(InputStream inputStream = new FileInputStream(srcFile)){
    27. try(OutputStream os = new FileOutputStream(copyPath)){
    28. // byte[] buf = new byte[1024];
    29. // int len = inputStream.read(buf);
    30. // while(len!=-1){
    31. // len = inputStream.read(buf);
    32. // os.write(buf,0,len);
    33. // os.flush();
    34. // }
    35. // byte[] buf = new byte[1024];
    36. while(true){
    37. byte[] buf = new byte[1024];
    38. int len = inputStream.read(buf);
    39. if(len==-1){
    40. break;
    41. }
    42. os.write(buf);
    43. os.flush();
    44. }
    45. }
    46. }
    47. }
    48. }

    在这里我们要注意,不使用注释内的代码原因是因为如果按照上面的代码进行读取,若第一次buf都没有读满,则很可能会导致程序多读了数据,导致文件出错。

  • 相关阅读:
    1. 带你玩转Java之Java基本概括
    【可靠性测试】什么是可靠性测试:定义、方法和工具
    C语言练习题解析:挑战与突破,开启编程新篇章!(4)
    华秋DFM从2.1.6升级到3.x版本出现的问题
    QT配置MySQL数据库 && ninja: build stopped: subcommand failed
    ROS vscode开发,故障解决
    langchain主要模块(五):Agent以及Wandb
    Android提供了多种方式来打开特定文件夹中的视频
    基于SSH开发网上室内家具销售系统
    QIBOX1-014-栏目的调用2
  • 原文地址:https://blog.csdn.net/Green_756/article/details/127666018