zoukankan      html  css  js  c++  java
  • java io系列23之 BufferedReader(字符缓冲输入流)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_23.html

    更多内容请参考:java io系列01之 "目录"

    BufferedReader 介绍

    BufferedReader 是缓冲字符输入流。它继承于Reader。
    BufferedReader 的作用是为其他字符输入流添加一些缓冲功能。

    BufferedReader 函数列表

    复制代码
    BufferedReader(Reader in)
    BufferedReader(Reader in, int size)
    
    void     close()
    void     mark(int markLimit)
    boolean  markSupported()
    int      read()
    int      read(char[] buffer, int offset, int length)
    String   readLine()
    boolean  ready()
    void     reset()
    long     skip(long charCount)
    复制代码

    BufferedReader 源码分析(基于jdk1.7.40)

    复制代码
      1 package java.io;
      2 
      3 public class BufferedReader extends Reader {
      4 
      5     private Reader in;
      6 
      7     // 字符缓冲区
      8     private char cb[];
      9     // nChars 是cb缓冲区中字符的总的个数
     10     // nextChar 是下一个要读取的字符在cb缓冲区中的位置
     11     private int nChars, nextChar;
     12 
     13     // 表示“标记无效”。它与UNMARKED的区别是:
     14     // (01) UNMARKED 是压根就没有设置过标记。
     15     // (02) 而INVALIDATED是设置了标记,但是被标记位置太长,导致标记无效!
     16     private static final int INVALIDATED = -2;
     17     // 表示没有设置“标记”
     18     private static final int UNMARKED = -1;
     19     // “标记”
     20     private int markedChar = UNMARKED;
     21     // “标记”能标记位置的最大长度
     22     private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
     23 
     24     // skipLF(即skip Line Feed)是“是否忽略换行符”标记
     25     private boolean skipLF = false;
     26 
     27     // 设置“标记”时,保存的skipLF的值
     28     private boolean markedSkipLF = false;
     29 
     30     // 默认字符缓冲区大小
     31     private static int defaultCharBufferSize = 8192;
     32     // 默认每一行的字符个数
     33     private static int defaultExpectedLineLength = 80;
     34 
     35     // 创建“Reader”对应的BufferedReader对象,sz是BufferedReader的缓冲区大小
     36     public BufferedReader(Reader in, int sz) {
     37         super(in);
     38         if (sz <= 0)
     39             throw new IllegalArgumentException("Buffer size <= 0");
     40         this.in = in;
     41         cb = new char[sz];
     42         nextChar = nChars = 0;
     43     }
     44 
     45     // 创建“Reader”对应的BufferedReader对象,默认的BufferedReader缓冲区大小是8k
     46     public BufferedReader(Reader in) {
     47         this(in, defaultCharBufferSize);
     48     }
     49 
     50     // 确保“BufferedReader”是打开状态
     51     private void ensureOpen() throws IOException {
     52         if (in == null)
     53             throw new IOException("Stream closed");
     54     }
     55 
     56     // 填充缓冲区函数。有以下两种情况被调用:
     57     // (01) 缓冲区没有数据时,通过fill()可以向缓冲区填充数据。
     58     // (02) 缓冲区数据被读完,需更新时,通过fill()可以更新缓冲区的数据。
     59     private void fill() throws IOException {
     60         // dst表示“cb中填充数据的起始位置”。
     61         int dst;
     62         if (markedChar <= UNMARKED) {
     63             // 没有标记的情况,则设dst=0。
     64             dst = 0;
     65         } else {
     66             // delta表示“当前标记的长度”,它等于“下一个被读取字符的位置”减去“标记的位置”的差值;
     67             int delta = nextChar - markedChar;
     68             if (delta >= readAheadLimit) {
     69                 // 若“当前标记的长度”超过了“标记上限(readAheadLimit)”,
     70                 // 则丢弃标记!
     71                 markedChar = INVALIDATED;
     72                 readAheadLimit = 0;
     73                 dst = 0;
     74             } else {
     75                 if (readAheadLimit <= cb.length) {
     76                     // 若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,
     77                     // 并且“标记上限(readAheadLimit)”小于/等于“缓冲的长度”;
     78                     // 则先将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。
     79                     System.arraycopy(cb, markedChar, cb, 0, delta);
     80                     markedChar = 0;
     81                     dst = delta;
     82                 } else {
     83                     // 若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,
     84                     // 并且“标记上限(readAheadLimit)”大于“缓冲的长度”;
     85                     // 则重新设置缓冲区大小,并将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。
     86                     char ncb[] = new char[readAheadLimit];
     87                     System.arraycopy(cb, markedChar, ncb, 0, delta);
     88                     cb = ncb;
     89                     markedChar = 0;
     90                     dst = delta;
     91                 }
     92                 // 更新nextChar和nChars
     93                 nextChar = nChars = delta;
     94             }
     95         }
     96 
     97         int n;
     98         do {
     99             // 从“in”中读取数据,并存储到字符数组cb中;
    100             // 从cb的dst位置开始存储,读取的字符个数是cb.length - dst
    101             // n是实际读取的字符个数;若n==0(即一个也没读到),则继续读取!
    102             n = in.read(cb, dst, cb.length - dst);
    103         } while (n == 0);
    104 
    105         // 如果从“in”中读到了数据,则设置nChars(cb中字符的数目)=dst+n,
    106         // 并且nextChar(下一个被读取的字符的位置)=dst。
    107         if (n > 0) {
    108             nChars = dst + n;
    109             nextChar = dst;
    110         }
    111     }
    112 
    113     // 从BufferedReader中读取一个字符,该字符以int的方式返回
    114     public int read() throws IOException {
    115         synchronized (lock) {
    116             ensureOpen();
    117             for (;;) {
    118                 // 若“缓冲区的数据已经被读完”,
    119                 // 则先通过fill()更新缓冲区数据
    120                 if (nextChar >= nChars) {
    121                     fill();
    122                     if (nextChar >= nChars)
    123                         return -1;
    124                 }
    125                 // 若要“忽略换行符”,
    126                 // 则对下一个字符是否是换行符进行处理。
    127                 if (skipLF) {
    128                     skipLF = false;
    129                     if (cb[nextChar] == '
    ') {
    130                         nextChar++;
    131                         continue;
    132                     }
    133                 }
    134                 // 返回下一个字符
    135                 return cb[nextChar++];
    136             }
    137         }
    138     }
    139 
    140     // 将缓冲区中的数据写入到数组cbuf中。off是数组cbuf中的写入起始位置,len是写入长度
    141     private int read1(char[] cbuf, int off, int len) throws IOException {
    142         // 若“缓冲区的数据已经被读完”,则更新缓冲区数据。
    143         if (nextChar >= nChars) {
    144             if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
    145                 return in.read(cbuf, off, len);
    146             }
    147             fill();
    148         }
    149         // 若更新数据之后,没有任何变化;则退出。
    150         if (nextChar >= nChars) return -1;
    151         // 若要“忽略换行符”,则进行相应处理
    152         if (skipLF) {
    153             skipLF = false;
    154             if (cb[nextChar] == '
    ') {
    155                 nextChar++;
    156                 if (nextChar >= nChars)
    157                     fill();
    158                 if (nextChar >= nChars)
    159                     return -1;
    160             }
    161         }
    162         // 拷贝字符操作
    163         int n = Math.min(len, nChars - nextChar);
    164         System.arraycopy(cb, nextChar, cbuf, off, n);
    165         nextChar += n;
    166         return n;
    167     }
    168 
    169     // 对read1()的封装,添加了“同步处理”和“阻塞式读取”等功能
    170     public int read(char cbuf[], int off, int len) throws IOException {
    171         synchronized (lock) {
    172             ensureOpen();
    173             if ((off < 0) || (off > cbuf.length) || (len < 0) ||
    174                 ((off + len) > cbuf.length) || ((off + len) < 0)) {
    175                 throw new IndexOutOfBoundsException();
    176             } else if (len == 0) {
    177                 return 0;
    178             }
    179 
    180             int n = read1(cbuf, off, len);
    181             if (n <= 0) return n;
    182             while ((n < len) && in.ready()) {
    183                 int n1 = read1(cbuf, off + n, len - n);
    184                 if (n1 <= 0) break;
    185                 n += n1;
    186             }
    187             return n;
    188         }
    189     }
    190 
    191     // 读取一行数据。ignoreLF是“是否忽略换行符”
    192     String readLine(boolean ignoreLF) throws IOException {
    193         StringBuffer s = null;
    194         int startChar;
    195 
    196         synchronized (lock) {
    197             ensureOpen();
    198             boolean omitLF = ignoreLF || skipLF;
    199 
    200             bufferLoop:
    201             for (;;) {
    202 
    203                 if (nextChar >= nChars)
    204                     fill();
    205                 if (nextChar >= nChars) { /* EOF */
    206                     if (s != null && s.length() > 0)
    207                         return s.toString();
    208                     else
    209                         return null;
    210                 }
    211                 boolean eol = false;
    212                 char c = 0;
    213                 int i;
    214 
    215                 /* Skip a leftover '
    ', if necessary */
    216                 if (omitLF && (cb[nextChar] == '
    '))
    217                     nextChar++;
    218                 skipLF = false;
    219                 omitLF = false;
    220 
    221             charLoop:
    222                 for (i = nextChar; i < nChars; i++) {
    223                     c = cb[i];
    224                     if ((c == '
    ') || (c == '
    ')) {
    225                         eol = true;
    226                         break charLoop;
    227                     }
    228                 }
    229 
    230                 startChar = nextChar;
    231                 nextChar = i;
    232 
    233                 if (eol) {
    234                     String str;
    235                     if (s == null) {
    236                         str = new String(cb, startChar, i - startChar);
    237                     } else {
    238                         s.append(cb, startChar, i - startChar);
    239                         str = s.toString();
    240                     }
    241                     nextChar++;
    242                     if (c == '
    ') {
    243                         skipLF = true;
    244                     }
    245                     return str;
    246                 }
    247 
    248                 if (s == null)
    249                     s = new StringBuffer(defaultExpectedLineLength);
    250                 s.append(cb, startChar, i - startChar);
    251             }
    252         }
    253     }
    254 
    255     // 读取一行数据。不忽略换行符
    256     public String readLine() throws IOException {
    257         return readLine(false);
    258     }
    259 
    260     // 跳过n个字符
    261     public long skip(long n) throws IOException {
    262         if (n < 0L) {
    263             throw new IllegalArgumentException("skip value is negative");
    264         }
    265         synchronized (lock) {
    266             ensureOpen();
    267             long r = n;
    268             while (r > 0) {
    269                 if (nextChar >= nChars)
    270                     fill();
    271                 if (nextChar >= nChars) /* EOF */
    272                     break;
    273                 if (skipLF) {
    274                     skipLF = false;
    275                     if (cb[nextChar] == '
    ') {
    276                         nextChar++;
    277                     }
    278                 }
    279                 long d = nChars - nextChar;
    280                 if (r <= d) {
    281                     nextChar += r;
    282                     r = 0;
    283                     break;
    284                 }
    285                 else {
    286                     r -= d;
    287                     nextChar = nChars;
    288                 }
    289             }
    290             return n - r;
    291         }
    292     }
    293 
    294     // “下一个字符”是否可读
    295     public boolean ready() throws IOException {
    296         synchronized (lock) {
    297             ensureOpen();
    298 
    299             // 若忽略换行符为true;
    300             // 则判断下一个符号是否是换行符,若是的话,则忽略
    301             if (skipLF) {
    302                 if (nextChar >= nChars && in.ready()) {
    303                     fill();
    304                 }
    305                 if (nextChar < nChars) {
    306                     if (cb[nextChar] == '
    ')
    307                         nextChar++;
    308                     skipLF = false;
    309                 }
    310             }
    311             return (nextChar < nChars) || in.ready();
    312         }
    313     }
    314 
    315     // 始终返回true。因为BufferedReader支持mark(), reset()
    316     public boolean markSupported() {
    317         return true;
    318     }
    319 
    320     // 标记当前BufferedReader的下一个要读取位置。关于readAheadLimit的作用,参考后面的说明。
    321     public void mark(int readAheadLimit) throws IOException {
    322         if (readAheadLimit < 0) {
    323             throw new IllegalArgumentException("Read-ahead limit < 0");
    324         }
    325         synchronized (lock) {
    326             ensureOpen();
    327             // 设置readAheadLimit
    328             this.readAheadLimit = readAheadLimit;
    329             // 保存下一个要读取的位置
    330             markedChar = nextChar;
    331             // 保存“是否忽略换行符”标记
    332             markedSkipLF = skipLF;
    333         }
    334     }
    335 
    336     // 重置BufferedReader的下一个要读取位置,
    337     // 将其还原到mark()中所保存的位置。
    338     public void reset() throws IOException {
    339         synchronized (lock) {
    340             ensureOpen();
    341             if (markedChar < 0)
    342                 throw new IOException((markedChar == INVALIDATED)
    343                                       ? "Mark invalid"
    344                                       : "Stream not marked");
    345             nextChar = markedChar;
    346             skipLF = markedSkipLF;
    347         }
    348     }
    349 
    350     public void close() throws IOException {
    351         synchronized (lock) {
    352             if (in == null)
    353                 return;
    354             in.close();
    355             in = null;
    356             cb = null;
    357         }
    358     }
    359 }
    复制代码

    说明
    要 想读懂BufferReader的源码,就要先理解它的思想。BufferReader的作用是为其它Reader提供缓冲功能。创建 BufferReader时,我们会通过它的构造函数指定某个Reader为参数。BufferReader会将该Reader中的数据分批读取,每次读 取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据。
    为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
    那干嘛不干脆一次性将全部数据都读取到缓冲中呢?第一,读取全部的数据所需要的时间可能会很长。第二,内存价格很贵,容量不想硬盘那么大。


    下面,我就BufferReader中最重要的函数fill()进行说明。其它的函数很容易理解,我就不详细介绍了,大家可以参考源码中的注释进行理解。我们先看看fill()的源码:

    复制代码
     1 private void fill() throws IOException {
     2     int dst;
     3     if (markedChar <= UNMARKED) {
     4         /* No mark */
     5         dst = 0;
     6     } else {
     7         /* Marked */
     8         int delta = nextChar - markedChar;
     9         if (delta >= readAheadLimit) {
    10             /* Gone past read-ahead limit: Invalidate mark */
    11             markedChar = INVALIDATED;
    12             readAheadLimit = 0;
    13             dst = 0;
    14         } else {
    15             if (readAheadLimit <= cb.length) {
    16                 /* Shuffle in the current buffer */
    17                 System.arraycopy(cb, markedChar, cb, 0, delta);
    18                 markedChar = 0;
    19                 dst = delta;
    20             } else {
    21                 /* Reallocate buffer to accommodate read-ahead limit */
    22                 char ncb[] = new char[readAheadLimit];
    23                 System.arraycopy(cb, markedChar, ncb, 0, delta);
    24                 cb = ncb;
    25                 markedChar = 0;
    26                 dst = delta;
    27             }
    28             nextChar = nChars = delta;
    29         }
    30     }
    31 
    32     int n;
    33     do {
    34         n = in.read(cb, dst, cb.length - dst);
    35     } while (n == 0);
    36     if (n > 0) {
    37         nChars = dst + n;
    38         nextChar = dst;
    39     }
    40 }
    复制代码

    根据fill()中的if...else...,我将fill()分为4种情况进行说明。

    情况1:读取完缓冲区的数据,并且缓冲区没有被标记
    执行流程如下,
    (01) 其它函数调用 fill(),来更新缓冲区的数据
    (02) fill() 执行代码 if (markedChar <= UNMARKED) { ... }
    为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:

    复制代码
     1 private void fill() throws IOException {
     2     int dst;
     3     if (markedChar <= UNMARKED) {
     4         /* No mark */
     5         dst = 0;
     6     } 
     7 
     8     int n;
     9     do {
    10         n = in.read(cb, dst, cb.length - dst);
    11     } while (n == 0);
    12 
    13     if (n > 0) {
    14         nChars = dst + n;
    15         nextChar = dst;
    16     }
    17 }
    复制代码

    说明
    这 种情况发生的情况是 — — Reader中有很长的数据,我们每次从中读取一部分数据到缓冲中进行操作。每次当我们读取完缓冲中的数据之后,并且此时BufferedReader没 有被标记;那么,就接着从Reader(BufferReader提供缓冲功能的Reader)中读取下一部分的数据到缓冲中。
    其中,判断是否读完缓冲区中的数据,是通过“比较nextChar和nChars之间大小”来判断的。其中,nChars 是缓冲区中字符的总的个数,而 nextChar 是缓冲区中下一个要读取的字符的位置。
    判断BufferedReader有没有被标记,是通过“markedChar”来判断的。
    理解这个思想之后,我们再对这种情况下的fill()的代码进行分析,就特别容易理解了。
    (01) if (markedChar <= UNMARKED) 它的作用是判断“BufferedReader是否被标记”。若被标记,则dst=0。
    (02) in.read(cb, dst, cb.length - dst) 等价于 in.read(cb, 0, cb.length),意思是从Reader对象in中读取cb.length个数据,并存储到缓冲区cb中,而且从缓冲区cb的位置0开始存储。该函数 返回值等于n,也就是n表示实际读取的字符个数。若n=0(即没有读取到数据),则继续读取,直到读到数据为止。
    (03) nChars=dst+n 等价于 nChars=n;意味着,更新缓冲区数据cb之后,设置nChars(缓冲区的数据个数)为n。
    (04) nextChar=dst 等价于 nextChar=0;意味着,更新缓冲区数据cb之后,设置nextChar(缓冲区中下一个会被读取的字符的索引值)为0。

    情况2:读取完缓冲区的数据,缓冲区的标记位置>0,并且“当前标记的长度”超过“标记上限(readAheadLimit)”
    执行流程如下,
    (01) 其它函数调用 fill(),来更新缓冲区的数据
    (02) fill() 执行代码 if (delta >= readAheadLimit) { ... }
    为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:

    复制代码
     1 private void fill() throws IOException {
     2     int dst;
     3     if (markedChar > UNMARKED) {
     4         int delta = nextChar - markedChar;
     5         if (delta >= readAheadLimit) {
     6             markedChar = INVALIDATED;
     7             readAheadLimit = 0;
     8             dst = 0;
     9         } 
    10     }
    11 
    12     int n;
    13     do {
    14         n = in.read(cb, dst, cb.length - dst);
    15     } while (n == 0);
    16     if (n > 0) {
    17         nChars = dst + n;
    18         nextChar = dst;
    19     }
    20 }
    复制代码

    说明
    这 种情况发生的情况是 — — BufferedReader中有很长的数据,我们每次从中读取一部分数据到缓冲区中进行操作。当我们读取完缓冲区中的数据之后,并且此 时,BufferedReader存在标记时,同时,“当前标记的长度”大于“标记上限”;那么,就发生情况2。此时,我们会丢弃“标记”并更新缓冲区。
    (01) delta = nextChar - markedChar;其中,delta就是“当前标记的长度”,它是“下一个被读取字符的位置”减去“被标记的位置”的差值。
    (02) if (delta >= readAheadLimit);其中,当delta >= readAheadLimit,就意味着,“当前标记的长度”>=“标记上限”。为什么要有标记上限,即readAheadLimit的值到底有何 意义呢?
    我们标记一个位置之后,更新缓冲区的时候,被标记的位置会被保存;当我们不停的更新缓冲区的时候,被标记的位置会被不停的放大。然后内存的容量是有效的,我们不可能不限制长度的存储标记。所以,需要readAheadLimit来限制标记长度!
    (03) in.read(cb, dst, cb.length - dst) 等价于 in.read(cb, 0, cb.length),意思是从Reader对象in中读取cb.length个数据,并存储到缓冲区cb中,而且从缓冲区cb的位置0开始存储。该函数 返回值等于n,也就是n表示实际读取的字符个数。若n=0(即没有读取到数据),则继续读取,直到读到数据为止。
    (04) nChars=dst+n 等价于 nChars=n;意味着,更新缓冲区数据cb之后,设置nChars(缓冲区的数据个数)为n。
    (05) nextChar=dst 等价于 nextChar=0;意味着,更新缓冲区数据cb之后,设置nextChar(缓冲区中下一个会被读取的字符的索引值)为0。

    情况3:读取完缓冲区的数据,缓冲区的标记位置>0,“当前标记的长度”没超过“标记上限(readAheadLimit)”,并且“标记上限(readAheadLimit)”小于/等于“缓冲的长度”;
    执行流程如下,
    (01) 其它函数调用 fill(),来更新缓冲区的数据
    (02) fill() 执行代码 if (readAheadLimit <= cb.length) { ... }
    为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:

    复制代码
     1 private void fill() throws IOException {
     2     int dst;
     3     if (markedChar > UNMARKED) {
     4         int delta = nextChar - markedChar;
     5         if ((delta < readAheadLimit) &&  (readAheadLimit <= cb.length) ) {
     6             System.arraycopy(cb, markedChar, cb, 0, delta);
     7             markedChar = 0;
     8             dst = delta;
     9 
    10             nextChar = nChars = delta;
    11         }
    12     }
    13 
    14     int n;
    15     do {
    16         n = in.read(cb, dst, cb.length - dst);
    17     } while (n == 0);
    18     if (n > 0) {
    19         nChars = dst + n;
    20         nextChar = dst;
    21     }
    22 }
    复制代码

    说明
    这 种情况发生的情况是 — — BufferedReader中有很长的数据,我们每次从中读取一部分数据到缓冲区中进行操作。当我们读取完缓冲区中的数据之后,并且此 时,BufferedReader存在标记时,同时,“当前标记的长度”小于“标记上限”,并且“标记上限”小于/等于“缓冲区长度”;那么,就发生情况 3。此时,我们保留“被标记的位置”(即,保留被标记位置开始的数据),并更新缓冲区(将新增的数据,追加到保留的数据之后)。

    情况4:读取完缓冲区的数据,缓冲区的标记位置>0,“当前标记的长度”没超过“标记上限(readAheadLimit)”,并且“标记上限(readAheadLimit)”大于“缓冲的长度”;
    执行流程如下,
    (01) 其它函数调用 fill(),来更新缓冲区的数据
    (02) fill() 执行代码 else { char ncb[] = new char[readAheadLimit]; ... }
    为了方便分析,我们将这种情况下fill()执行的操作等价于以下代码:

    复制代码
     1 private void fill() throws IOException {
     2     int dst;
     3     if (markedChar > UNMARKED) {
     4         int delta = nextChar - markedChar;
     5         if ((delta < readAheadLimit) &&  (readAheadLimit > cb.length) ) {
     6             char ncb[] = new char[readAheadLimit];
     7             System.arraycopy(cb, markedChar, ncb, 0, delta);
     8             cb = ncb;
     9             markedChar = 0;
    10             dst = delta;
    11             
    12             nextChar = nChars = delta;
    13         }
    14     }
    15 
    16     int n;
    17     do {
    18         n = in.read(cb, dst, cb.length - dst);
    19     } while (n == 0);
    20     if (n > 0) {
    21         nChars = dst + n;
    22         nextChar = dst;
    23     }
    24 }
    复制代码

    说明
    这 种情况发生的情况是 — — BufferedReader中有很长的数据,我们每次从中读取一部分数据到缓冲区中进行操作。当我们读取完缓冲区中的数据之后,并且此 时,BufferedReader存在标记时,同时,“当前标记的长度”小于“标记上限”,并且“标记上限”大于“缓冲区长度”;那么,就发生情况4。此 时,我们要先更新缓冲区的大小,然后再保留“被标记的位置”(即,保留被标记位置开始的数据),并更新缓冲区数据(将新增的数据,追加到保留的数据之 后)。

     

    示例代码

    关于BufferedReader中API的详细用法,参考示例代码(BufferedReaderTest.java): 

    复制代码
     1 import java.io.BufferedReader;
     2 import java.io.ByteArrayInputStream;
     3 import java.io.File;
     4 import java.io.InputStream;
     5 import java.io.FileReader;
     6 import java.io.IOException;
     7 import java.io.FileNotFoundException;
     8 import java.lang.SecurityException;
     9 
    10 /**
    11  * BufferedReader 测试程序
    12  *
    13  * @author skywang
    14  */
    15 public class BufferedReaderTest {
    16 
    17     private static final int LEN = 5;
    18 
    19     public static void main(String[] args) {
    20         testBufferedReader() ;
    21     }
    22 
    23     /**
    24      * BufferedReader的API测试函数
    25      */
    26     private static void testBufferedReader() {
    27 
    28         // 创建BufferedReader字符流,内容是ArrayLetters数组
    29         try {
    30             File file = new File("bufferedreader.txt");
    31             BufferedReader in =
    32                   new BufferedReader(
    33                       new FileReader(file));
    34 
    35             // 从字符流中读取5个字符。“abcde”
    36             for (int i=0; i<LEN; i++) {
    37                 // 若能继续读取下一个字符,则读取下一个字符
    38                 if (in.ready()) {
    39                     // 读取“字符流的下一个字符”
    40                     int tmp = in.read();
    41                     System.out.printf("%d : %c
    ", i, tmp);
    42                 }
    43             }
    44 
    45             // 若“该字符流”不支持标记功能,则直接退出
    46             if (!in.markSupported()) {
    47                 System.out.println("make not supported!");
    48                 return ;
    49             }
    50               
    51             // 标记“当前索引位置”,即标记第6个位置的元素--“f”
    52             // 1024对应marklimit
    53             in.mark(1024);
    54 
    55             // 跳过22个字符。
    56             in.skip(22);
    57 
    58             // 读取5个字符
    59             char[] buf = new char[LEN];
    60             in.read(buf, 0, LEN);
    61             System.out.printf("buf=%s
    ", String.valueOf(buf));
    62             // 读取该行剩余的数据
    63             System.out.printf("readLine=%s
    ", in.readLine());
    64 
    65             // 重置“输入流的索引”为mark()所标记的位置,即重置到“f”处。
    66             in.reset();
    67             // 从“重置后的字符流”中读取5个字符到buf中。即读取“fghij”
    68             in.read(buf, 0, LEN);
    69             System.out.printf("buf=%s
    ", String.valueOf(buf));
    70 
    71             in.close();
    72        } catch (FileNotFoundException e) {
    73            e.printStackTrace();
    74        } catch (SecurityException e) {
    75            e.printStackTrace();
    76        } catch (IOException e) {
    77            e.printStackTrace();
    78        }
    79     }
    80 }
    复制代码

    程序中读取的bufferedreader.txt的内容如下:

    abcdefghijklmnopqrstuvwxyz
    0123456789
    ABCDEFGHIJKLMNOPQRSTUVWXYZ

    运行结果
    0 : a
    1 : b
    2 : c
    3 : d
    4 : e
    buf=01234
    readLine=56789
    buf=fghij

  • 相关阅读:
    django 静态文件模版上传下载配置
    drf ModelViewSet之匹配路由参数
    Django drf序列化外键关联表ID以为字段
    Django 自关联递归序列化模块 django-rest-frame-recursive模块
    Python利用Psycopg2模块将Excel表格数据导入Postgressql
    PyTorch中view的用法
    P1113 杂务 【拓扑排序】
    P3916 图的遍历 【反向建图+DFS】
    P2814 家谱【map型的并查集】
    P1102 A-B 数对【map】
  • 原文地址:https://www.cnblogs.com/shangxiaofei/p/3844062.html
Copyright © 2011-2022 走看看