输入
BufferedInputStream
我们使用FileInputStream, BufferedInputStream来读取文件
1 | // 读取二进制文件try (BufferedInputStream bf = new BufferedInputStream( |
BufferedInputStream是一个带有缓冲区域的InputStream, 支持mark()标记和reset()重置方法.输入到byte[]数组里.只将数据读取进byte字节数组里, 因此这种方式只能读取二进制字节流
FileInputStream 一个字节一个字节的从文件里读取数据
BufferedReader
BufferedReader 从字符输入流中读取文本,缓冲各个字符.提供字符、数组和行的高效读取. 我们有俩种方式创建BufferedReader.
- 使用带缓冲区的写入器 Files.newBufferedReader(Paths.get(“new.txt”), StandardCharsets.UTF_8);;
- 读取UTF-8格式编码的文件 new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读写字节并将其解码为字符.将“字节输入流”转换成“字符输入流”.它继承于Reader.
1 | BufferedReader reader = Files.newBufferedReader(Paths.get("new.txt"), StandardCharsets.UTF_8); |
我们可以使用JAVA8中的Stream来快捷的遍历每一行
从标准IO中输入. 按照标准的IO模型,Java提供了System.out, System.out, System.err System.out,System.err 已经被包装成了PrintStream对象,但是System.in作为原生InputStream却没有进行过任何包装. 所以在使用System.in时必须对其进行包装,下例中展示了,我们使用InputStreamReader将System.in包装Reader,然后再包装一层BufferedReader
另外还有一点需要提到的是FileReader, 它一个字符一个字符地读取.
LineNumberInputStream
此类是一个输入流过滤器,它提供跟踪当前行号的附加功能.行是以回车符 (\r)、换行符 (\n)或回车符后面紧跟换行符结尾的字节序列.在所有这三种情况下,都以单个换行符形式返回行终止字符.行号以 0 开头,并在 read 返回换行符时递增 1.
LineNumberReader
跟踪行号的缓冲字符输入流.此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号.默认情况下,行编号从 0 开始.该行号随数据读取在每个行结束符处递增,并且可以通过调用 setLineNumber(int) 更改行号.但要注意的是,setLineNumber(int) 不会实际更改流中的当前位置;它只更改将由getLineNumber()返回的值.可认为行在遇到以下符号之一时结束:换行符(\n)、回车符(\r)、回车后紧跟换行符.
1 | // 获取行数int lineCount = 0; |
RandomAccessFile
读写随机访问文件 RandomAccessFile除了实现了DataInput和DataOutput接口之外,有效地与IO继承层次结构的其他部分实现了分离.
因为它不支持装饰模式,所以不能将其与InputStream和OutputStream子类的任何部分组合起来而且必须假定RandomAccessFile已经被正确的缓冲
用来访问那些保存数据记录的文件的,你就可以用seek()方法来访问记录,并进行读写了.这些记录的大小不必相同;但是其大小和位置必须是可知的.但是该类仅限于操作文件.
1 | // 读取所有的行try (RandomAccessFile r = new RandomAccessFile(file, "rw")) { |
getResourceAsStream
我们还可以使用类加载器的getResourceAsStream()从指定路径或者jar包中加载文件资源
- Class.getResourceAsStream(String path) : path 不以’/‘开头时默认是从此类所在的包下取资源,以’/‘开头则是从ClassPath根下获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。
- Class.getClassLoader.getResourceAsStream(String path) :默认则是从ClassPath根下获取,path不能以’/‘开头,最终是由ClassLoader获取资源。
1 | public class TestReadFile { |
读取压缩包文件
1 | public static Map<String, byte[]> getLoadedClass(String jarPath) { |
在写这个东西的时候遇到个小坑, 刚开始使用的是input.available()读取的文件内容, 但是大概要读取100多个文件, 总有6个文件读取的不完整,查看JDK的文档,上面说
Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream. The next invocation might be the same thread or another thread. A single read or skip of this many bytes will not block, but may read or skip fewer bytes.
Note that while some implementations of InputStream will return the total number of bytes in the stream, many will not. It is never correct to use the return value of this method to allocate a buffer intended to hold all data in this stream.
这个方法返回的是在非阻塞情况下对该流进行下一次某个方法(读或者skip操作)调用时的预估字节数.下一个方法调用可以在同一个线程或者在不同的线程中。下一次读取或者skip掉available预估字节数不会产生阻塞的情况,但是实际读取出来的字节数可能小于预估数。
因此在ZipFileInputStream中,它依赖于native方法实现,我们也不知道它返回的究竟是流里剩下的全部的字节数还是部分的。最后文档也说了,永远不要使用这个方法返回的预估字节数分配buffer大小的buffer.
输出
BufferedOutputStream
我们使用FileOutputStream, BufferedOutputStream来读取文件
1 | try (BufferedOutputStream bf = new BufferedOutputStream(new FileOutputStream(new File("")));) { |
BufferedOutputStream 缓冲输出流。它继承于FilterOutputStream。作用是为另一个输出流提供“缓冲功能”。输出byte[]字节数组 BufferedOutputStream只提供了输出byte数据的方式,因此这种方式只能读取二进制流
FileOutputStream 一个字节一个字节的向文件里输出数据
BufferedWriter
- 支持字符串输出
- 支持换行输出
- 支持文件追加输出
1 | BufferedWriter writer = Files.newBufferedWriter(Paths.get("new.txt"), StandardCharsets.UTF_8); |
另外还有一点需要提到的是FileWriter, 它一个字符一个字符地输出
OutputStreamWriter
OutputStreamWriter 将字节流转换为字符流。是字节流通向字符流的桥梁。如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:GBK。
1 | // 写入UTF-8格式编码的文件StringBuffer buffer = new StringBuffer(); |
PrintStream
标准IO重定向
打印输出流,用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。PrintStream永远不会抛出IOException;PrintStream提供了自动flush和字符集设置功能。所谓自动flush,就是往PrintStream写入的数据会立刻调用flush()函数。
System类提供了一些简单的静态方法调用,以允许我们对标准输入,输出和错误IO进行重定向IO重定向是对字节流的操纵而不是字符流,因此在该例中使用的是InputStream和OutputStream而不是Reader和Writer
示例 如果在显示器上创建大量输出,而这些输出滚动地太快而无法阅读时,IO重定向就显得很有用
1 | PrintStream console = System.out; |
PrintWriter
用于向文本输出流打印对象的格式化表示形式。它实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。
FileWriter可以向文件输出数据. 首先创建一个与指定文件连接的FileWriter.然后使用BufferedWriter对其进行包装进行性能提升 最后使用PrintWriter提供格式化功能
1 | try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));) { |
System.out 是一个PrintStream,而PrintStream是一个OutputStream而PrintWriter有一个参数是接受OutputStream,因此我们可以将System.out转换成PrintWriter
1 | try (PrintWriter out = new PrintWriter(System.out);) { |
文件系统 API
FileFilter
检测文件是否存在.FileFilter 和他的前身FilenameFilter 唯一的不同是FileFilter 提供文件对象的访问方法,而FilenameFilter 是按照目录和文件名的方式来工作的.
1 | FileFilter fileFilter = pathname -> { |
FilenameFilter
1 | FilenameFilter filenameFilter = (dir, name) -> { |
FileDescriptor
用来表示开放文件、开放套接字等.当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件.但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作.
类实例作为一个不透明的句柄底层机器特有的结构表示一个打开的文件,打开的套接字或其他来源或字节的接收器.以下是关于FileDescriptor要点:
- 主要实际使用的文件描述符是创建一个FileInputStream或FileOutputStream来遏制它.
- 应用程序不应创建自己的文件描述符.
DirectoryStream
遍历某个文件夹内的所有文件,但是不会遍历子目录. 也就是这会遍历当前路径中的所有文件
1 | DirectoryStream<Path> paths = Files.newDirectoryStream(Paths.get("E:")); |
FileVisitor
遍历某个文件夹内的所有文件接口.
SimpleFileVisitor实现了这个接口. 与DirectoryStream 不同的是,这个类会遍历目录下包括子目录的所有文件并且提供了多种处理接口方法.
1 | SimpleFileVisitor visitor = new SimpleFileVisitor<Path>() { |
WatchService
监控文件变化
1 | WatchService service = FileSystems.getDefault().newWatchService(); |
FileStore
代表了真正的存储设备,提供了设备的详尽信息
FileSystems
- FileSystems.getDefault() :返回 JVM 默认的 FileSystem – 一般说来,也就是操作系统的默认文件系统
- FileSystems.getFileSystem(uri) : 可以获取远程主机的FileSystem
1 | FileSystem system = FileSystems.getDefault(); |
Paths
Path 类可以在任何文件系统(FileSystem)和任何存储空间 Path 类引用默认文件系统(计算机的文件系统)的文件,但是 NIO.2是完全模块化的—— FileSystem 的具体实现是在内存中的一组数据,因此在网络环境或在虚拟文件系统中,NIO.2 也完全适用.NIO.2提供给我们在文件系统中操作文件、文件夹或链接的所有方法
File
File对象给我们提供了以下的功能
- 删除文件
- 文件重命名
- 创建新的文件
- 创建新的文件
- 获取文件的最后修改时间
- 设置文件只读
- 设置文件可写
- 获取文件长度(总字节数)
- 获取文件路径
- 获取绝对文件路径
- 文件是否隐藏
- 获得剩余磁盘空间?
- 拷贝文件夹
- 遍历文件夹
- 检查文件夹是否为空?
FileLock
锁定文件
ByteBuffer.allocate()语句改为ByteBuffer.allocateDirect().用来证实性能之间的差异,但是请注意程序的启动时间是否发生了明显的改变.
修改{@link JGrep}让其使java的nio内存映射文件.
JDK1.4引入了文件加锁机制,它允许我们同步访问某个作为共享资源的文件.不过,竞争同一文件的两个线程可能在不同的Java虚拟机上;或者一个是Java线程,另一个是操作系统中其他的某个本地线程.
文件锁对其他的操作系统进程是可见的,因为Java的文件加锁直接映射到了本地操作系统的加锁工具.通过对FileChannel调用tryLock()或lock(),就可以获得整个文件的FileLock.
(SocketChannel、DatagramChannel和 ServerSocketChannel不需要加锁,因为他们是从单进程实体继承而来;我们通常不在两个进程之间共享网络socket.)
tryLock()是非阻塞式的,它设法获取锁,但是如果不能获得(当其他一些进程已经持有相同的锁,并且不共享时),它将直接从方法调用返回.lock()则是阻塞式的,它要阻塞进程直至锁可以获得,或调用lock()的线程中断,或调用lock()的通道关闭.
使用FileLock.release()可以释放锁.
也可以使用此方法对文件上锁tryLock()或者lock()其中,加锁的区域由size-position决定.第三个参数指定是否是共享锁.
尽管无参数的加锁方法将根据文件尺寸的变化而变化,但是具有固定尺寸的锁不随文件尺寸的变化而变化.如果你获得了某一区域(从position到position+size)上的锁,当文件增大超出position+size时,那么在position+size之外的部分不会被锁定.无参数的加锁方法会对 整个文件进行加锁,甚至文件变大后也是如此.
对独占锁或者共享锁的支持必须由底层的操作系统提供.如果操作系统不支持共享锁并为每一个请求都创建一个锁,那么它就会使用独占锁.
锁的 类型(共享或独占)可以通过FileLock.isShared()进行查询.
1 | FileOutputStream fos = new FileOutputStream("file.txt"); |
内存IO API
ByteArrayInputStream
从byte[]数组中读取数据到缓存中.可以将字节数组转化为输入流此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
1 | byte[] buff = {1, 2, 3, 4, 5}; |
ByteArrayOutputStream
输出数据到byte[]数组里,可以捕获内存缓冲区的数据,转换成字节数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray()和 toString()获取数据。 关闭 ByteArrayOutputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
1 | byte[] buff = {1, 2, 3, 4, 5}; |
CharArrayReader
与ByteArrayInputStream对应。 支持mark和reset读取char[] 数组
1 | char[] array = {'a', 'z', 'g'}; |
CharArrayWriter
向内部char[] 缓冲区存储数据. 支持rest, 文件追加写操作, 支持string write
1 | try(CharArrayWriter out = new CharArrayWriter()) { |
PushbackInputStream
拥有一个PushBack缓冲区,从PushbackInputStream读出数据后,只要PushBack缓冲区没有满,就可以使用unread()将数据推回流的前端。
PushbackReader
允许将字符推回到流的字符流 reader。当程序调用推回输入流的unread()方法时,系统会把指定数组的内容推回到该缓冲区中,而推回输入流每次调用read()方法时,总是先从推回缓冲区读取内容,只有完全读取了推回缓冲区里的内容后,但是还没有装满read()所需要的数组时才会从原输入流中读取
1 | try ( |
PipedReader
PipedWriter 是字符管道输出流,可以通过管道进行线程间的通讯。
PipedWriter
PipedReader 是字符管道输入流,可以通过管道进行线程间的通讯。
PipedInputStream
管道输入流是让多线程可以通过管道进行线程间的通讯
PipedOutputStream
管道输出流是让多线程可以通过管道进行线程间的通讯
SequenceInputStream
从多个输入流中向程序读入数据。此时,可以使用合并流,将多个输入流合并成一个SequenceInputStream流对象。SequenceInputStream会将与之相连接的流集组合成一个输入流并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末 尾为止。 合并流的作用是将多个源合并合一个源。
StreamTokenizer
获取输入流并将其解析为“标记”,允许一次读取一个标记。解析过程由一个表和许多可以设置为各种状态的标志控制。该流的标记生成器可以识别标识符、数字、引用的字符串和各种注释样式等。
Console
专用来访问基于字符的控制台设备。如果你的Java程序要与Windows下的cmd或者Linux下的Terminal交互,就可以用这个Java Console类java.io.Console 只能用在标准输入、输出流未被重定向的原始控制台中使用,在 Eclipse 或者其他 IDE 的控制台是用不了的。
1 | Console cons = System.console(); |