如何使用行号提出最小的BufferedReader

时间:2017-10-11 12:52:09

标签: java

也许是一个新手问题,但我需要一个BufferedReader,它也是一个LineNumberReader但具有专业化,即能够限制一条线的长度。我无法使用JDK中的所述项目,因为它们包含私有字段和无关的功能,即“标记”和“重置”,如果没有额外的努力来简单地覆盖readline()等方法,就无法实现。

是否有一个缓冲读取器的最小实现,它只是读取和计算JDK或其他公共库中可用的行?我似乎找不到任何东西。如果没有,简单地删除所有与“标记”和“重置”有关的功能是否合理,这样我就可以更轻松地为我的目的制作一个简单的缓冲读卡器;或者更好的是,有一个我可以遵循的缓冲读卡器的例子,所以我不会省略任何关键步骤吗?

同样,我只想要一个围绕Reader的包装器,它使用readLine()方法中的一小部分附加功能读取和计算行。看起来很简单,我只是不想错过处理缓冲线时的任何关键边缘情况,JDK版本似乎通过增加的标记/重置功能来混淆这个功能,这样我就无法快速识别出必要的简单线缓冲的算法。如果有人有一些强大的例子或者知道一个库可以执行强大的行缓冲无标记/重置请分享。

“限制行长度”意味着通过过长的行长度避免拒绝服务攻击,导致套接字超时或导致OutOfMemoryError例外等。

4 个答案:

答案 0 :(得分:0)

您可以轻松扩展BufferedReader类:

import javafx.util.Pair;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;

public class LimitLineReader extends BufferedReader {

    private int lineno = 0;
    private int maxlinelength;
    private String remainder = "";

    public LimitLineReader(Reader in, int sz) {
        super(in, sz);
    }

    public LimitLineReader(Reader in) {
        super(in);
    }

    public void setMaxLineLength(int len){
        this.maxlinelength = len;
    }


    public Pair<Integer,String> readLineTruncated() throws IOException {
        if( remainder.equals(""))
            remainder = super.readLine();
        String line = remainder.substring(0,Math.min(remainder.length(), maxlinelength));
        remainder = remainder.substring(Math.min(remainder.length(), maxlinelength));
        return new Pair<>(lineno++, line);
    }
}

这导致以下行为(如此测试应该通过):

@Test
public void test() throws IOException{
    String teststring = "12345\n1234567890\n1\n\n";

    LimitLineReader reader = new LimitLineReader(new StringReader(teststring));
    reader.setMaxLineLength(5);

    Pair<Integer, String> p;

    p = reader.readLineTruncated();
    assertEquals(p.getKey(), new Integer(0));
    assertEquals(p.getValue(), "12345");

    p = reader.readLineTruncated();
    assertEquals(p.getKey(), new Integer(1));
    assertEquals(p.getValue(), "12345");

    p = reader.readLineTruncated();
    assertEquals(p.getKey(), new Integer(2));
    assertEquals(p.getValue(), "67890");

    p = reader.readLineTruncated();
    assertEquals(p.getKey(), new Integer(3));
    assertEquals(p.getValue(), "1");

    p = reader.readLineTruncated();
    assertEquals(p.getKey(), new Integer(4));
    assertEquals(p.getValue(), "");
}

答案 1 :(得分:0)

我会用你自己的课程装饰LineNumberReader

public class LengthLimitedLineNumberReader extends Reader {

     private LineNumberReader delegate;

     public LengthLimitedLineNumberReader(LineNumberReader delegate) {
          this.delegate = delegate;
     }

     @Override
     public int read() {
         return delegate.read();
     }

     // similar passthrough methods for Reader's abstract methods
     // and for `getLineNumber()` and any other methods of the 
     // delegate you need

     public String readLine() {
          // your own implementation of readLine()
          // using calls to delegate
     }
}

因为它不是BufferedReaderLineNumberReader的子类,所以这不是一个简单的替代品,但它接近了。

BufferedReader中的所有方法都不是final,因此可以extends Reader替换为extends BufferedReaderextends LineNumberReader,并且仍然将它作为装饰者实现;您只需要注意不要直接暴露BufferedReader直接与此实现冲突的任何方法。

答案 2 :(得分:0)

除非必要,否则最好避免使用子类化。在这种情况下,您不需要子类;只需使用普通的BufferedReader实现自己的方法:

String readLine(BufferedReader reader)
throws IOException {
    StringBuilder line = new StringBuilder();

    int c = reader.read();
    while (c >= 0 && c != '\r' && c != '\n'
            && line.length() < MAX_LINE_LENGTH) {

        line.append((char) c);
        c = reader.read();
    }

    // Consume CRLF sequence, if any.
    if (c == '\r') {
        reader.mark(1);
        if (reader.read() != '\n') {
            reader.reset();
        }
    }

    return line.toString();
}

<强>更新

幸运的是,LineNumberReader也可以支持,因为LineNumberReader允许手动更改其行号:

String readLine(LineNumberReader reader)
throws IOException {
    StringBuilder line = new StringBuilder();

    int c = reader.read();
    while (c >= 0 && c != '\r' && c != '\n') {

        line.append((char) c);

        if (line.length() >= MAX_LINE_LENGTH) {
            reader.setLineNumber(reader.getLineNumber() + 1);
            break;
        }

        c = reader.read();
    }

    // Consume CRLF sequence, if any.
    if (c == '\r') {
        reader.mark(1);
        if (reader.read() != '\n') {
            reader.reset();
        }
    }

    return line.toString();
}

答案 3 :(得分:0)

我的最终解决方案得到了@VGR和@slim的帮助:

package com.company.util;

import java.io.IOException;
import java.io.LineNumberReader;

public class LengthLimitedLineNumberReader {

  public static final int DEFAULT_MAX_LINE_LENGTH = 1024;
  private LineNumberReader delegate;
  private int readerMaxLineLen = DEFAULT_MAX_LINE_LENGTH;

  private int lineNumber = 0;

  public LengthLimitedLineNumberReader(LineNumberReader delegate) {
    this.delegate = delegate;
  }

  public LengthLimitedLineNumberReader(LineNumberReader delegate, int readerMaxLineLen) {
    this(delegate);
    this.readerMaxLineLen = readerMaxLineLen;
  }

  public int getLineNumber() {
    return lineNumber;
  }

  public String readLine() throws IOException {

    StringBuilder line = new StringBuilder();

    int c = delegate.read();
    if (c <= 0) {
      return null;
    }
    while (c >= 0 && c != '\n') {
      if (line.length() >= readerMaxLineLen) {
        throw new IOException("Exceeded max line length of " + readerMaxLineLen);
      }

      line.append((char) c);
      c = delegate.read();
    }

    lineNumber++;

    return line.toString();
  }

  public void close() throws IOException {
    delegate.close();
  }
}

注意:

  • 我使用了@slim建议的装饰模式,但我没有看到 需要实施读者合同。

我使用了@VGR的基本代码,但是:

  • 我不需要消费CRLF因为已经在 代表们的几个地方。
  • 我需要在本地跟踪lineNumber 因为委托LineNumberReaderread()方法中递增,因此在行没有\r\n的情况下无法递增它。
  • 当一行超过最大长度时,我需要抛出异常而不是继续处理。这是为了避免无限长的行会导致拒绝服务的情况。