Skip to content

Latest commit

 

History

History
515 lines (370 loc) · 17.7 KB

输入与输出流概述.md

File metadata and controls

515 lines (370 loc) · 17.7 KB

.wiz-todo, .wiz-todo-img {width: 16px; height: 16px; cursor: default; padding: 0 10px 0 2px; vertical-align: -10%;-webkit-user-select: none;} .wiz-todo-label { display: inline-block; padding-top: 7px; padding-bottom: 6px; line-height: 1.5;} .wiz-todo-label-checked { /text-decoration: line-through;/ color: #666;} .wiz-todo-label-unchecked {text-decoration: initial;} .wiz-todo-completed-info {padding-left: 44px; display: inline-block; } .wiz-todo-avatar { width:20px; height: 20px; vertical-align: -20%; margin-right:10px; border-radius: 2px;} .wiz-todo-account, .wiz-todo-dt { color: #666; }[TOC]

# Introduction

Java.io包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。Java.io包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。Java为I/O提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。 Java是面向对象的编程语言,对象是对现实实体的抽象表述。所以Java API中流(Stream)是对一连串数据的抽象,同时定义了一些操作,write和read等。所以现实实体,只要包含数据和对数据的读写操作都可以表 示为流。OutputStream类和InputStream类,是2个抽象类,分别对应输出、输入流,所有其它流对象,都是其子类。   比如文件,文件本质是保存在存储设备中的一连串数据,在Java API中抽象为FileOutputStream类和FileInputStream类,文件的读写可以通过对相应流的读写实现的。

  比如控制台中命令和结果的输入输出,键盘的输入是一串数据,程序的输出是一串数据,所以在Java API中也被抽象为流对象。控制台输入由System.in对象体现,System.in是类型为InputStream的对象。控制台输出由 System.out对象体现,System.out是类型为PrintStream的对象。   由于文件和控制台输入输出都和操作系统有关,所以文件流和控制台流对象最终都是由Java虚拟机创建的。

  ByteArrayOutputStream、ByteArrayInputStream,是完全不依赖Java虚拟机的流对象,其完全是对一个 byte[]数组的抽象。因为byte[]数组也是一连串数据,byte[]数组支持读写功能,所以完全可以抽象为流对象,这可以从这两个类的源代码中看 出。  Unix的5种I/O模型:阻塞式I/O, 非阻塞式I/O,I/O复用模型,信号驱动式I/O和异步I/O。

阻塞式I/O

blocking_io

非阻塞式I/O

nonblocking_io

I/O复用(select,poll)

I/O多路复用通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况可以同时处理多个客户端请求。 目前支持I/O多路复用的系统调用有select,pselect,poll,epoll,在linux网络编程过程中,很长一段时间都是用select做轮询和网络事件通知,然而select的一些固有缺陷导致了它的应用受到了很大的限制,最终linux不得不载新的内核版本中寻找select的替代方案,最终选择了epoll。

multplexing_io

信号驱动式I/O

signal_driven

异步I/O

asynchronous_io

# Stream&Reader

# Console

单个字符读入


格式1:Scanner sc = new Scanner (new BufferedInputStream(System.in));


格式2:Scanner sc = new Scanner (System.in);


在读入数据量大的情况下,格式1的速度会快些。


读一个整数: int n = sc.nextInt(); 相当于 scanf("%d", &n); 或 cin >> n; 


读一个字符串:String s = sc.next(); 相当于 scanf("%s", s); 或 cin >> s; 


读一个浮点数:double t = sc.nextDouble(); 相当于 scanf("%lf", &t); 或 cin >> t; 


读一整行: String s = sc.nextLine(); 相当于 gets(s); 或 cin.getline(...); 


判断是否有下一个输入可以用sc.hasNext()或sc.hasNextInt()或sc.hasNextDouble()或sc.hasNextLine()
```## Input

Java的控制台输入由System.in完成。为了获得一个绑定到控制台的字符流,你可以把System.in包装在一个BufferedReader 对象中来创建一个字符流。

下面是创建BufferedReader的基本语法:

``` 
BufferedReader br = new BufferedReader(new 
                      InputStreamReader(System.in));

### 多字符输入

从BufferedReader对象读取一个字符要使用read()方法,它的语法如下:

int read( ) throws IOException

每次调用read()方法,它从输入流读取一个字符并把该字符作为整数值返回。 当流结束的时候返回-1。该方法抛出IOException。

下面的程序示范了用read()方法从控制台不断读取字符直到用户输入"q"。

// 使用 BufferedReader 在控制台读取字符

import java.io.*;

public class BRRead {
   public static void main(String args[]) throws IOException
   {
      char c;
      // 使用 System.in 创建 BufferedReader 
      BufferedReader br = new BufferedReader(new 
                         InputStreamReader(System.in));
      System.out.println("Enter characters, 'q' to quit.");
      // 读取字符
      do {
         c = (char) br.read();
         System.out.println(c);
      } while(c != 'q');
   }
}

以上实例编译运行结果如下:

Enter characters, 'q' to quit.
123abcq
1
2
3
a
b
c
q

### 字符串输入

从标准输入读取一个字符串需要使用BufferedReader的readLine()方法。

它的一般格式是:

String readLine( ) throws IOException

下面的程序读取和显示字符行直到你输入了单词"end"。

// 使用 BufferedReader 在控制台读取字符
import java.io.*;
public class BRReadLines {
   public static void main(String args[]) throws IOException
   {
      // 使用 System.in 创建 BufferedReader 
      BufferedReader br = new BufferedReader(new
                              InputStreamReader(System.in));
      String str;
      System.out.println("Enter lines of text.");
      System.out.println("Enter 'end' to quit.");
      do {
         str = br.readLine();
         System.out.println(str);
      } while(!str.equals("end"));
   }
}

以上实例编译运行结果如下:

Enter lines of text.
Enter 'end' to quit.
This is line one
This is line one
This is line two
This is line two
end
end

### 参数处理

- 调用函数

    Scanner scanner = new Scanner(System.in);
    System.out.println("Command me please!");
    String inputString = "";
    while (!inputString.equals("quit")) {
        inputString = scanner.nextLine();
        String[] commandTokens = inputString.split(" ");

        if (commandTokens.length == 0) {
            scanner.close();
            throw new InputMismatchException("please enter a command");
        } else {
            String command = commandTokens[0];
            if (command.equals("search")) {
                System.out.println("this is where we can call search with "
                        + makeArguments(commandTokens));
            }
        }
    }
    scanner.close();

  • 封装好的处理函数: ​``` java     /**      * a function that assembles all the arguments into a single string so that      * we can call a method like search      *       * @param commandTokens      *            - a string array that corresponds to what the user entered      * @return a string that can be passed on to functions like search, serve      *         etc      */     public static String makeArguments(String[] commandTokens) {         String completeStringArgument = "";         for (int i = 1; i < commandTokens.length - 1; i++) {             completeStringArgument += commandTokens[i] + " ";         }         return completeStringArgument + commandTokens[commandTokens.length - 1];     }
## Out

在此前已经介绍过,控制台的输出由 print( ) 和println( )完成。这些方法都由类PrintStream 定义,System.out是该类对象的一个引用。

PrintStream 继承了OutputStream类,并且实现了方法write()。这样,write()也可以用来往控制台写操作。

PrintStream 定义write()的最简单格式如下所示:

``` 
void write(int byteval)

该方法将byteval的低八位字节写到流中。

### 实例

下面的例子用write()把字符"A"和紧跟着的换行符输出到屏幕:

import java.io.*;

// 演示 System.out.write().
public class WriteDemo {
   public static void main(String args[]) {
      int b; 
      b = 'A';
      System.out.write(b);
      System.out.write('\n');
   }
}

运行以上实例在输出窗口输出"A"字符

A

**注意:**write()方法不经常使用,因为print()和println()方法用起来更为方便。

# FileIO

 java-files-io

## Directory

### Path

### Create

File类中有两个方法可以用来创建文件夹:

- **mkdir( )**方法创建一个文件夹,成功则返回true,失败则返回false。失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。 - **mkdirs()**方法创建一个文件夹和它的所有父文件夹。

下面的例子创建 "/tmp/user/java/bin"文件夹:

import java.io.File;

public class CreateDir {
   public static void main(String args[]) {
      String dirname = "/tmp/user/java/bin";
      File d = new File(dirname);
      // 现在创建目录
      d.mkdirs();
  }
}

编译并执行上面代码来创建目录"/tmp/user/java/bin"。

**注意:**Java在UNIX和Windows自动按约定分辨文件路径分隔符。如果你在Windows版本的Java中使用分隔符(/) ,路径依然能够被正确解析。

### Read

一个目录其实就是一个File对象,它包含其他文件和文件夹。

如果创建一个File对象并且它是一个目录,那么调用isDirectory( )方法会返回true。

可以通过调用该对象上的list()方法,来提取它包含的文件和文件夹的列表。

下面展示的例子说明如何使用list()方法来检查一个文件夹中包含的内容:

import java.io.File;

public class DirList {
   public static void main(String args[]) {
      String dirname = "/tmp";
      File f1 = new File(dirname);
      if (f1.isDirectory()) {
         System.out.println( "Directory of " + dirname);
         String s[] = f1.list();
         for (int i=0; i < s.length; i++) {
            File f = new File(dirname + "/" + s[i]);
            if (f.isDirectory()) {
               System.out.println(s[i] + " is a directory");
            } else {
               System.out.println(s[i] + " is a file");
            }
         }
      } else {
         System.out.println(dirname + " is not a directory");
    }
  }
}

以上实例编译运行结果如下:

Directory of /tmp
bin is a directory
lib is a directory
demo is a directory
test.txt is a file
README is a file
index.html is a file
include is a directory

## File

### File Path(文件寻址) (1)如果要找resource目录下的某个文件

BufferedReader bufferedReaderB = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/B/B1.txt")));

1. String url = this.getClass().getResource("/userFile.properties").getFile();   2.     userList.store(new FileOutputStream(url), "Users Info List");  

(2)如果要找某个类

或者:

String[] classpathEntries = classpath.split(File.pathSeparator);

### 按行读取

package test
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class ReadSelectedLine{
    // 读取文件指定行。
    static void readAppointedLineNumber(File sourceFile, int lineNumber)
            throws IOException {
        FileReader in = new FileReader(sourceFile);
        LineNumberReader reader = new LineNumberReader(in);
        String s = "";
        if (lineNumber <= 0 || lineNumber > getTotalLines(sourceFile)) {
            System.out.println("不在文件的行数范围(1至总行数)之内。");
            System.exit(0);
        }
        int lines = 0;
        while (s != null) {
            lines++;
            s = reader.readLine();
            if((lines - lineNumber) == 0) {
             System.out.println(s);
             System.exit(0);
            }
        }
        reader.close();
        in.close();
    }
    // 文件内容的总行数。
    static int getTotalLines(File file) throws IOException {
        FileReader in = new FileReader(file);
        LineNumberReader reader = new LineNumberReader(in);
        String s = reader.readLine();
        int lines = 0;
        while (s != null) {
            lines++;
            s = reader.readLine();
        }
        reader.close();
        in.close();
        return lines;
    }

    /**
     * 读取文件指定行。
     */
    public static void main(String[] args) throws IOException {
        // 指定读取的行号
        int lineNumber = 2;
        // 读取文件
        File sourceFile = new File("D:/java/test.txt");
        // 读取指定的行
        readAppointedLineNumber(sourceFile, lineNumber);
        // 获取文件的内容的总行数
        System.out.println(getTotalLines(sourceFile));
    }
}

## 字节流

## 字符流

## Performance Benchmark(效率分析)

 按行读取文件比较Scanner和RandomAccessFile读取的效率                    

| 读                                     | 写                                       | 耗用时间(秒) | | ------------------------------------- | --------------------------------------- | ------- | | RandomAccessFile                      | RandomAccessFile                        | 95.848  | | BufferedInputStream + DataInputStream | BufferedOutputStream + DataOutputStream | 2.935   |

# Okio

Okio是 java.io 和 java.nio的补充,使用它更容易访问、存储和处理数据。

Okio中有几个常用的概念:

- ByteString:不可变的字节序列,类似String类,使用它更容易处理二进制数据。 - Buffer:可变的字节序列,类似ArrayList。 - Source:类似InputStream(输入流) - Sink:类似OutputStream(输出流)

## Source

Source相当于输入流(InputStream)。把硬盘中的数据输入到内存中。

try {
      File file = new File("test.txt");

      BufferedSource source = Okio.buffer(Okio.source(file));

      //byte[] data = source.readByteArray();
      //System.out.println(new String(data, Charset.forName("UTF-8")));

      BufferedSink sink = Okio.buffer(Okio.sink(new File("test2.txt")));
      source.readAll(sink);
      sink.close();

      source.close();
 } catch (IOException e) {
      e.printStackTrace();
 }

## Sink

try {
       File file = new File("test.txt"); //如果文件不存在,则自动创建
       BufferedSink sink = Okio.buffer(Okio.sink(file));
       sink.writeUtf8("Hello, World");
       sink.writeString("测试信息", Charset.forName("UTF-8"));
       sink.close();
 } catch (IOException e) {
       e.printStackTrace();
}