I/O ,I 是 Input (输入)的缩写,O是Output (输出) 的缩写,众所周知,人与人之间想要沟通交流,就需要讲彼此都能听懂的语言,比如大家都统一说英语。
人类如果想和计算机交流,也需要共同的语言,而计算机只懂二进制0101代码,然而人类发现很难理解和学懂这门语言,于是乎有了一个“翻译机“——高级编程语言,比如C#,Java 等,高级编程语言通过一种解释器的程序可以将人类容易学习的高级语言代码解释翻译成二进制代码,于是乎计算机便能听懂我们的命令,做我们想让它做的事情了。
同样,程序和程序之间也有交流方式,那就是I/O.
I/O 其实在某种意义上是一个应用程序与文件,网络连接,控制台 通信交流的一套API.
在Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。掌握了这些就掌握了Java I/O的精髓了。
正如上面所提到,如果我们想对文件操作,可以使用File相关的类,来进行文件新建,复制,移动,遍历,删除等操作。
如果想对文件的内容进行操作,我们则可以考虑两种方式,一种是读取字节流的方式,一种是读取字符的方式。
也许你会困惑什么时候用哪一种方式会更好呢?
我的建议:
如果操作的文件内容中没有中文,那么一般用字节流就行(InputStream和OutputStream),当然用字符流(Reader 和Writer)也没啥问题。
但是如果操作的文件中有中文,那么就不得不用字符流来操作了,使用的时候要注意字符编码。
这是为什么呢?
因为一个中文文字至少占用2个字节,一个英文文字一般至少占据一个字节即可。
Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。
- 字节流:数据流中最小的数据单元是字节
- 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。
想要正确显示中文汉字,则至少需要两个字节的组合来读写,如果用字节流来读写,可能看到的就是乱码。
当不同数量的字节组合就产生了不通的字符编码。比如GB2312能完美的支持简体中文和英文,一个中文文字占用2个字节,一个英文字母同样占用2个字节。
关于字符编码的讨论细节,当年大一那时候偶然发现写过一篇有趣的博文,当执行下面这条命令,C语言打印出来是 “我爱你“”三个字
printf("%c%c%c%c%c%c\n",206,210,176,174,196,227);
显示打印结果:
有兴趣可以看下 C语言中两位ASCLL码可以表示汉字
JDK6 在线中文文档:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
JDK7: http://tool.oschina.net/apidocs/apidoc?api=jdk_7u4
2. Java 的 I/O 的基本分类
I/O 问题是任何编程语言都无法回避的问题,可以说 I/O 问题是整个人机交互的核心问题,因为 I/O 是机器获取和交换信息的主要渠道。
我们通过高级语言编写程序代码来进行I/O操作, 从而实现人机交互。
在Java.io包中最重要的就是5个类和一个接口。
5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。
掌握了这些就掌握了Java I/O的精髓了。
Java 的 I/O 操作类在包 java.io 下,大概有将近 80 个类,但是这些类大概可以分成四组,分别是:
- 基于字节操作的 I/O 接口:InputStream 和 OutputStream
- 基于字符操作的 I/O 接口:Writer 和 Reader
- 基于磁盘操作的 I/O 接口:File
- 基于网络操作的 I/O 接口:Socket
前两组主要是根据传输数据的数据格式,后两组主要是根据传输数据的方式,虽然 Socket 类并不在 java.io 包下,但是我仍然把它们划分在一起,因为我个人认为 I/O 的核心问题要么是数据格式影响 I/O 操作,要么是传输方式影响 I/O 操作,也就是将什么样的数据写到什么地方的问题,I/O 只是人与机器或者机器与机器交互的手段,除了在它们能够完成这个交互功能外,我们关注的就是如何提高它的运行效率了,而数据格式和传输方式是影响效率最关键的因素了。我们后面的分析也是基于这两个因素来展开的。————摘自IBM 学习文档
java.io包里有4个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流。
其他各种各样的流都是由这4个派生出来的。
在JDK 1.4 之后引入了 NIO,NIO 相比传统的IO流做了改进,其中一个亮点就是引入了缓冲区的概念。
2.1 File 类
关于 File 这个对象我们需要注意的是,它不仅可以用来表示一个文件也可以表示一个文件夹。
File 类可以写一个实用的工具类。
现在假如我们需要查询某一个文件夹下所有的文件中是.java 类型的文件列表
那么我们需要两个参数,一个是查询文件夹路径名称,第二个参数是文件的类型
如果不递归的话,可以这样调用
List<File> fileList=SmartFileUtils.lookFollder("C:\\Users\\fairy\\Pictures\\国家地理馆",".jpg");//只查询图片文件 不递归
//List<File> fileList=SmartFileUtils.lookFollder("C:\\Users\\fairy\\Pictures\\国家地理馆");//查询所有文件夹和文件 不递归
for (File file : fileList) { System.out.println(file.getAbsolutePath()); }
递归查询当前文件夹包括子文件夹下所有的java 文件列表,我们可以这样调用:
public static void main(String[] args) { FileTreeInfo fileTreeInfo=SmartFileUtils.watchFolder(".",".java");//只查询java文件 递归
//FileTreeInfo fileTreeInfo=SmartFileUtils.watchFolder(".");//查询所有文件夹和文件 递归
System.out.println(fileTreeInfo.toString());
}
如果想单独获取查询结果的文件夹和文件集合列表,这样调用即可
List<File> fileList=fileTreeInfo.fileList;
List<File> folderList=fileTreeInfo.folderList;
SmartFileUtils.java
import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; /** * @author fairy 查询文件夹下的文件 ***/ public class SmartFileUtils { /** * 查看某个文件夹下所有文件和文件夹列表 * 无递归 * */ public final static List<File> lookFollder(String startDir) { return lookFollderInfo(new File(startDir), ".*"); } /** * 查看某个文件夹下指定类型的文件和文件夹列表 * 无递归 * */ public final static List<File> lookFollder(String startDir, String regex) { return lookFollderInfo(new File(startDir), ".*\\" + regex); } private final static List<File> lookFollderInfo(File rootFolder, final String regex) { return Arrays.asList( rootFolder.listFiles(new FilenameFilter() { private Pattern pattern = Pattern.compile(regex); @Override public boolean accept(File dir, String name) { // TODO Auto-generated method stub return pattern.matcher(name).matches(); } })); } /** * 查看某个文件夹下所有的文件和文件夹列表 * 递归遍历 * */ public final static FileTreeInfo watchFolder(String startDir) { return watchDirs(new File(startDir), ".*"); } /** * 查看某个文件夹下指定类型的文件和文件夹列表 * 递归遍历 * */ public final static FileTreeInfo watchFolder(String startDir, String regex) { return watchDirs(new File(startDir), ".*" + regex); } private final static FileTreeInfo watchDirs(File startDir, String regex) { FileTreeInfo resultInfo = new FileTreeInfo(); for (File item : startDir.listFiles()) { if (item.isDirectory()) { resultInfo.folderList.add(item); resultInfo.addAll(watchDirs(item, regex)); } else { if (item.getName().matches(regex)) { resultInfo.fileList.add(item); } } } return resultInfo; } /** * TreeInfo ***/ public static class FileTreeInfo implements Iterable<File> { public List<File> fileList = new ArrayList<File>(); public List<File> folderList = new ArrayList<File>(); public Iterator<File> iterator() { return fileList.iterator(); } void addAll(FileTreeInfo other) { fileList.addAll(other.fileList); folderList.addAll(other.folderList); } public String toString() { return "dirs:" + PPrint.pFormat(folderList) + "\n\nfiles:" + PPrint.pFormat(fileList); } } }