Java I/O流操作(三)---File文件操作及打印流和序列流-合并流
- -------------------------------------------File文件操作---------------------------------------------
用来将文件或者文件夹封装成对象
方便对文件与文件夹进行操作
File对象可以作为参数传递给流的构造函数.
下面来看一下File类的构造方法:
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 file5 = new File("C:"+File.separator+"file"+File.separator+"test"+File.separator+"", "a.txt");
接下来就是File类的常用方法了:
创建方法:
这和输出流不同,输出流一旦实例化就会创建文件,文件不存在创建,存在则覆盖.
mkdir ()创建文件夹
mkdirs ()创建文件夹,包括父文件夹
删除方法:
boolean |
delete() |
void |
deleteOnExit() |
这两个方法是有区别的:
delete ()根据抽象的路径名称删除文件,如果程序出现异常没有执行到该行代码,那么这个文件就不会被执行,有人会说放在finally执行,但是如果一个程序正在使用该文件,那么这个文件还是不能被删除.
deleteOnExit()这个方法不会发生这种情况,因为该方法在虚拟机停终止,文件就会被删除.
判断方法:
isDirectory
()抽象路径名称所指示的是不是文件夹
isFile
()抽象路径名称所指示的是不是文件
isAbsolute
()判断是否是绝对路径
File file =new File("a.txt");
System.out.println("isDirectory:"+file.isDirectory());
System.out.println("isFile:"+file.isFile());
发现控制台输出
isDirectory:falseisFile:false
发现它既不是文件也不是目录,为什么会出现这样的情况呢?
原来用上面的两个方法判断时候,需要先判断该抽象的路径名称所指定的file和directory是否存在.存在才能进行判断.
获取信息:
File
getAbsoluteFile
()
返回File对象的绝对路径,但是他把这个字符串封装成了File对象返回 这也是和
getAbsolutePath的主要区别
String getAbsolutePath () 返回File对象的绝对路径
String getAbsolutePath () 返回File对象的绝对路径
String
getPath
()
返回File对象的路径,如果File里的抽象路径名称是绝对路径,如果是相对路径,就返回相对路径
String getName ()如果File对象里的抽象路径名称指定的是目录,那么返回的目录最后的文件夹名称,如果是文件,则返回文件的名称
String getName ()如果File对象里的抽象路径名称指定的是目录,那么返回的目录最后的文件夹名称,如果是文件,则返回文件的名称
String
getParent
()
如果File对象里的抽象路径名称指定的是目录,那么返回的目录最后的文件夹的上一个文件夹的名称,如果是文件,则返回文件所在目录的名称,如果象路径名称指定的是相对路径,有可能返回null,如:File file = new File("a.txt");那么该file的父目录就是null.
File getParentFile ()这和 getParent的区别就是 ,这个方法把getParent方法的返回值封装成了File对象
long length () 返回文件的大小
File getParentFile ()这和 getParent的区别就是 ,这个方法把getParent方法的返回值封装成了File对象
long length () 返回文件的大小
例如:
File f =new File("C:\\test\\test.txt");
File f2 =new File("C:\\test\\testDemo.txt");
f.renameTo(f2);
如果盘符或者目录不同这就成了剪切了
文件列表:
File []
listRoots
()
返回系统盘符的名称 如:C D盘等
String[] list () 返回File目录下的所有文件 如果抽象的路径名称指定的是一个文件将会出现空指针异常;并且该目录一定要存在
例如1:
String[] list () 返回File目录下的所有文件 如果抽象的路径名称指定的是一个文件将会出现空指针异常;并且该目录一定要存在
例如1:
for(File file: File.listRoots()){
System.out.println(file);
}
输出结果为:
C:\
D:\
E:\
F:\
G:\
例如2:
File file =new File("C:\\");
for (String name : file.list()) {
System.out.println(name);
}
输出结果为:C盘目录下的所有文件和目录包含隐藏文件,但是不包含子文件夹里的文件
文件过滤:
例如我想输出在某个文件夹下的java文件,那么这时候就要用到过滤文件:
String[]
list
(
FilenameFilter
filter)
我们查看API不难发现,
FilenameFilter
是一个接口里面只有一个方法booleanaccept(File dir,String name)
publicstatic 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);
}
下面来看一下:
File[]
listFiles
()
;这个方法和
list
()
方法的区别是
list
()
方法是返回一个个字符串的,而
listFiles
()是返回一个个File对象.那么就可以通过File对象获取文件的信息了,所以File[]listFiles()方法更使用一些.例如:
File file =new File("C:\\");
for(File file2 : file.listFiles()){
System.out.println(file2.getName());
}
通过递归实现列出目录下的所有文件
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());
}
}
}
删除带目录的文件,如果直接使用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();
}
怎么把java文件的路径存在一个文件里面,方便以后查找.
publicclass JavaFileItem {
publicstatic 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();
}
}
}
}
这里就是使用递归和IO操作的结合了.
-------------------------关于Properties类:--------------------------------------------
关于Properties类:
Properties是Hashtable的子类,也就是说它具备map结婚的特点,而且它里面存储的键值对都是字符串
他是集合和IO相结合的集合容器
该对象的特点是可以用于键值对形式的配置文件
getProperty
(
String
key)
根据key从Properties对象中获取值
setProperty ( String key, String value) 为Properties对象设置key和value
store ( OutputStream out, String comments) 把Properties对象储存到一个文件里面去,底层通过字节输出流处理
store ( Writer writer, String comments) 这是JDK1.6后才有的,直接使用字符输出流就可以把Properties对象储存到文件中
load ( InputStream inStream) 读取属性列表从字节输入流中,加载某个键值对文件,使得Properties与该文件相关联
load ( Reader reader) 这是JDK1.6后才出现的,读取属性列表从字符输入流中,
setProperty ( String key, String value) 为Properties对象设置key和value
store ( OutputStream out, String comments) 把Properties对象储存到一个文件里面去,底层通过字节输出流处理
store ( Writer writer, String comments) 这是JDK1.6后才有的,直接使用字符输出流就可以把Properties对象储存到文件中
load ( InputStream inStream) 读取属性列表从字节输入流中,加载某个键值对文件,使得Properties与该文件相关联
load ( Reader reader) 这是JDK1.6后才出现的,读取属性列表从字符输入流中,
模拟限制软件使用次数:
publicstatic void count()throws IOException{
Properties properties =new Properties();
File file =new File("count.ini");
if(!file.exists()){
file.createNewFile();
}
InputStream is =new FileInputStream(file);
properties.load(is);
String times = properties.getProperty("times");
int count = 0;
if(times!=null){
count = Integer.parseInt(times);
if(count>=5)
System.out.println("你使用的次数已到,请续费!");
}
count++;
OutputStream os =new FileOutputStream(file);
properties.setProperty("times", count+"");
properties.store(os,"count times");
os.close();
is.close();
}
----------------------------------打印流------------------------
下面学习打印流对象PrintStream PrintWriter
首先
PrintStream
大概的意思是说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)
PrintStream ( OutputStream out)
PrintStream ( OutputStream out, boolean autoFlush)
PrintStream ( String fileName)
PrintStream ( OutputStream out, boolean autoFlush, String encoding)
看一下
PrintWriter的API
大概的意思是说,PrintWriter类不像PrintStream类,如果设置了自动刷新(PrintWriter(Writer out, boolean autoFlush) 或者通过PrintWriter(OutputStream out, boolean autoFlush) 设置),只有当调用println()printf() format()方法才会被刷新,而不是无论什么时候只要出现换行字符就会输出,这些方法使用的是平台自己的行分隔符标准,而不是使用换行字符.
接着看一下他的主要构造方法:
PrintWriter
(
String
fileName)
PrintWriter ( OutputStream out)
PrintWriter ( OutputStream out, boolean autoFlush)
PrintWriter ( Writer out)
PrintWriter ( Writer out, boolean autoFlush)
PrintWriter ( OutputStream out)
PrintWriter ( OutputStream out, boolean autoFlush)
PrintWriter ( Writer out)
PrintWriter ( Writer out, boolean autoFlush)
比较PrintStream和PrintWriter的构造方法的最大区别PrintWriter可以接受字符输出流Writer而PrintStream不能,所以PrintWriter更常用.
下面通过一个实例来运用它:
publicstatic 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()
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 property
line.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){
pw.write(value+System.getProperty("line.separator"));//我们使用writer方法,里面的字符串参数加上行分隔符,因为java api里面说println()因为内部加上了行分隔符才会触发刷新的,那么现在我们手工加上行分隔符会触发刷新吗?
}
测试的结果还是不行!
那么为什么?java不是要系统的行分隔符吗?我现在已经加上了行分隔符还不行呢?
我们来看一下PrintWriter的源代码是怎么定义println(String)方法的
println(String)的定义:
publicvoid println(String x) {
synchronized (lock) {
print(x);//先打印数据
println();//再打印行分隔符,我们在进入println()是怎么定义的,
}
}
println()的定义:
publicvoid 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
(
Enumeration
<? extends
InputStream
> e)
里面传一个
Enumeration里面的元素使用了泛型限定.
SequenceInputStream ( InputStream s1, InputStream s2) 里面传入两个字节输入流对象
实例:怎么把3个文件的内容输入到一个文件中:
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();
-----------------------------------------切割流------------------------------------------------
需求 :一个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>(){
publicboolean 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();
}
转载请注明出处 : http://blog.csdn.net/johnny901114/article/details/8710433