文件操作与IO—文件读写
上篇文章:
文件操作与IO—File类https://blog.csdn.net/sniper_fandc/article/details/146881359?fromshare=blogdetail&sharetype=blogdetail&sharerId=146881359&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link
目录
1 数据流
2 InputStream和FileInputStream VS Reader和FileReader
2.1 InputStream
2.2 FileInputStream
2.3 代码实例
2.4 Reader与FileReader
2.5 Scanner
3 OutputStream和FileOutputStream VS Writer和FileWriter
3.1 OutputStream
3.2 代码示例
3.3 Writer和FileWriter
3.4 PrintWriter
1 数据流
流(Stream)是一种数据的读写方式,读视为输入流,写视为输出流,批量读取数据时,这里的“批量”就是流,而java中数据的读写就是面向流的读写方式。
Java中读写数据主要有两种方式:
1.InputStream(读)和OutputStream(写)
2.Reader(读)和Writer(写)
其中方式1是以字节流为读写方式(以字节为单位的流),方式2是以字符流的为读写方式(以字符为单位的流)。因此方式1主要用来操作二进制文件,方式2主要用来操作文本文件。
2 InputStream和FileInputStream VS Reader和FileReader
2.1 InputStream
InputStream是一个抽象类,内部包含如下方法:
返回值 | 方法 | 含义 |
int | read() | 读一个字节,返回值0-255表示字节的值,返回值-1表示读到文件结尾 |
int | read(byte[] b) | 最多读b.length字节(如果有的话),返回值实际读到的数量,返回值-1表示读到文件结尾,读到的数据填入字节数组 |
int | read(byte[] b, int off, int len) | 从off下标开始,最多读len字节,最多读b.length字节(如果有的话),返回值实际读到的数量,返回值-1表示读到文件结尾,读到的数据填入字节数组 |
void | close() | 关闭字节流,会抛异常IOException |
注意:打开一个文件,最后一定记得关闭。因为实际中的程序多是服务器程序,即程序持续运行,如果打开文件不关闭,内存中进程对应的文件描述符表对应的文件描述符会一直存在,从而导致文件描述符表最终爆满。及时不使用关闭文件,释放资源(内存+文件描述符表)。
2.2 FileInputStream
FileInputStream是InputStream的一个具体实现类,用于打开一个输入流(在内存中,用于操作磁盘中的文件,作用类似于“句柄”(Handle))。
构造方法 | 含义 |
FileInputStream(File file) | 利用File对象创建文件对应的输入流 |
FileInputStream(String name) | 利用路径字符串创建文件对应的输入流 |
使用FileInputStream会抛出异常FileNotFoundException,属于IOException的子类。
2.3 代码实例
public class ReadFile {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try{
inputStream = new FileInputStream("newTest.txt");
while(true){
int b = inputStream.read();
if(b == -1){
break;
}
System.out.println(b);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
inputStream.close();
}
}
}
这里把close()放到finally中,防止因为抛出异常导致关闭输入流的代码无法执行到。而newTest.txt文件内的内容是:hello,因此读入的字节是对应的ASCII值。
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try{
inputStream = new FileInputStream("newTest.txt");
byte[] b = new byte[1024];
int length = inputStream.read(b);
System.out.println(length);
for(int i = 0;i < length;i++){
System.out.println(b[i]);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
inputStream.close();
}
}
这是一次性尽可能读入1024字节长度的方式,这种方式IO次数更少,更推荐使用。
如果把文件中的文本换成中文:你好,输出就变成了如下:
这是因为在UTF-8字符编码的规则下,一个中文字符占3个字节,因此两个汉字有6个字节,如果按照3个字节组合,就可以还原成原来的汉字。
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try{
inputStream = new FileInputStream("newTest.txt");
byte[] b = new byte[1024];
int length = inputStream.read(b);
System.out.println(length);
String s = new String(b,0,length,"utf-8");
System.out.println(s);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
inputStream.close();
}
}
可以看到,InputStream只适合处理字节类型的文件,对于文本类型尤其是中文,操作就繁琐许多。
2.4 Reader与FileReader
Reader和FileReader的关系和InputStream和FileInputStream类似,都是抽象类和具体实现类的关系,同时其中的方法也类似,只是参数类型变成了char[]。这组类可以解决字符类型文件的读取:
public static void main(String[] args) throws IOException {
Reader reader = null;
try{
reader = new FileReader("newTest.txt");
char[] b = new char[1024];
int length = reader.read(b);
System.out.println(length);
String s = new String(b,0,length);
System.out.println(s);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
reader.close();
}
}
2.5 Scanner
Scanner类提供了更加强大的文本读取工具,也是推荐使用的文本类型文件的读取方式。
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try{
inputStream = new FileInputStream("newTest.txt");
//接受来自键盘的输入
//Scanner scanner = new Scanner(System.in);
//接受来自输入流的输入
Scanner scanner = new Scanner(inputStream);
String s = scanner.next();
System.out.println(s);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
inputStream.close();
}
}
3 OutputStream和FileOutputStream VS Writer和FileWriter
3.1 OutputStream
OutputStream也是一个抽象类,内部包含如下方法:
返回值 | 方法 | 含义 |
void | write(int b) | 写一个字节,字节范围0-255表示字节的值 |
void | write(byte[] b) | 将字符数组b中的数据全部写入文件中 |
void | write(byte[] b, int off, int len) | 在b中从off下标开始,写len长度的字节 |
void | close() | 关闭字节流,会抛异常IOException |
void | flush() | 将缓冲区的数据刷新到磁盘 |
由于磁盘IO速度很慢,因此OutputStream 为了减少IO次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里(缓冲区),待数据写满了或者满足一定条件再写入磁盘。为了写入文件后续能及时读到写入的数据,就需要调用flush()强制刷新。
3.2 代码示例
public class WriteFile {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("newTest.txt");){
outputStream.write(104);
outputStream.write(101);
outputStream.write(108);
outputStream.write(108);
outputStream.write(111);
outputStream.write('h');
outputStream.write('e');
outputStream.write('l');
outputStream.write('l');
outputStream.write('o');
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里需要注意,写入字节和写入ascll字符是等效的,因此代码最终写入了两个hello。并且写入的方式是覆写(打开一次文件,所有写入的内容会覆盖原文内容。在同一次打开的文件中多次写,是续写)。
代码中还用到了try with resources,含义详细见代码模版部分。
3.3 Writer和FileWriter
public static void main(String[] args) {
try(Writer writer = new FileWriter("newTest.txt");){
writer.write("hello java");
} catch (IOException e) {
e.printStackTrace();
}
}
Writer类提供了多种writer方法,由于字符串可以和字符数组转化,因此writer方法可以接收字符串参数来进行写入(其他参数的方法和输入流类似)。
3.4 PrintWriter
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("newTest.txt");){
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println("hello world");
printWriter.println("hello java");
printWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
PrintWriter类对标读文件时的Scanner类,也提供了更丰富地写文件的方法,比如println(),这是换行写入的方法。需要注意:如果写入的内容在文件中不显示,一定要及时调用flush()方法,问题可能就出现在写入的数据还在缓冲区未及时刷新到文件中。
下篇文章: