文件:是存储在某种长期储存设备上的一段数据流。
如果程序运行过程中需要输入或输出信息量大,直接用键盘或显示器显然不可行,此时可以采用文件。即将要输入的信息预先保存到磁盘文件中,程序运行时,直接从文件读入信息;程序的大量输出信息直接写入磁盘文件,为此Java提供了功能强大的File类对磁盘、文件和目录操作功能。
文件类:用来管理磁盘、文件和目录的类。
注意:
Java提供的File类位于Java.io包中;
File类只负责对磁盘、文件和目录建立连接,不负责数据的输入和输出;
数据的输入和输出由流来完成,流包括输入流和输出流。
读取文件中的内容:
字节的文件流类:FileInputStream
字符的文件流类:FileReader
向文件中写入内容:
字节的文件流类:FileOutputStream
字符的文件流类:FileWriter
文件的路径的三种表现形式:
d:\\test1.txt
” //只能在windows下使用(不用)d:/test1.txt
” //兼容unix,linux,windows下使用d:"+File.separator+"test1.txt
”; //官方推荐,兼容各个操作系统IO流的分类:
IO流的4个抽象基类
输入 | 输出 | |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
1、什么是流 ?
“流”是一个抽象的概念,它是对输入输出设备的一种抽象理解,在java语言中,对数据的输入输出的操作都是以“流”的方式进行的。
2、流的分类 ?
Java中的流有很多分类方式,有输入流和输出流,有字节流和字符流、内存流、管道流、网络流等。
常见流类的结构:
IO流 | 字符流 | 字节流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
输入流:从外设流入到计算机的数据序列,
图1
输出流:从计算机流向外设的数据序列。
图2
字节流:数据流中的最小的数据单元是字节,一次读入读出8位二进制;
字符流:数据流中的最小的数据单元是字符,一次读入读出16位二进制,java中的字符是Unicode编码,一个字符占用两个字节。
输入流与输出流的特点是数据的获取和发送均按照数据序列顺序进行,每一个数据都必须等待排在它前面的数据读取或写出后才能被读写,每次操作处理都是序列中剩余的原始二进制数据,也可以是按照一定编码处理之后符合某种格式规定的特定数据,如字符数据,所以Java中的流分为字节流(二进制流)和字符流。
通俗的理解为“ 文件是流的容器,流是文件的内容 ”。就像水杯是水的容器,水是水杯中的内容一样。
文件与流的相关类都位于java.io包,其中包含5个重要的类,几乎所有与输入输出相关的类都继承了这5个类,即InputStream、OutputStream、Reader 、Writer和File类。
利用这些类Java程序可以很方便的实现多种输入输出操作和复杂的文件与目录管理。
在对文件的读写操作中,字节流可用于读写二进制文件,字符流用于读写文本文件。
二进制文件:指的是无字符编码格式,均由字节(Byte)组成,图片文件、word文档等均为二进制文件。
文本文件:是一种特殊的二进制文件,也是由字节组成,但是需要通过特定的字符编码格式读取或写入,否则会出现乱码,后缀名为txt的文件就是典型的文本文件。
File类的对象表示磁盘、目录、文件。其对象的属性包含了文件或目录的相关信息,如名称、长度、所含文件个数等,其方法可以完成对文件或目录的常用管理操作,如创建、删除等。
因为Java是平台无关的编程语言,而不同平台下文件系统的差异很大,Java使用File 类统一描述不同平台的文件系统。以windows操作系统为例,在windows操作系统中,文件系统主要由“磁盘分区”、“目录”、“文件”三者组成,三者可使用File类进行描述。
下表列出了File类的常用方法:
方法名称 | 参数 | 作用 | 返回值 |
---|---|---|---|
构造方法 | String | 传入文件或目录名,获取对应的文件或目录对象 | |
canRead | 无 | 文件是否可读 | Boolean:是否可读 |
canWrite | 无 | 文件是否可写 | Boolean:是否可写 |
delete | 无 | 删除文件或目录 | Boolean:操作结果 |
exists | 无 | 文件或目录是否存在 | Boolean:是否存在 |
getAbsolutePath | 无 | 获取绝对路径 | String:绝对路径 |
getFreeSpace | 无 | 获取分区剩余空间(自由空间) | Long:字节数量 |
getTotalSpace | 无 | 获取分区的已用空间(可用空间) | Long:字节数量 |
getUsableSpace | 无 | 获取分区的已用空间(可用空间) | Long:字节数量 |
getName | 无 | 获取文件或目录的名称 | String:文件或目录名称 |
isDirectory | 无 | 是否为目录 | Boolean:是否为目录 |
isFile | 无 | 是否为文件 | Boolean:是否为文件 |
isHidden | 无 | 是否为隐藏文件或目录 | Boolean:是否隐藏 |
lastModified | 无 | 获取文件最后修改时间 | Long:最后修改时间 |
length | 无 | 获取文件长度 | Long:字节数量 |
listFiles | 无 | 获取目录的子目录、文件 | File[]:子目录和子文件 |
listRoots | 无 | 获取所有磁盘分区 | File[]:磁盘分区 |
mkdir | 无 | 创建目录 | Boolean:是否创建成功 |
mkdirs | 无 | 创建多级目录 | Boolean:是否创建成功 |
Demo1:查看磁盘(分区)的空间
File[] disks = File.listRoots();
for (File i : disks) {
System.out.println(i);
System.out.println("总的空间:" + i.getTotalSpace() / 1024 / 1024 / 1024 + "G");
// 可用空间和自由空间的区别???
System.out.println("可用空间:" + i.getUsableSpace() / 1024 / 1024 / 1024 + "G");
System.out.println("自由空间:" + i.getFreeSpace() / 1024 / 1024 / 1024 + "G");
}
Demo2:查看指定磁盘的文件目录
File disk = new File("D:"); // 确定磁盘位置
File[] files = disk.listFiles(); //存储指定磁盘的所有文件目录
// 显示所有文件目录
for (File i : files) {
System.out.println(i);
}
例:
package chapter2;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo002 {
public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public static void main(String[] args) {
File[] disks = File.listRoots();//disk磁盘,碟盘
System.out.println("系统磁盘分区情况:");
for(int i=0;i<disks.length;i++) {
File disk = disks[i];
System.out.println(disk.getAbsolutePath()+"盘\t总空间"+disk.getTotalSpace()/1024/1024/1024+"G\t剩余空间"
+disk.getFreeSpace()/1024/1024/1024+"G");
}
File c = new File("C:");
if(!c.exists()) {
System.out.println("c盘根不存在。");
return ;
}
System.out.println("c盘根目录结构:");
File[] files = c.listFiles();
for(int i=0;i<files.length;i++) {
File file = files[i];
if(file.isDirectory()) {
System.out.print("[目录]"+file.getName());
}
if(file.isFile()) {
System.out.println("[文件]"+file.getName());
System.out.println("\t大小:"+file.length()/1024+"k");
Date date = new Date(file.lastModified());
System.out.println("\t修改日期:"+sdf.format(date)+"\t");
if(file.isHidden()) {
System.out.print("[隐藏]");
}
if(!file.canWrite()) {
System.out.print("[只读]");
}
}
System.out.println();
}
}
}
使用文件过滤器(FilenameFilter
)来筛选文件
String pathname="d:/aaa";
File file=new File(pathname);
file.mkdir();
String[] list = file.list();
System.out.println(Arrays.toString(list));
File[] listFiles = file.listFiles();
System.out.println(Arrays.toString(listFiles));
File file2=new File("d:/aaa");
System.out.println(file2.getAbsolutePath());
String[] list2 = file2.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if(name.endsWith(".txt")) return true;
return false;
}
});
System.out.println(Arrays.toString(list2));
使用字节输入(FileInputStream)读取文件为一下步骤:
第一种
private static void test3() {
String name = "d:/fileTest/hello.txt";
try {
InputStream inputStream = new FileInputStream(name);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
第二种
private static void test3() throws FileNotFoundException {
String name = "d:/fileTest/hello.txt";
InputStream inputStream = new FileInputStream(name);
}
第三种(1):try…resource
try…resource这种方式从JDK7以后才支持。
private static void test3() {
String name = "d:/fileTest/hello.txt";
try(InputStream inputStream = new FileInputStream(name)) {
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
第三种(2):try…resource
private static void test3() {
String name = "d:/fileTest/hello.txt";
try(InputStream inputStream = new FileInputStream(name)) {
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void test3() {
String name = "d:/fileTest/hello.txt";
try(InputStream inputStream = new FileInputStream(name)) {
// 1、一次只能读取一个字节 a
System.out.println((char)inputStream.read());
// 2、一次读取多个字节
byte[] buffer=new byte[5];
// 按照数组大小批量读取
// 返回的数据表示的是成功读取的字节数
// 数组不会被自动清除内容,上次的数据还存在
// 如果内容已经读取完了,再次读取时将会显示内容未完时读取的内容,数据读取完毕返回-1
/*
* 第一次读取
* 5
* [98, 99, 100, 101, 102]
*/
System.out.println(inputStream.read(buffer));
System.out.println(Arrays.toString(buffer));
/*
* 第二次读取
* 1
* [103, 99, 100, 101, 102]
*/
System.out.println(inputStream.read(buffer));
System.out.println(Arrays.toString(buffer));
/*
* 第三次读取
* -1
* [103, 99, 100, 101, 102]
*/
System.out.println(inputStream.read(buffer));
System.out.println(Arrays.toString(buffer));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
字节输出流(FileOutputStream)的使用也分为打开文件、输出、关闭文件三部分。
例子2
1、注意::文件输出流,会自动创建文件,不能创建目录。
2、构造参数append,为true是追加模式,false是覆盖模式(默认)
3、汉字是2个字节,只写入了前半个,所以不能显示
4、outputStream.flush();//把缓冲区内容写入资源
5、outputStream.close();//close会flush,在close前应该强制flush,保护性写法
package com.bennett.test1008;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class OutputStreamTest {
public static void main(String[] args){
test1();
}
private static void test1() {
// 1、注意::文件输出流,会自动创建文件,不能创建目录
String path="d:/fileTest/test1.txt";
OutputStream outputStream =null;
InputStream inputStream =null;
try {
// 创建输出流对象
// 构造参数append,为true是追加模式,false是覆盖模式(默认)
outputStream = new FileOutputStream(path,true);
/*
* 1、单个字符的写入
* // 定义写入字符 char c = 'a';
* // 输出流对象写入字符 outputStream.write(c);
*/
// 2、多个字符的写入
byte[] array ={97,98,99,100,101,102,103};
outputStream.write(array);
outputStream.write(' ');
outputStream.write(array,1,4);//写入指定下标区间的字符
//汉字是2个字节,只写入了前半个,所以不能显示
outputStream.write('中');//显示为-
// outputStream.flush();//把缓冲区内容写入资源
// outputStream.close();//close会flush,在close前应该强制flush,保护性写法
// 创建输入流对象
inputStream = new FileInputStream(path);
// 输入流对象读取字符
int i;
while ((i=inputStream.read())!=-1) {
System.out.print((char)i);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inputStream!=null) {
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (outputStream!=null) {
try {
outputStream.flush();//把缓冲区内容写入资源
outputStream.close();//close会flush,在close前应该强制flush,保护性写法
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
字符流的操作方式与字节流基本相同,字符流会根据当前操作系统与语言环境选择适当的字符编码方式读写文件,适合读取文本文件。因为字符流会对文件内容编码,所以不能用于读取二进制文件。下面使用文件输入流(FileReader)和缓存输入流(BufferedReader)读文件。
在这里插入代码片
接下列是使用文件输出流(FileWrite)与缓存输出流(BufferedWriter)的例子,同样BufferedWriter提供了输出整个字符串的方法,需要注意的是,如果要输出换行符,不能简单的使用”\n”,因为不同操作系统定义的换行符是不一样的,推荐使用BufferedWriter提供的newLine()方法输出换行。
package com.bennett.test1008;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import org.junit.jupiter.api.Test;
class ReaderAndWriterTest {
@Test
void readerTest() {
String path="d:/fileTest/test2.txt";
File file = new File(path);
Reader reader = null;
try {
if (!file.exists()) {
file.createNewFile();//创建新文件。注意:不是目录(文件夹)
}
// 如果没有指定的读取文件值,reader不会自动创建该文件
reader = new FileReader(path);
int i;
while ((i=reader.read())!=-1) {
System.out.print((char)i);//读取一个字符
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Test
void writerTest() {
String path="d:/fileTest/test2.txt";
// Writer writer =null;
// try {
构造参数append,为true是追加模式,false是覆盖模式(默认)
// writer = new FileWriter(path,true);
写入一个字符串
// writer.write("使我有洛阳二顷田,安能佩六国相印");
// System.out.println("写入成功!");
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }finally {
// try {
// writer.close();
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
try(Writer writer=new FileWriter(path)) {
writer.write('妙');
writer.write(99);
writer.write("人间清暑殿");
writer.append("天上广寒宫");
writer.flush();
writer.close();
System.out.println("写入完毕");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
package com.bennett.test1008;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest {
public static void main(String[] args) throws IOException {
FileReader fileReader=new FileReader("d:/fileTest/test2.txt");
BufferedReader bufferedReader=new BufferedReader(fileReader);
System.out.println(bufferedReader.readLine());
System.out.println(bufferedReader.readLine());
System.out.println(bufferedReader.readLine());
System.out.println(bufferedReader.readLine());
System.out.println(bufferedReader.readLine());
bufferedReader.close();
}
}
I/O流
File类
创建File对象
File file = new File(“文件绝对路径/相对路径/盘符”);
文件操作
创建文件对象
File file = new File(“G:\a.txt”);
操作功能
是否可执行
file.canExecute()
是否可读
file.canRead()
是否可写
file.canWrite()
删除文件
file.delete()
文件是否存在
file.exists()
是否为目录
f.isDirectory()
是否为文件
file.isFile()
文件重命名
file.renameTo(new File(“shuaishuai.txt”))
信息打印
绝对地址
file.getAbsolutePath()
相对地址
file.getPath()
创建文件,目录
创建文件boolean
file.createNewFile()
创建目录
创建一级目录
file.mkdir()
级联创建目录
file.mkdirs()
盘符操作
创建磁盘对象
File dir = new File(“G:\”);
信息打印
磁盘自由空间
dir.getFreeSpace()
磁盘总共空间
dir.getTotalSpace()
磁盘可用空间
dir.getUsableSpace()
操作功能
是否为隐藏文件
f.isHidden()
最后一次修改时间
new Date(f.lastModified())
流概念
管道
是一条不间断的字节流,用来实现程序或进程间的通信,或读写文件
流分类
数据流向
输入流
InputStream,Reader
输出流
OutputStream,Writer
数据单位
字节流
InputStream
OutputStream
字符流
Reader
Writer
实现功能
节点流
处理流
常用流
字节流
InputStream
int read()
int read(byte[])
int read(byte[],int ,int)
close()
OutputStream
write(int)
wirte(byte[])
write(byte[],int,int)
flush()
close()
字符流
Reader
转换流
InputStreamReader
缓冲流
BufferedReader
Writer
转换流
OutputStreamWriter
缓冲流
BufferedWriter
对象流
一个自定义类如果要被序列化或者反序列化的时候需要做如下事情:
字节输入流转换为字符输入流
一个inputstreamreader是从字节流转换为字符流:将字节数组解码成文字使用指定的 charset
。字符集,它使用可指定名称或可给予明确,或平台的默认字符集可以接受。
转换流用于将底层的字节流转换为字符流供子类使用。
字节流和字符流有什么区别?
最大的区别是:字节流直接与终端文件进行数据交互(读入、输出),而字符流需要将数据经过缓冲区处理才与终端文件进行数据交互。
如在使用字节输出流OutputStream输出数据时,即使最后没有关闭输出流,内容也可以正常输出,但是如果使用的是字符输出流Writer(子类FileWriter),程序最后如果没有关闭字符输出流,就表示在缓冲区处理的内容不会被强制清空,所以就不会输出数据。如果有特殊情况,字符输出流无法关闭,可以使用flush()强制清空缓冲区!
说明:InputStreamReader类的构造方法接收字节输出流InputStream对象in,而InputStreamReader类是Reader的子类,所以该子类的对象可以向上转型为Reader类对象,这样就表示可以将接收的字节输入流转换为字符输入流。
package com.bennett.test1011;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
/**
* @version:
* @Description:InputStreamReader 转换流 (读取)
* @author gxd
* @date: 2021年10月11日 下午5:02:54
*/
public class InputStreamReaderTest {
public static void main(String[] args) throws IOException {
// test1();
test2();
}
private static void test1() throws UnsupportedEncodingException, IOException {
System.out.println("请输入信息:");
// int input = System.in.read();
System类 中的InputStream in;对象调用read()方法。
// System.out.println((char)input);
// InputStreamReader reader=new InputStreamReader(System.in,"UTF-8");
InputStreamReader reader = new InputStreamReader(System.in, "GBK");// 简体中文操作系统的默认字符集使用的默认字符集是GBK
// InputStreamReader reader=new InputStreamReader(System.in,"BIG5");
// System.out.println((char) reader.read());//读取一个字节
// System.out.println((char) reader.read());
// 缓冲流
BufferedReader br = new BufferedReader(reader);
System.out.print(br.readLine());// 一整行输出
}
// 字节流转换字符流
private static void test2() throws FileNotFoundException, IOException {
// 第一步:定义路径
File file = new File("D:/fileTest/test1.txt");
// 第二步:取得字节输出流(InputStream)
InputStream inputStream = new FileInputStream(file);
// 第三步:将字节流转换为字符流
InputStreamReader reader = new InputStreamReader(inputStream, "UTF-8");
// 注意:合并前三步
// InputStreamReader reader1 = new InputStreamReader(new FileInputStream("D:/fileTest/test1.txt"), "UTF-8");
// 3.2 输出信息(1个字符)
//System.out.println((char) reader.read());//输出一个字符
//3.3 输出信息(整行输出)
BufferedReader bufferedReader = new BufferedReader(reader);
//System.out.println(bufferedReader.readLine());
String is;
while ((is = bufferedReader.readLine()) != null) {
//System.out.println(bufferedReader.readLine());
System.out.println(is);
}
// 第四步:关闭流
reader.close();
inputStream.close();
}
}
字节输出流转换为字符输出流
注明:默认的字符集码表使用的是UTF-8
说明:OutputStreamWriter类的构造方法接收字节输出流OutputStream对象out,而OutputStreamWriter类是Writer的子类,所以该子类的对象可以向上转型为Writer类对象,这样就表示可以将接收的**字节输出流转换为字符
package com.bennett.test1011;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
* @version:
* @Description: OutputStreamWriter 转换流(写入)
* @author gxd
* @date: 2021年10月11日 下午5:02:19
*/
public class OutputStreamWriterTest {
public static void main(String[] args) throws IOException {
// 第一步:定义文件和路径
File file = new File("d:fileTest/utf-8.txt");
// 第二步:定义输出流
OutputStream outputStream = new FileOutputStream(file);
// 第三步:字节流转换为字符流
OutputStreamWriter writer=new OutputStreamWriter(outputStream, "utf-8");
// 第四步:写入信息
writer.write("山有木兮木有枝,心悦君兮君不知。");
writer.flush();
writer.close();
System.out.println("ok");
}
}
打印流是输出信息最方便的类,注意包含字节打印流PrintStream和字符打印流:PrintWriter。打印流提供了非常方便的打印功能,可以打印任何类型的数据信息,例如:小数,整数,字符串。
package com.bennett.test1011;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
/**
* @version:
* @Description:PringStream 打印流
* 将会这首诗写入到test5.txt文件中
* @author gxd
* @date: 2021年10月11日 下午5:09:04
*/
public class PringStreamTest {
public static void main(String[] args) throws FileNotFoundException {
// System.out
// System.err
PrintStream printStream = new PrintStream(new FileOutputStream("D:/fileTest/test5.txt"));
// System.out=printStream;
// 记录控制台对象
PrintStream out = System.out;
System.setOut(printStream);
System.out.println("人生若只如初见,何事秋风悲画扇。");
System.out.println("等闲变却故人心,却道故心人易变。");
System.out.println("骊山语罢清宵半,泪雨霖铃终不怨。 ");
System.out.println("何如薄幸锦衣郎,比翼连枝当日愿。");
System.setOut(out);
System.out.println("输出完毕");
}
}
数据输出流允许应用程序以与机器无关方式将Java基本数据类型写到底层输出流。
在io包中,提供了两个与平台无关的数据操作流:
数据输出流(DataOutputStream)
数据输入流 (DataInputStream)
通常数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据读入。
作者:that_is_this
链接:https://www.jianshu.com/p/c7ba05b1afa0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
数据输入流,让应用程序读取原始java数据类型从底层输入流中的一个独立于机器的方式。一个应用程序使用一个数据输出流来写数据,以后可以通过数据输入流读取。
输入流是不一定安全的多线程访问。线程安全是可选的,是在这个类中的方法的用户的责任。
在这里插入代码片
数据输出流可以让一个应用java写的原始数据类型在一个便携式的方式输出流。一个应用程序可以使用一个数据输入流来读取数据。