程序在运行期间可能需要从外部的存储媒介或其他程序中读取所需要的数据,这就需要使用输入流,输入流的指向称为它的源;另一方面,程序在处理数据后,可能需要将处理的结果写入到永久的存储媒介中或传送给其他的应用程序,这就需要使用输出流。
通俗的解释就是,以我当前程序为主,我要和其他的事物进行交流,所以我必须要搭一个通道(这个通道就是流),让我和它可以交流,我获取该事物的数据就叫输入,我给予该事物数据就叫输出。
那么输入输出有两种形式,一是以字节的形式输入输出;二是以字符的形式输入输出。
java.io包提供大量的流类,所有输入流都是抽象类InputStream(字节输入流)或Reader(字符输入流)的子类,而所有输出流都是抽象类OutputStream(字节输出流)或抽象类Writer(字符输出流)的子类。
一、File类
构造方法:
File(String filename);//没有路径,即在当前目录
File(String directoryPath,String filename);
File(File dir,String filename);
filename是文件名,directoryPath是文件路径,dir是目录
注:文件夹即目录也是一种文件,知识没有后缀名罢了
常用方法:
public boolean canRead():是否可读
public boolean canWrite():是否可写
public boolean exists():是否存在
public long length():获取文件长度(单位字节)
public boolean isFile():是否是一个文件(而不是目录)
public boolean isDirectory():是否是目录
public boolean isHidden():是否隐藏
public long last Modified():获取最后修改时间(从1970年至今的毫秒数)
public String getName():获取文件名字
public String getAbsolutePath():获取文件绝对路径
public String getParent():获取文件父目录
注:当构造一个File实例时,它只是说明了它把“文件夹-文件”这个模式抽象成了一个java对象,也就是说我可以用这个对象来操作实际的文件了。但是有一点就是,它并不会检测这个java对象实际对应的文件是否真实存在,所以需要判断,如果不存在就需要创建。。。
创建目录
File对象调用public boolean mkdir()创建一个目录,如果创建成功,返回true,否则返回false(所以你的那个File对象的filename参数的字符串应该是没有文件后缀的,才会创建出文件夹)
public boolean mkdirs()区别:这个如果父目录不存在,帮忙创建;上面不加s,会显示创建失败。
列出目录中的文件
如果File对象是一个目录,那么该对象调用下列方法可列出该目录下的文件和子目录:
public String[] list(FilenameFiler obj):以字符串形式返回目录下指定类型所有文件
public File[] listFiles(FilenameFiler obj):以File对象形式返回目录下指定类型所有文件
其中,FilenameFiler是一个接口,在调用以上两个方法之前,需要实现该接口中的方法:
public boolean accept(File dir,String name)
形如:String filename[] = file.list(filenamefiler);即可遍历完成。通俗讲,在实现接口里制定一个筛选规则,对当前文件目录依次遍历。先把文件的信息传到accept方法中,让accept方法进行判断,如果满足条件,即返回true,即会把文件信息存到数组中,返回false则不会存到数组中,这就完成了一次筛选。list()参数其实也可以省略。这里有使用此函数打印文件目录的示例:https://blog.csdn.net/qq_36923376/article/details/89884797
创建文件
如果File对象所指的文件不存在,可调用public boolean createNewFile()方法创建。
删除文件
调用public boolean delete()可以删除当前文件
运行可执行文件
的 当要执行机器上可执行文件时,可使用java.lang包中的Runtime类。
Runtime ec = Runtime.getRuntime();//创建对象
ec.exec(String command);//即可打开本地机器上的可执行文件,command是文件的绝对路径
二、字节输入输出流
输入流
输入流基本步骤:
- 设定输入流的源
- 创建指向源的输入流
- 让输入流读取源中的数据
- 关闭输入流
构造方法
- FileInputStream(String name);
- FileInputStream(File file);
创建时会抛出IO异常,比如你要读的文件实际不存在,所以需要捕获处理。
使用输入流对象读取字节
int read():读取单个字节,返回0-255之间一个整数,如果未读出字节就返回-1
int read(byte b[]):从源中读取b.length个字节到b中,返回实际读取的字节数目,如果未读出字节就返回-1。
int read(byte b[],int off,int len):从源中读取len个字节到b中,返回实际读取的字节数目,如果未读出字节就返回-1。从off位置开始存放读取的数据。
注:FileInputStream顺序读取文件,只要不关闭流,每次调用read() 方法就顺序读取源中其余内容,直到源末尾或流被关闭。尽管程序结束时会自动关闭所有打开的流,但是当程序使用完流后,显式的关闭任何打开的流仍是一个良好的习惯。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Main{
public static void main(String args[]){
int n=-1;
byte a[] = new byte[100];
String separator = File.separator;
String localdisk ="E:";
String filepath = localdisk+separator+"1017"+separator+"make"+separator;
String filename = "aaa.txt";
File file = new File(filepath,filename);
try {
FileInputStream in = new FileInputStream(file);
while((n=in.read(a))!=-1) {//这个地方
String s = new String(a,0,n);
System.out.println(s);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//txt内容:你好123i45like哈哈78bananas90很高兴
输出流
输出流基本步骤:
- 给出输出流的目的地
- 创建指向目的地的输出流
- 让输出流把数据写入到目的地
- 关闭输出流
构造方法
- FileOutputStream(String name);
- FileOutputStream(File file);
创建时会抛出IO异常,需捕获。
注:这里构造函数还可以有第二个参数,FileOutputStream(File file,boolean append);append如果为true,则会在目的地文件的末尾继续写入字节,如果为false或缺省,则会把目的地文件内容清空即刷新文件使得文件的长度为0,从头开始写。如果输出流指向的文件不存在,Java就会创建该文件。
使用输出流对象写字节
void write(int n):输出流调用该方法向目的地写数据
void write(byte b[]):输出流调用该方法向目的地写入一个字节数组
void write(byte b[],int off,int len):给定字节数组中起始于偏移量off处取len个字节写到目的地。
注:FileOutputStream顺序地写文件,只要不关闭流,每次调用write() 方法就顺序地向目的地写入内容,直到流被关闭。需要注意的是,在操作系统把程序所写到输出流上的那些字节保存到磁盘上之前,有时被存放在内存缓冲区中,通过调用close()方法,可以保证操作系统把流缓冲区的内容写到它的目的地,即关闭输出流可以把该流所用的缓冲区的内容冲洗掉。
public class Main{
public static void main(String args[]){
File file = new File("F:\\haha\\bbb","aaa.txt");
try {
FileOutputStream out = new FileOutputStream(file,false);
//注意这里的第二个参数,上面有讲解
String a = "啥事啊";
byte[] b = a.getBytes();
System.out.println(file.getName()+":"+file.length()+"byte");
out.write(b);
System.out.println(file.getName()+":"+file.length()+"byte");
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、字符输入输出流
前言:文件字节输入、输出流的read和write方法使用字节数组读写数据,即以字节为单位处理数据。因此字节流不能很好地操作Unicode字符,比如,一个汉字在文件中占用2个字节,如果使用字节流,读取不当会出现“乱码”现象。
输入流
FileReader是Reader的间接子类,是InputStreamReader的直接子类。
构造方法:
- FileReader(String filename);
- FileReader(File filename);
读取方法:
同样使用read方法,和上述一致,只不过把字节数组换成字符数组,不再赘述。
输出流
FileWriter是Writer的子类,是OutputStreamWriter的直接子类。
构造方法:
- FileWriter(String filename,boolean append);
- FileWriter(File filename,boolean append);
注:同样,第二个参数和上面字节流一样,可以省略。
写入方法:
同样使用write方法,和上述一致,只不过把字节数组换成字符数组,不再赘述。
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Main{
public static void main(String args[]){
File file = new File("F:\\haha\\bbb","aaa.txt");
try {
int n= -1;
FileReader in = new FileReader(file);
FileWriter out = new FileWriter(file,true);
char[] c = {'a','b','c','d','e','f','g'};
System.out.println(file.getName()+":"+file.length()+"byte");
out.write(c,0,c.length);
//把c数组中的字符全部写入
out.flush();
//别忘了刷新
System.out.println(file.getName()+":"+file.length()+"byte");
while((n=in.read(c))!=-1) {
//读取文件,一次读c.length长度,一次就读完了,进入while输出,第二次检测是-1,说明读完了,不进入循环
System.out.println(c);
}
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注:对于Writer流,write方法将数据首先写入到缓冲区,每当缓冲区溢出时,缓冲区的内容被自动写入到目的地,如果关闭流,缓冲区的内容会like被写入到目的地。流调用flush()方法可以立刻冲洗当前缓冲区,即将当前缓冲区的内容写入到目的地。
四、缓冲流
Java提供了更高级的流:BufferedReader流和BufferedWriter流,二者的源和目的地必须是字符输入流和字符输出流。
构造函数分别是:
BufferedReader(Reader in);
BufferedWriter(Writer out);
相比将与字符输入输出流的优点:
BufferedReader流能够读取文本行,方法是readLine(),如:String strline = inline.readLine()
BufferedWriter流可以向文件写入一个回行符,方法是newLine();
解释:可以把BufferedReader流和BufferedWriter流称为上层流,它们的字符流参数称为底层流,java采用缓存技术将上层流和底层流连接。
当BufferedWriter流调用flush()刷新缓存或调用close()方法关闭时,即使缓存没有溢出,底层流也会立刻将缓存的内容写入目的地。一般先关闭上层流再关闭底层流。在编写代码时只需关闭上层流,那么上层流的底层流将自动关闭。
五、InputStreamReader
public class InputStreamReader extends Reader
InputStreamReader是从字节流到字符流的桥接器:它读取字节并使用特定的编码格式转换为字符。它使用的字符集可以通过名称指定,也可以明确指定,或者可以接受平台的默认字符集。
每次调用一个InputStreamReader的read()方法都可能导致从底层字节输入流中读取一个或多个字节。为了能够有效地将字节转换为字符,可以从基础流中提取比满足当前读取操作所需的更多字节。
为了获得最高效率,请考虑在BufferedReader中包装InputStreamReader。例如:
BufferedReader in
= new BufferedReader(new InputStreamReader(InputStream in,Charset cs));
在下面的例子中,将使用URL类爬取百度首页的源码,网页是utf-8编码,所以爬过来的数据应用utf-8解码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
public class Main1{
public static void main(String args[]){
try {
URL url = new URL("http://www.baidu.com");
InputStream in = url.openStream();
BufferedReader buff = new BufferedReader(new InputStreamReader(in,"utf-8"));
String n=null;
while((n=buff.readLine())!=null) {
System.out.println(n);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
五、随机流
能否建立一个流,通过这个流既能读文件也能写文件呢?即随机流诞生!
RandomAccessFile类创建的流称作随机流,当准备对一个文件进行读写操作时,创建一个指向该文件的随机流即可。
构造方法:
RandomAccessFile(String name, String mode);
RandomAccessFile(File file, String mode);
参数mode取r(只读)或rw(可读写),决定创建流对文件的访问权利。
注:RandomAccessFile流指向文件时,不刷新文件。
RandomAccessFile类中有一个方法seek(long a)用来定位RandomAccessFile流的读写位置,其中参数a确定读写位置距离文件开头的字节个数。另外流还可以调用getFilePointer()方法获取流的当前读写位置。
RandomAccessFile类有好多读写的函数,可自行查阅api
需要注意的是,RandomAccessFile流的readLine()方法在读取含有非ASCII字符的文件时(比如汉字)会出现“乱码”现象,因此,需要把readLine()读取的字符用“iso-8859-1”编码重新编码存放到byte数组中,然后再用当前机器的默认编码将该数组转化为字符串,操作如下:
1.读取
String str = in.readLine();
2.用“iso-8859-1”重新编码
byte[] b = str,getBytes("iso-8859-1");
3.用当前机器的默认编码将该数组转化为字符串
String content = new String(b);
如果机器默认编码是GB2312,等价于String content = new String(b,“GB2312”);