1,操作文件类(File)
1.1,File类的基本介绍
在整个io包中,唯一与文件本身相关的类就是File类。使用File类可以进行创建或删除文件等常用操作,如果要使用一个File类,则必须向File类的构造方法中传递一个文件路径。
public File(String pathname)
方法或常量 类型 描述 public static final String pathSeparator 常量 表示路径的分隔符(windows是“;”) public static final String separator 常量 表示路径的分隔符(window是“\”) public File(String pathname) 构造 创建File类对象,传入完整路径 public boolean createNewFile() throws IOException 普通 创建新文件 public boolean delete() 普通 删除文件 public boolean exists() 普通 判断文件是否存在 public boolean isDirectory() 普通 判断给定路径是否是一个目录 public long length() 普通 返回文件的大小 public String[] list() 普通 列出指定目录的全部内容,只是列出了名称 public File[] listFiles() 普通 列出指定目录的全部内容,会列出路径 public boolean mkdir() 普通 创建一个目录 public boolean renameTo(File dest) 普通 为已有的文件重新命名 问题:为啥File类中的常量定义的命名规则不符合标准命名规则?不应该全部大写吗?
答案:这里确实不符合JAVA的标准命名规则,主要原因是JAVA的发展经过一段很长的时间,而命名规则也是逐步形成的,File类因为出现较早,所以当时并没有对命名规范有严格的要求,这些都算是JAVA的历史遗留问题。
1.2,使用File类操作文件
1,创建一个新文件:File类的对象实例化完成之后,就可以使用createNewFile()创建一个新文件,但是此方法使用了throws关键字声明,所以在使用中,必须使用try...catch进行异常的处理。
File file = new File("D:\\ysy.txt"); try { file.createNewFile(); }catch (Exception e){ e.printStackTrace(); }
此时,D盘中已经创建好了ysy.txt文件。但是对于不同的操作系统中,则路径的分隔符表示是不一样的。Windows中使用反斜杠表示目录分隔符“\”;Linux中使用正斜杠表示目录的分隔符“/”。Java程序本身具有可移植性,但是面对不通过操作系统不符合系统要求,为了达到可以执行的目的,需要观察File类中提供的两个常量。
System.out.println("pathSeparator:"+File.pathSeparator); System.out.println("separator:"+File.separator); ========================================================= pathSeparator:; separator:\
File file = new File("d:"+File.separator+"ysy.txt");
2,删除一个指定文件:File类中也支持删除文件操作,如果要删除一个文件,则可以使用File类中的delete()方法。
File file = new File("d:"+File.separator+"ysy.txt"); file.delete();
此时,文件已经删除,但是以上做法也存在问题,即在删除文件前应该保证文件存在,所以需要需要先判断文件是否存在。判断一个文件是否存在可提供exists()方法,此方法返回boolean类型。
File file = new File("d:"+File.separator+"ysy.txt"); if (file.exists()){ file.delete(); }
3,综合创建和删除文件的操作
File file = new File("d:"+File.separator+"ysy.txt"); if (file.exists()){ file.delete(); }else{ try { file.createNewFile(); }catch (IOException e){ e.printStackTrace(); } }
程序执行之后,文件并不会立刻创建或删除,会有一些延迟,这是因为所有的操作都需要通过JVM完成所造成的,会出现一定的延迟。
4,创建文件夹和列出指定目录的全部文件
File file = new File("d:"+File.separator+"dir"); file.mkdir();
方法 描述 public String[] list() 列出全部名称,返回一个字符串数组 public File[] listFiles() 列出完整的路径,返回一个File对象数组。 File file = new File("c:"+File.separator); String str[] = file.list(); File files[] = file.listFiles(); for(int i=0;i<str.length;i++){ System.out.println(str[i]); } for(int i=0;i<files.length;i++){ System.out.println(files[i]); }
2,RandomAccessFile类
2.1,RandomAccessFIle类简介
File只是对文件本身进行了操作,而如果要对文件内容进行操作,则可以使用RandomAccessFile类,此类属于随机读取类,可以随机地读取一个文件中指定位置的数据。
方法 类型 描述 public RandomAccessFile(File file,String mode) throws FileNotFoundException 构造 接收File类对象,指定操作路径,但是在设置时需要设置模式,r为只读,w为只写,rw为读写。 public RandomAccessFile(String name,String mode) throws FileNotFoundException 构造 不再使用File类对象表示文件,而是直接输入了一个固定的文件路径 public void close() throws IOException 普通 关闭操作 public int read(byte[] b) throws IOException 普通 将内容读取到一个byte数组中 public final byte readByte() throws IOException 普通 读取一个字节
public final int readInt() throws IOException 普通 从文件中读取整数数据 public void seek(long pos) throws IOException 普通 设置读取指针位置 public final void writeBytes(String s) throws IOException 普通 将一个字符串写入到文件中 public final void writeInt(int v) throws IOException 普通 将一个int型数据写入文件,长度为4位 public final void skipBytes(int n) throws IOException 普通 指针跳过指定的字节 PS:如果使用rw方式声明RandomAccessFile对象,要写入的文件不存在,系统将会自动创建。
2.2,使用RandomAccessFile类写入数据
File file = new File("d:"+File.separator+"ysy.txt"); RandomAccessFile rdf = null; rdf = new RandomAccessFile(file,"rw"); String name = "ysy"; int age = 30; rdf.writeBytes(name); rdf.writeInt(age); rdf.close();
打开记事本,发现只有ysy,却看不到30,这是为啥?实际上数字由于编码的原因变成了其他符号,可以修改为300,3000查看后结果是乱码,但是是真实存在的,丝毫不影响读取。
RandomAccessFile rdf = null; rdf = new RandomAccessFile(file, "r"); byte b[] = new byte[3]; //对应ysy for (int i=0;i<b.length;i++){ b[i] = rdf.readByte(); } System.out.println("name:"+new String(b)+" age:"+rdf.readInt()); ========================================= name:ysy age:30
随机读写流可以实现对文件内容的操作,但是却过于复杂,会出现乱码、无法查看int数据等各种问题,所以一般情况下操作文件内容往往会使用字节或字符流。
3,字节流和字符流
在程序中所由的数据都是以流的方式进行传输或保存的,程序需要数据时使用输入流读取数据,而当程序许哟啊讲一些数据保存起来时,就要使用输出流。在java.io包中流的操作主要由字节流、字符流两大类,两类都有输入和输出操作。
- 在字节流中输出数据主要用OutputStream类完成,输入使用的是InputStream类。
- 在字符流中输出主要是使用Write类完成,输入主要是使用Reader类完成。
主要步骤:(1)使用File类打开一个文件(2)通过字节流或字符流的子类指定输出的位置(3)进行读/写操作(4)关闭输入/输出
3.1,字节流
字节流主要操作byte类数据,以byte数组为准,主要操作类是OutputStream类和InputStream类。
1,字节输出流:OutputStream类:OutputStream类是一个抽象类,如果要使用此类,则首先必须通过子类实例化对象。如果现在要操作的是一个文件,则可以使用FileOutputStream类,通过向上转型,可以为OutputStream类实例化。
方法 描述 public void close() throws IOException 关闭输出流 public void flush() throws IOException 刷新缓冲区 public void write(byte[] b) throws IOException 将一个byte数组写入数据流 public void write(byte[] b,int off, int len) throws IOException 将一个指定范围的byte数组写入数据流 public abstract void write(int b) throws IOException 将一个字节数据写入数据流 可以使用FileOutputStream子类,完成操作。
//第1步:使用File类找到一个文件 File file = new File("d:"+File.separator+"ysy.txt"); //第2步:通过子类实例化父类对象 OutputStream out = new FileOutputStream(file); //第3步:进行写操作 String str = "Hello Word!!!"; byte b[] = str.getBytes(); //字节流以byte数组为准 out.write(b); //第4步:关闭输入流 out.close();
文件不存在,则会自动创建,并将内容写入到文件之中。
2,追加新内容:之前的所有操作,如果重新执行程序,则肯定会覆盖文件中的已有内容,那么此时可以通过FileOutputStream类的另一个构造方法实例化,这样写入的时候就表示在文件中追加内容。
OutputStream out = new FileOutputStream(file,true);
换行:\r\n
3,字节输入流InputStream:既然程序可以向文件中写入内容,则可以通过InputStream从文件中把内容读取进来。InputStream本身也是一个抽象类,必须依靠其子类,如果现在从文件中读取,子类可你当时FileInputStream。
方法 描述 public int available() throws IOException 可以取得输入文件的大小 public void close() throws IOException 关闭输入流 public abstract int read() throws IOException 读取内容,以数字的方式读取 public int read(byte[] b) throws IOException 将内容读取到byte数组中,同时返回读入的个数 //第1步:使用File类找到一个文件 File file = new File("d:"+File.separator+"ysy.txt"); //第2步:通过子类实例化父类对象 InputStream input = new FileInputStream(file); //第3步:进行读操作 byte b[] = new byte[1024]; input.read(b); //第4步:关闭输入流 input.close(); System.out.println("内容为:"+new String(b)); ======================================== 内容为:燕双嘤
内容虽然已经被读取出来了,但是发现后面有很多个空格,这是因为开辟的byte数组大小为1024,而实际上内容只有6个字节,也就是说存在1018个空白空间,再将byte数组变为字符串时也将这1018个无用的空间转为字符串。
解决byte[]空间过大产生空格问题:
File file = new File("d:"+File.separator+"ysy.txt"); InputStream input = new FileInputStream(file); byte b[] = new byte[1024]; int len = input.read(b); input.close(); System.out.println("内容为:"+new String(b,0,len));
内容虽然没有显示大量空格,但是如果文件内容过小的化,会造成很多内存空间的浪费。
解决空间浪费问题:
File file = new File("d:"+File.separator+"ysy.txt"); InputStream input = new FileInputStream(file); byte b[] = new byte[(int)file.length()]; input.read(b); input.close(); System.out.println("内容为:"+new String(b));
上面程序都是再明确知道了具体数组大小的前提下开展的,如果此时不知道内容多大,只能通过是否读到文件尾部来读取文件。
File file = new File("d:"+File.separator+"ysy.txt"); InputStream input = new FileInputStream(file); byte b[] = new byte[(int)file.length()]; int temp = 0,len=0; while ((temp=input.read())!=-1){ b[len]=(byte)temp; len++; } input.close(); System.out.println("内容为:"+new String(b,0,len));
上面代码中要判断temp接收到的内容是否是-1,正常情况下是不会返回-1的,只有当输入流的内容已经读到底,才会返回这个数字,判断输入流中是否还有其他内容。
3.2,字符流
在程序中一个字符等于两个字节,Java中提供了Reader和Writer两个专门操作字符流的类。
1,字符输出流Writer:此类本身也是一个抽象类,如果使用此类,则肯定要使用其子类,此时如果向文件中写入内容,应该使用FileWriter的子类。
方法 描述 public abstract void close() throws IOException 关闭输入流 public void write(String str) throws IOException 将字符串输出 public void write(char[] chuf) throws IOException 将字符数组输出 public abstract void flush() throws IOException 强制性清空缓存 File file = new File("d:"+File.separator+"ysy.txt"); Writer out = new FileWriter(file); String str = "Hello World"; out.write(str); out.close();
追加文件内容:
Writer out = new FileWriter(file,true);
2,字符输入流Reader
方法 描述 public abstract void close() throws IOException 关闭输入流 public int read() throws IOException 读取单个字符 public int read(char[] cbuf) throws IOException 将内容读取到字符数组中,返回读取的长度 同FileInputStream
File file = new File("d:"+File.separator+"ysy.txt"); Reader reader = new FileReader(file); char c[] = new char[(int)file.length()]; reader.close(); System.out.println("内容为:"+new String(c));
3.3,字节流与字符流的区别
实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身的直接操作,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件。
此时没有关闭字节流操作,但是文件中也依然存在了输出的内容,证明字节流是直接操作文件本身。
程序运行后会发现文件中没有任何内容,这是因为字符流操作时使用了缓冲区,而在关闭字符流时会强制性地将缓冲区中的内容进行输出,但是如果程序没有关闭,则缓冲区中的内容是无法输出的,所以可以得出结论:字符流使用了缓冲区,字节流没有使用缓冲区。
问题:字节流和字符流那个好?
回答:字节流,因为所有的文件再磁盘中传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符只有在内存中才会形成,所以在开发中,字节流使用更为广泛。
3.4,转换流(OutputStreamWriter类和InputStreamReader类)
OutputStreamWriter:Writer的子类,将输出的字节流变为字符流,将一个字符流的输出对象变为字节流的输出对象。
InputStreamReader:Reader的子类,将输入的字节流变为字符流,将一个字节流的输入对象变为字符流的输入对象。
File file = new File("d:"+File.separator+"ysy.txt"); Writer out = new OutputStreamWriter(new FileOutputStream(file)); out.write("Hello World"); out.close();
File file = new File("d:"+File.separator+"ysy.txt"); Reader reader = new InputStreamReader(new FileInputStream(file)); char c[] = new char[1024]; int len = reader.read(c); reader.close(); System.out.println(new String(c,0,len));
问题:为啥没有把字符流转换成字节流呢?
回答:字节流比字符流的使用范围更广,但字符流比字节流操作方便。如果有一个流已经是字符流了,是一个用起来更方便的流,为什么要转换成字节流呢?反之,如果现在有一个字节流,但可以确定这个字节流的内容都是文本内容,那么它转换成字符流来处理就会方便一些,所以Java只提供了将字节流转换成字符流的转换流,没有提供将字符流转换成字节流的转换流。
4,其他流
4.1,内存操作流
前面所讲解的程序中输出和输入都是从文件中来的,当然也可以将输入和输出的位置设置在内存上。此时就要使用ByteArrayInputStream、ByteArrayOutputStream来完成内存的输入和输出内容。
方法 类型 描述 public ByteArrayInputStream(byte[] buf) 构造 将全部的内容写入内存中 public ByteArrayInputStream(byte[] buf,int offset,int length) 构造 将指定范围的内容写入到内存 public ByteArrayOutputStream() 构造 创建对象 public void write(int b) 普通 将内容从内存中输出 public static void main(String[] args) throws Exception{ String str = "HELLOWORLD"; ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int temp = 0; while ((temp=bis.read())!=-1){ char c = (char) temp; bos.write(Character.toLowerCase(c)); //将大写转变为小写 } String newStr = bos.toString(); try { bis.close(); bos.close(); }catch (Exception e){ e.printStackTrace(); } System.out.println(newStr); } ====================================== helloworld
内存操作流一般在生成一些临时信息时才会使用,如果将这些临时信息保存在文件中,则代码执行完后肯定还要删除这个临时文件会比较麻烦,这时使用内存操作流时最合适的。
4.2,管道流
管道流的主要作用时可以进行两个线程之间的通信(联动:操作系统中线程的管道通信),分为管道输出流(PipedOutputStream)和管道输入流(PipedInputStream)。如果要进行管道输出,则必须把输出流连接在输入流上。
class Send implements Runnable{ private PipedOutputStream pos = null; public Send(){ this.pos = new PipedOutputStream(); } public void run(){ String str = "Hello Word!!!"; try { this.pos.write(str.getBytes()); this.pos.close(); }catch (IOException e){ e.printStackTrace(); } } public PipedOutputStream getPos(){ return pos; } } class Receive implements Runnable{ private PipedInputStream pis = null; public Receive(){ this.pis = new PipedInputStream(); } public void run(){ byte b[] = new byte[1024]; int len = 0; try { len = this.pis.read(b); this.pis.close(); }catch (Exception e){ e.printStackTrace(); } System.out.println("接收的内容为:"+new String(b,0,len)); } public PipedInputStream getPis(){ return pis; } } public class HelloWord { public static void main(String[] args) throws Exception{ Send s = new Send(); Receive r = new Receive(); try { s.getPos().connect(r.getPis()); //连接管道 }catch (Exception e){ e.printStackTrace(); } new Thread(s).start(); new Thread(r).start(); } }
4.3,打印流
打印流时输出信息量最方便的类,主要包含字节打印流(PrintStream)和字符打印流(PrintWriter)。打印流提供了非常方便的打印功能,可以打印任务的数据类型,如小数、整数、字符串等。
方法 类型 描述 public PrintStream(File file) throws FileNotFoundException 构造 通过一个File对象实例化PrintStream类 public PrintStream(OutputStream out) 构造 接收OutputStream对象,实例化PrintStream类 public PrintStream printf(Locale l,String format,Object..args) 普通 根据指定的Locale进行格式化输出 public PrintStream printf(String format,Object...args) 普通 根据本地环境格式化输出 public void print(boolean b) 普通 此方法被重载很多次,输出任意数据 public void println(boolean b) 普通 此方法被重载很多次,输出任意格式数据后换行 有一个构造方法可以直接接收OutputStream类的实例,这是因为与OutputStream类相比,PrintStream类可以更加方便地输出数据,这就好像将OutputStream类重新包装了一下,使之输出更方便。
PrintStream ps = new PrintStream(new FileOutputStream(new File("d:"+File.separator+"ysy.txt"))); ps.print("hello"); ps.print("world!!!"); ps.print("1+1="+2); ps.close();
在输出内容的时候明显比OutputStream直接输出方便了很多。
使用打印流进行格式化:
字符 描述 %s 表示内容为字符串 %d 表示内容为整数 %f 表示内容为小数 %c 表示内容为字符