编辑
哈喽,大家好~我是保护小周ღ,本期为大家带来的是 Java 文件操作,理解文件的概念以及,常用的操作文件的类和方法, FileInputStream 类 和 FileOutputStream , PrintWriter and Scnner, Reader and Wirter 确定不来看看嘛~更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘
编辑
一、文件的基本概念
1.1 为什么使用文件
在我们程序中输入的数据一般会随着 main() (主线程)—— 进程的结束而清空,这是因为此时的数据是存放在内存中的,程序结束,内存回收,等我们下次再次运行程序的时候又需要重新输入数据,如果我们想要将数据持续化存储,就必须依托于第三方介质,依托于硬盘,依托于数据库等等Windows中对硬盘中信息的管理和使用是以文件为单位的,Windows系统中推行万物皆文件的模式,这样对整体数据的存储、管理就有了一个统一的规范,降低了管理的复杂度。
1.2 什么是文件
文件可以认为是操作系统使用、管理数据的基本单位,是硬盘存储和管理数据的基本单位,我们想要某些相关联的数据作为一个独立的整体的进行保存,这个独立的整体就可以认为是一个文件。
百度百科:
文件后缀名也叫文件的扩展名,是用来表示某种文件所采用的机制。文件扩展名是加在主文件名后面的,用“.”分隔。不同的软件要求不同的文件格式,后缀名可以帮助用户了解文件是应该使用哪种软件打开文件。
在程序设计中从文件功能的角度分析有两种文件:程序文件,数据文件。
程序文件:包括我们编写的目标源文件(. java, . C), 目标文件 (windows环境后缀为 .obj), 可执行文件(windows环境后缀为 . exe)。我们的 . java 文件经过编译后得到一个 .class 文件,由Java JVM 解析文件。
数据文件:文件的内容不一定是程序,还可以是程序运行中读写的数据,例如程序运行时需要从内存(CPU 只能直接对内存的数据进行处理,所以要处理的数据会先从第三方介质:硬盘等读取到内存)中读取数据进行处理,将处理后的数据又写回第三方介质,那么这个过程中读取和写入数据的源头就可以称之为数据文件。
根据数据的类型可以将文件划分为 文本文件 和 二进制文件
文本文件:是指以ASCII字符集方式(也称文本方式)存储的文件,依托于某种字符集来识别,C语言中使用的是ASCll 编码,所以一个 char 类型只占 一个字节, 例如: 大写字符 A 的ASCll 编码 0110 0001 (65),Java 中使用的是 Unicode 字符集 一个char 类型两个字节,可以用来描述更多的字符,也包括中文,Unicode 字符集兼容 Utf -8 字符集,utf- 8 兼容 ASCll 字符集 但是 utf- 8 使用三个字节描述中文。
二进制文件(binary):二进制文件是按机器(即电脑)能够阅读的格式(只有0和1)进行存储的文件,所有文件都是以二进制的形式存储,文本文件不同是,它可以使用字符集来解析二进制,二进制文件反正操作系统能解析,正常人都是看不懂的,如果使用某种字符集解析(utf-8)大概率也是无法成功的,编码的规则不同,自然就无法显示,即使显示了字符,也是偶然适配了字符,是没有意义的。
以下是一个二进制文件使用 utf-8 字符集解析的信息,没有意义,但是系统可是识别出信息。
编辑
小正方块就是无法被字符集识别统一显示的信息。
1.3 文件的组织
伴随着文件越来越多,就需要一种管理文件的机制,在 Windows 系统中采用树型结构来组织管理文件夹。文件除了有数据内容之外,还有文件本身的一个信息,例如:文件名、文件类型、文件大小,文件路径、文件源地址,我们把这一块这信息称之文件的 “元信息”。
编辑
每一个文件夹对应硬盘上一块存储空间,它提供了指向对应空间的地址——子文件,可以把文件夹看作是一颗树的 Node 节点,节点中可以保存另一个节点或者是文本文件,二进制文件等等(实际上存储的是文件的元信息)。
文件夹是计算机术语也称之为 “目录” 是用来组织和管理磁盘文件的一种数据结构。
1.4 文件路径(Path)
熟悉电脑的朋友应该,Windows 不允许在同一文件下有相同类型并同名的文件。
文件命名规则:
(1)文件名最长可以使用255个字符。
(2)可以使用扩展名,扩展名用来表示文件类型,也可以使用多间隔符的扩展名。如 txt.jpg.exe是一个合法的文件名,但其文件类型由最后一个扩展名决定。
(3)文件名中允许使用空格,但不允许使用下列字符(英文输入法状态):< > / \ | : " * ?
(4)windows系统对文件名中字母的大小写在显示时有不同,但在使用时不区分大小写。
如何在文件管理系统中定位我们的唯一的文件?Windows 中不同文件夹中允许已使用的文件名,如果我们需要对文件精确定位,就需要使用文件路径了。
所谓文件路径就是从树形结构的角度来看,从根节点开始,逐渐向子节点延申,直到找到目标文件,那么找到该文件的所经过的节点,就称之为文件路径,从根节点开始扫描这种描述方式也被称之为文件的 “绝对路径”。
举个例子:D:\JAVA\JDK\bin\jave.exe 这是一个绝对路径
编辑
除了使用绝对路径定位文件,还有一种相对路径的方式,相对表示根据当前文件为根节点直至目标文件的路径,相对路径的表示方式也是我们常用的,缺点是文件路径不过精确,有时候会识别不到文件。
" . " 符号在相对路径中是一个特殊符号,表示当前目录
" .. " 符号也是特殊符号,表示当前目录中的上级目录
在相对路径的情况下要描述当前目录的上上级目录,或者级别更高的目录时,可以使用 “..\ ..\ ..\ ” 的形式,每一个 " ../ " 表示上一个目录。
在绝对路径中可以理解为以 “此电脑” 为根目录。
这一切的大前提都是建立在 Windows 操作系统的情况下,在其他操作系统的环境中可能会有所差异。
二、Java 中的文件操作
Java 中通过 java. io 包中的 File 类来对一个文件(包括目录)进行抽象的描述,File 对象是硬盘上的一个文件的“抽象”表示,为什么这么说呢,文件是存储来硬盘上的(第三方介质)的,直接通过代码操作硬盘,怕是不太友好,于是就在内存中创建一个文件对应的对象,然后我们通过操作内存中的对象,就可以间接的操作硬盘中的文件了。
2.1 File 类解析
我们从 File 类中常见的属性、构造方法和方法及其使用这几个方面来讲述。
2.1.1 构造方法
编辑
2.1.2 方法
编辑
2.2 常用方法演示
2.2.1 文件属性
// 文件的属性 public static void main(String[] args) throws IOException { File file = new File("hello_world.txt"); // 使用相对路径 //file.createNewFile(); System.out.println(file.exists()); // 判断文件是否存在 System.out.println(file.isDirectory()); // 判断file 对象是否是一个目录 System.out.println(file.isFile()); // 判断对象是否是一个普通文件 System.out.println(file.createNewFile()); // 根据file 对象自动创建一个空文件,成功创建后返回 true System.out.println(file.exists()); // 判断文件是否存在 System.out.println(file.isDirectory()); System.out.println(file.isFile()); System.out.println(file.createNewFile()); // 文件如果存在返回 false }
编辑
2.2.2 文件路径显示
public static void main(String[] args) throws IOException { File file = new File("..\\hello-world.txt"); // 代码中要使用 \ 要使用转意字符 System.out.println(file.getParent()); // 返回对象的父目录文件路径 System.out.println(file.getName()); //返回对象的纯文本名称 System.out.println(file.getPath()); // 返回对象的文件路径 System.out.println(file.getAbsolutePath()); // 返回对象的绝对路径 System.out.println(file.getCanonicalFile()); // 返回对象的修饰过的绝对路径 }
编辑
2.2.3 文件的删除
public static void main(String[] args) throws IOException { File file = new File("some-file.txt"); System.out.println(file.exists()); // 判断文件是否存在 System.out.println(file.createNewFile()); // 根据file 对象自动创建一个空文件,成功创建后返回 true System.out.println(file.exists()); // 判断文件是否存在 System.out.println(file.delete()); // 删除这个文件,删除成功返回 true System.out.println(file.exists()); file.deleteOnExit();// 标注这个文件将被删除,删除动作会到 JVM 运行结束时才会进行 }
编辑
2.2.4 创建目录
public static void main(String[] args) { File dir = new File("some-dir"); System.out.println(dir.isDirectory()); // 判断对象是否是一个目录 System.out.println(dir.isFile());// 判断对象是一个普通的文件 System.out.println(dir.mkdir());// 创建file 对象的目录 System.out.println(dir.isDirectory()); System.out.println(dir.isFile()); dir.mkdirs();// 可以创建中间目录, mkdir 只能创建单极目录 }
编辑
2.2.5 文件的重命名
编辑
编辑
运行结果博主已经验证,helloWorld,被创建博主手动向文件中添加了数据,最后再运行一次,renameTo() 方法,helloWorld 就替换掉成 helloWorld2 其中的数据没有丢失,重命名成功!!
三、Java 操作文件内容
第三个模块涉及的是文件内容的读写操作,学习向指定文件中写入数据,从指定文件中读取数据。
Java 标准库提供了一组类专门用于对文件进行读写操作。
针对文本文件,提供了一组类,面向 “字符流” 操作,经典代表: Reader, Writer ,读写的基本单位是字符。
针对二进制文件,提供了一组类,面向 “字节流” 操作,经典代表: InputStream, OutputStream . 读写的基本单位是字节。
3.1 文件缓冲区
先给大家补充一下 文件怎样写入信息或读出信息,当给文件写入数据时,最先应该在输出缓冲区,等缓冲区满了再输出到磁盘保存,当从文件里读取信息时,数据应先到达输入缓冲区,然后再给到程序对应的数据类型变量等。
编辑
3.2 InputStream 抽象类
InputStream 提供的抽象方法
编辑
InputStream 只是一个抽象类,关于 InputStream 的实现类有很多,基本上可以认为不同的输入设备都对应有一个 InputStream类的实现类,这里我们从文件中读取数据,可以使用 FileInputStream 类。
FileInputStream 构造方法
编辑
使用:先准备一个 hello.txt 文件,向文件内部填充一些内容:
编辑
// InputStream 字节文件读取 public static void main(String[] args) throws IOException { try(InputStream file = new FileInputStream("hello.txt")) { byte[] buf = new byte[1024]; // 数组长度自定义,不可扩容 while (true) { int data = file.read(buf); if(data == -1) { // 表示文件已经全部读完 break; } //1. 每次读取 3 字节进行 utf-8 解码,得到中文字符,utf-8 字符集一个汉字三个字节 //2. 利用 String 的构造方法完成 //3. data统计的是字节数 for (int i = 0; i < data; i += 3) { String str = new String(buf, i, 3, "utf-8"); System.out.printf("%s", str);// java 保留了C语言的打印机制 } } } }
编辑
字节流读取数据,我们想读到汉字的话巧妙的利用了 String 类的构造方法,将指定区间内(3个字节)的数据按照 utf- 8 编码集转换成字符。
3.3 Scanner 进行字符读取
上文 InputStream 针对字符进行字节流读取很不方便,还要转换,所以我们可以使用 Scanner 类将字节流读取,包装成 “字符流”,自动的转换~~
编辑
使用:先准备一个 hello.txt 文件,向文件内部填充一些内容:
编辑
// 利用 Scanner 进行字符读取 public static void main(String[] args) throws IOException{ try(InputStream data = new FileInputStream("hello.txt")) { try(Scanner in = new Scanner(data, "utf-8") ) { while (in.hasNext()) { String str = in.next(); System.out.println(str); } } } }
编辑
因为 str = in.next 遇到空格会换行,所以 world! 再第二行打印。
3.4 OutputStream 抽象类
InputStream 提供的抽象方法
编辑
OutputStream 也只是一个抽象类,关于 OutputStream 的实现类有很多,这里我们往文件中写入数据,可以使用 FileOutputStream 类,面向字节流。
编辑
使用 write() 方法向文件中写入数据
编辑
注意:博主的文件流资源会随着进程(main)的结束而结束,所以并没有手动的调用 close() 。
3.5 PrintWirter 进行字符写入
使用字节流 向文件中 “写” 数据总归是不方便的,所以可以使用 PrintWirter 将字节流数据包装成指定字符集的数据来完成输出。
PrintWriter 类中提供了我们熟悉的 print(), println(), printf ()方法。
PrintWriter 类是基于 Reader 类实现的,关于 Reader 类稍后再讲。
// PrintWriter 字符写数据publicstaticvoidmain(String[] args) throwsIOException { try(OutputStreamdata=newFileOutputStream("output.txt")) { try(OutputStreamWriteros=newOutputStreamWriter(data, "utf-8")) { // 如果想要使得每次写入的数据不被覆盖可以向构造方法中添加 truetry(PrintWriterwriter=newPrintWriter(os,true)) { writer.println("我是第一行"); writer.printf("%d:我是第二行 \n", 1+2); writer.print("我是第三行"); } } } }
编辑
避免二次写入文件被覆盖:
PrintWriter writer = new PrintWriter(os,true)
这一组文件操作类,InputStream 和 OutputStream 面向字节流操作文件, read() 和 write() 也可以一次读写多个字节,使用 byte[] 数组来表示,read() 方法会尽可能的 byte[] 数组填满,如果读到文件末尾,返回 -1 , writer() 方法会把 byte[] 数组中所有的数据都写入文件。
3.6 Reader 类字符读取
// Reader 类 read 方法 以字符的形式读取publicstaticvoidmain(String[] args) { try(Readerreader=newFileReader("hello.txt")) { while (true) { intc=reader.read(); if(c==-1) { break; } charch= (char)c; System.out.print(ch); } reader.close(); //释放资源 } catch (IOExceptione) { e.printStackTrace(); } }
编辑
3.7 Writer 类字符写入
// Writer 类 writer 方法 以字符的形式写入publicstaticvoidmain(String[] args) { try(Writerwriter=newFileWriter("output3.txt",true) ){ // true文件不覆盖写Stringstr="我的老哥"; writer.write(str); } catch (IOExceptione) { e.printStackTrace(); } }
编辑
以上一组 Reader 和 Writer 类,构造方法打开文件,写入时文件不存在会自动的创建一个文件。
read() 方法来读,一次读取一个 char 或者 char[]
write() 方法来写,一次写入一个 char 或者 char[] 或者 String
close()关闭资源
到这里,Java的文件操作 IO 流博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。
编辑
本期收录于博主的专栏——JavaEE,适用于编程初学者,感兴趣的朋友们可以订阅,查看其它“JavaEE基础知识”。
感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★*
遇见你,所有的星星都落在我的头上……