Java I/O 流操作(三)文件流、打印流、缓冲流、SequenceInputStream

35 篇文章 11 订阅

Java I/O 系列文章目录:

 

本文涉及到的I/O类

  1. File
  2. Properties
  3. FileInputStream
  4. FileOutputStream
  5. PrintStream
  6. PrintWriter
  7. BufferedReader
  8. InputStreamReader
  9. SequenceInputStream

File文件操作

用来将文件或者文件夹封装成对象,方便对文件与文件夹进行操作,File 对象可以作为参数传递给流的构造函数.

下面来看一下 File 类的构造函数:
 

私有构造函数:

  • private File(String pathname, int prefixLength)
  • private File(String child, File parent)

公有构造函数:

  • public File(String pathname)
  • public File(String parent, String child)
  • public File(File parent, String child)
  • public File(URI uri)

 

一个小的测试 Demo:

File file =new File("a.txt");

File file2 =new File("C:\\file\\test\\a.txt");
File d =new File("C:\\file\\test\\");
File file3 =new File(d, "a.txt");
File file4 =new File("C:\\file\\test\\","a.txt");//这和file3是一回事
System.out.println(file +"\r\n" + file2 + "\r\n" + file3 +"\r\n" + file4 + "\r\n");


控制台输出为:

a.txt
C:\file\test\a.txt
C:\file\test\a.txt
C:\file\test\a.txt

 

 
但是我们在程序中,文件的路径 分隔符 只能在 windows 平台下,在 Linux 就不能用了,怎么现在跨平台呢?
 
通过  File.separator 来实现跨平台:
 
File file5 = new File("C:"+File.separator+"file"+File.separator+"test"+File.separator+"", "a.txt");

 

File 常用方法

接下来介绍下 File 类的常用方法了:
 
 
  • createNewFile: 在指定位置创建文件,如果文件不存在,则创建成功返回 true ,如果文件已经存在,则不创建,返回 false 这和输出流不同,输出流一旦实例化就会创建文件,文件不存在创建,存在则覆盖.
  • mkdir:创建文件夹
  • mkdirs:创建文件夹,包括父文件夹
  • delete:根据抽象的路径名称删除文件,如果程序出现异常没有执行到该行代码,那么这个文件就不会被执行,有人会说放在 finally 执行,但是如果一个程序正在使用该文件,那么这个文件还是不能被删除.
  • deleteOnExit:这个方法不会发生这种情况,因为该方法在虚拟机停终止,文件就会被删除.
  • exists:表示抽象的路径名称所指示的文件或文件夹是否存在
  • canExecute:应用程序能否执行抽象路径名称所指示的文件
  • canRead:应用程序能否读取抽象路径名称所指示的文件
  • canWrite:应用程序能否修改抽象路径名称所指示的文件
  • isDirectory:抽象路径名称所指示的是不是文件夹
  • isFile:抽象路径名称所指示的是不是文件
  • isAbsolute:判断是否是绝对路径
  • getAbsoluteFile:返回 File 对象的绝对路径,但是他把这个字符串封装成了 File 对象返回 这也是和 getAbsolutePath 的主要区别
  • getAbsolutePath:返回 File 对象的绝对路径
  • getPath:返回 File 对象的路径,如果File里的抽象路径名称是绝对路径,如果是相对路径,就返回相对路径
  • getName:如果 File 对象里的抽象路径名称指定的是目录,那么返回的目录最后的文件夹名称,如果是文件,则返回文件的名称
  • getParent:如果 File 对象里的抽象路径名称指定的是目录,那么返回的目录最后的文件夹的上一个文件夹的名称,如果是文件,则返回文件所在目录的名称,如果象路径名称指定的是相对路径,有可能返回 null,如: File file = new File("a.txt"); 那么该 file 的父目录就是 null.
  • getParentFile:这和 getParent 的区别就是,这个方法把 getParent 方法的返回值封装成了 File 对象
  • length:返回文件的大小
  • renameTo(File dest):重命名
  • listRoots:返回系统盘符的名称 如 C、D 盘等
  • list:返回 File 目录下的所有文件 如果抽象的路径名称指定的是一个文件将会出现空指针异常;并且该目录一定要存在
  • listFiles:这个方法和 list() 方法的区别是 list() 方法是返回一个个字符串的,而 listFiles 是返回一个个 File 对象.那么就可以通过 File 对象获取文件的信息了,所以 File[] listFiles() 方法更使用一些
  • listFiles(FileFilter filter):根据过滤器,返回文件列表
 
 
 

File 常用方法 Demo 讲解

Demo1:isDirectory、isFile 方法

File file =new File("a.txt");
System.out.println("isDirectory:"+file.isDirectory());  // false
System.out.println("isFile:"+file.isFile());  // false

发现它既不是文件也不是目录,为什么会出现这样的情况呢?

原来用上面的两个方法判断时候,需要先判断该抽象的路径名称所指定的 file 和 directory 是否存在,存在才能进行判断.
 

Demo2 :renameTo 方法
 
File f =new File("C:\\test\\test.txt");
File f2 =new File("C:\\test\\testDemo.txt");
f.renameTo(f2);

如果盘符或者目录不同这就成了剪切了

 

Demo3 :listRoots、list 方法

// listRoots

for(File file: File.listRoots()){
    System.out.println(file);
}

// 控制台输出:
C:\
D:\
E:\
F:\
G:\


// list

File file =new File("C:\\");
for (String name : file.list()) {
System.out.println(name);
}

输出结果为:C 盘目录下的所有文件和目录包含隐藏文件,但是不包含子文件夹里的文件

 

Demo4:list(FilenameFilter filter)
 
 
public static void method_1() {

    final File file =new File("D:\\");
    String[] names = file.list(new FilenameFilter() {
	    publicboolean accept(File dir, String name) {
	        return name.endsWith(".java");
	    }
    });

    for (String name : names)
        System.out.println(name);

}

Demo5:listFiles、listFiles(FileFilter filter)

 
/*
File[] listFiles() 这个方法和 list() 方法的区别是 list() 方法是返回一个个字符串的,
而 listFiles() 是返回一个个 File 对象,那么就可以通过 File 对象获取文件的信息了,所以File[]listFiles() 方法更使用一些。
*/

File file =new File("C:\\");
for(File file2 : file.listFiles()){
	System.out.println(file2.getName());
}



/*

listFiles() 同样也有自己的过滤功能.通过 listFiles(FileFilter filter) 方法可以实现.

通过普通的递归实现列出目录下的所有文件

*/
publicstatic void main(String[] args) {
	File file =new File("E:\\MyeclipseWorkbenck\\shop");
	method_1(file);
}

publicstatic void method_1(File file) {
	System.out.println(file.getAbsolutePath());
	File[] files = file.listFiles();
	for (File file2 : files) {
		if(file2.isDirectory()){
			method_1(file2);
		} else {
			System.out.println(file2.getName());
		}

	}
}

 

Demo6:delete、deleteDir

 

/*
删除带目录的文件,如果直接使用 delete 方法,则不能删除成功, 
因为 Java 删除文件的原理是这样的, 把最里面的文件删除,然后删除文件夹
*/
publicstatic void main(String[] args) {
    File file =new File("D:\\java");
    deleteDir(file);
}
publicstatic void deleteDir(File file){
    File[] files = file.listFiles();
    for(File file2 : files){
	        //如果是目录
	    if(file2.isDirectory()){
	        //则使用递归 调用本身
	        deleteDir(file2);
	    } else {
	        //不是目录就直接删除
	        file2.delete();
	    }
    }
    //然后删除空文件夹
    file.delete();
}

Demo7 将 File 路径存入集合中(使用递归和IO操作)

 
public class JavaFileItem {
	public static void main(String[] args) {
	    List<File> list =new ArrayList<File>();
	    saveJavaFilePath(new File("E:\\MyeclipseWorkbenck\\HeimaTest"), list);
	    writeFilePath(list,"javaItem.txt");
	}
	
	//则把java文件放在集合中
	publicstatic void saveJavaFilePath(File dir, List<File> list) {
	    File[] files = dir.listFiles();
	    for (File file : files) {
	        if (file.isDirectory()) {
	            saveJavaFilePath(file, list);
	        }else {
	    //如果是java文件,则把该文件放在集合中
	        if (file.getAbsolutePath().endsWith(".java"))
	            list.add(file);
	        }
	    }
	}
	
	//把java的路径信息写入文件中
	publicstatic void writeFilePath(List<File> files, String targetFile) {
		BufferedWriter buffw =null;
		try {
		        FileWriter fw =new FileWriter(targetFile);
		        buffw =new BufferedWriter(fw);
		        for (File file : files) {
		            //把java文件的绝对路径写入文件
		            buffw.write(file.getAbsolutePath());
		            //换行
		            buffw.newLine();
		            //使用字符流注意刷新
		            buffw.flush();
		        }
		    }catch (IOException e) {
		        e.printStackTrace();
		    }finally {
		        try {
		            if (buffw !=null)
		                buffw.close();
		            }catch (IOException e) {
		                e.printStackTrace();
		            }
		    }
	 }
}

 

 

 PrintStream 和 PrintWriter

 
下面学习打印流对象 PrintStream 、PrintWriter
 
先来看下 PrintStream 官方描述:
 
A PrintStream adds functionality to another output stream, namely the ability to print representations of various data values conveniently. 

Two other features are provided as well. Unlike other output streams, 
a PrintStream never throws an IOException; 
instead, exceptional situations merely set an internal flag that can be tested via the checkError method. Optionally, 
a PrintStream can be created so as to flush automatically; 
this means that the flush method is automatically invoked after a byte array is written, one of the println methods is invoked, 
or a newline character or byte ('\n') is written.

All characters printed by a PrintStream are converted into bytes using the platform's default character encoding. 
The PrintWriter class should be used in situations that require writing characters rather than bytes.

大概的意思是说 PrintStream 相比其他的输出流增强了许多功能,也就是很方便的打印数据,另外 PrintStream 还可以自动刷新,也就是说一个字节数组被写入或者调用 println 方法或者写入了换行字符或者字节 \n 后这个 flush 方法会被自动调用

 

PrintStream 的常用的构造方法:
 
 
  • PrintStream(File file)
  • PrintStream(OutputStream out)
  • PrintStream(OutputStream out, boolean autoFlush) 
  • PrintStream(String fileName)
  • PrintStream(OutputStream out, boolean autoFlush, String encoding)
 
然后再来看一下 PrintWriter 的 API 描述
 
Prints formatted representations of objects to a text-output stream. 
This class implements all of the print methods found in PrintStream. 
It does not contain methods for writing raw bytes, for which a program should use unencoded byte streams.
Unlike the PrintStream class, if automatic flushing is enabled it will be done only when one of the println, printf, 
or format methods is invoked, rather than whenever a newline character happens to be output.
These methods use the platform's own notion of line separator rather than the newline character.
Methods in this class never throw I/O exceptions, although some of its constructors may. 
The client may inquire as to whether any errors have occurred by invoking checkError().

大概的意思是说,PrintWriter 类不像 PrintStream 类,如果设置了自动刷新 (PrintWriter(Writer out, boolean autoFlush) 或者通过 PrintWriter(OutputStream out, boolean autoFlush) 设置),只有当调用 println()printf() format() 方法才会被刷新,
而不是无论什么时候只要出现换行字符就会输出,这些方法使用的是平台自己的行分隔符标准,而不是使用换行字符。

 

接着看一下他的主要构造方法:
 
 
  • PrintWriter(File file)
  • PrintWriter(String fileName)
  • PrintWriter(OutputStream out)
  • PrintWriter(OutputStream out, boolean autoFlush) 
  • PrintWriter(Writer out)
  • PrintWriter(Writer out, boolean autoFlush)
 
 
比较 PrintStream 和 PrintWriter 的构造方法的最大区别 PrintWriter 可以接受字符输出流 Writer 而 PrintStream 不能,所以PrintWriter 更常用。下面通过一个实例来运用它:
 
public static void print()throws IOException{
    //获取键盘输入
    BufferedReader buffr =new BufferedReader(new InputStreamReader(System.in));
    //目的是控制台
    PrintWriter pw =new PrintWriter(System.out);
    String value =null;
    while((value=buffr.readLine())!=null){
        pw.write(value);
        //注意刷新
        pw.flush();
    }
    pw.close();
    buffr.close();

}

控制太输出如下:

发现他没有换行,看上去不美观:怎么实现换行呢?我们来看一下这两个个方法

  • println(String x)
  • println()

API 是这样解释 println(String x) 方法的:

Prints a String and then terminates the line. 
This method behaves as though it invokes print(String) and then println().

先打印字符串然后终止这行,也就是先调用 print(String str) 方法然后在调用 println() 方法.

API 是这样解释 println() 方法的:

Terminates the current line by writing the line separator string. 
The line separator string is defined by the system propertyline.separator, 
and is not necessarily a single newline character ('\n').


 
也就是通过行分隔符终止当前行,行分隔符通过系统属性 line.separator 设置的而不需要简单的换行符 '\n'

是不是我们通过 println();方法就可以解决上面的问题呢?

 
while((value=buffr.readLine())!=null){
    pw.write(value);
    pw.println();//终止一行
    //手动刷新
    pw.flush();
}

 

 
其实还有方法可以解决,因为上面说了可以通过设置是否自动刷新而不是手动刷新,所以可以把 pw.flush(); 注释掉如:

 

PrintWriter pw =new PrintWriter(System.out,true);//设置自动刷新
String value =null;
while((value=buffr.readLine())!=null){
    pw.write(value);
    pw.println();//因为println()printf()format()都会触发刷新
    //pw.flush();
}

 

 
我们还可以不使用write(),使用 println(String) 就会更加简单:
 
PrintWriter pw =new PrintWriter(System.out,true););//设置自动刷新
String value =null;
while((value=buffr.readLine())!=null){
    pw.println(value);();//因为println()printf()format()都会触发刷新
}

 

我们还可以想一想下面程序:
 
PrintWriter pw =new PrintWriter(System.out,true););//设置自动刷新
String value =null;
while((value=buffr.readLine())!=null){
    pw.write(value+"\r\n");//末尾加上换行符是否会触发自动刷新呢?
}

 

答案是否定的。
 
在 PrintWriter 的 API 有这样一句话:
 
These methods use the platform's own notion of line separator rather than the newline character.
 
也就是说是否会触发自动刷新,Java 内部不是使用换行符 ("\r\n") 而是使用平台内部的行分隔符(当然在 windows 他们是一样的)。
 
那么我们可不可以模拟这个 Java 内部所谓的行分隔符呢?在学习 System 类的时候我们可以得到系统的环境变量。
 
在里面我们发现了行分隔符 "line.separator"
 

我们来测试一下:

//设置自动刷新
PrintWriter pw =new PrintWriter(System.out,true););
while((value=buffr.readLine())!=null){
    //我们使用writer方法,里面的字符串参数加上行分隔符,
    //因为java api里面说println()因为内部加上了行分隔符才会触发刷新的,
    // 那么现在我们手工加上行分隔符会触发刷新吗?
    pw.write(value+System.getProperty("line.separator"));
}

测试的结果还是不行!

 
那么为什么? Java 不是要系统的行分隔符吗?我现在已经加上了行分隔符还不行呢?
我们来看一下 PrintWriter 的源代码是怎么定义 println(String) 方法的:
 
public void println(String x) {
        synchronized (lock) {
            print(x);//先打印数据
            println();//再打印行分隔符,我们在进入println()是怎么定义的,
        }
}

 

 
println() 的定义:
 
    public void println() {
        newLine();//打开是怎么样定义的
    }

 

newLine() 方法的定义:
 

privatevoid newLine() {
    try {
        synchronized (lock) {
        ensureOpen();
        out.write(lineSeparator);//写入行分隔符
        if (autoFlush)//判断是否设置刷新
            out.flush();//如果设置为true,那么调用flush()方法
        }
    }catch (InterruptedIOException x) {
        Thread.currentThread().interrupt();
    }catch (IOException x) {
        trouble =true;
    }
}


经过查看 writer()方法的源代码,没有发现他去判断是否刷新,

结论:尽管我们在使用 writer(String) 方法的 String 参数后面加上了行分隔符,但是在 writer 方法里面根本不去判断是否设置了 autoFlush = true

所以我们在 writer(String) 方法的 String 参数后面加上了行分隔符也不能达到预期的效果!

如果我们想更加了解 PrintStream和 PrintWriter 的区别,查看源代码是最好的帮手。

PrintStream 是字节输出流的子类,PrintWriter是字符输出流的子类。

 

SequenceInputStream 序列流

简单的说就是把多个字节输入流合并成一个字节输入流,构造方法:
 
  • SequenceInputStream(Enumeration<? extends InputStream> e) 里面传一个Enumeration里面的元素使用了泛型限定.
  • SequenceInputStream(InputStream s1,InputStream s2) 里面传入两个字节输入流对象

实例: 怎么把 3 个文件的内容输入到一个文件中?
 
//通过Vector可以获取Enumeration
Vector<InputStream> vector = new Vector<InputStream>();
vector.add(new FileInputStream("C:\\1.txt"));
vector.add(new FileInputStream("C:\\2.txt"));
vector.add(new FileInputStream("C:\\3.txt"));
//实例化合并流
SequenceInputStream sequence = new SequenceInputStream(vector.elements());
InputStreamReader isr = new InputStreamReader(sequence);
BufferedReader fr=new BufferedReader(isr);
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\123.txt"));
String value = null;
while((value=fr.readLine())!=null){
	bw.write(value);
	bw.newLine();
}
bw.close();
sequence.close();

 

 

FileInputStream 实现文件切割

 
需求: 一个 MP3 文件切割成几个文件
 
publicstatic void cut()throws Exception {
    InputStream is =new FileInputStream("C:\\mp3\\卓依婷- 好人好梦.mp3");
    OutputStream os =null;
    byte[] buffer =new byte[1024*1024];
    int len=0;
    int name = 1;
    while ((len=is.read(buffer))!=-1) {
        //每一次循环都会生成一个文件,这样就实现了切割
        os =new FileOutputStream("C:\\mp3\\part\\"+(name++)+".part");
        //每个文件写1M,最后一个文件可能没有1M
        os.write(buffer,0,len);
        os.close();
    }
    is.close();
}

 

 
发现 part 目录多了4个文件,并且大小和以前的文件一样:
 
 

从而也验证了前面 4 个文件都是 1M 最后一个没有 1M 的结论
 
现在怎么把上面的 4 个文件重新合并成一个文件
 
publicstatic void merger()throws Exception {
    List<InputStream> list =new ArrayList<InputStream>();
    for (int i = 1; i <= 4; i++) {
        list.add(new FileInputStream("C:\\mp3\\part\\" + (i) +".part"));
    }
    final Iterator<InputStream> iterator = list.iterator();
    Enumeration<InputStream> enumeration =new Enumeration<InputStream>(){
    public boolean hasMoreElements() {
        return iterator.hasNext();
    }
    public InputStream nextElement() {
    return iterator.next();
}
};
    SequenceInputStream sequence =new SequenceInputStream(enumeration);
    OutputStream os =new FileOutputStream("C:\\mp3\\part\\卓依婷- 好人好梦.mp3");
    byte[] buffer =new byte[1024];
    int len = 0;
    while((len=sequence.read(buffer))!=-1){
        os.write(buffer,0,len);
    }
    os.close();
    sequence.close();
}

 

 

 

如果你觉得本文帮助到你,给个关注和赞呗!

您还可以查看我的 Github 查看 Java 技术栈:https://github.com/chiclaim/Java

不仅包括 Java I/O,还有 Java 虚拟机、多线程、集合框架。由于篇幅原因,只展示下,Java I/O 部分:

 

Java 技术栈 I/O

 

 

 

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiclaim

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值