如何仅使用FileReader读取多行?

时间:2014-03-05 13:30:29

标签: java filereader

我有以下代码:

public class Reader {
    public static void main(String[] args) throws IOException {
        try (FileReader in = new FileReader("D:/test.txt")) {
            // BufferedReader br = new BufferedReader(in);
            int line = in .read();
            for (int i = 0; i < line; i++) {
                //System.out.println(line);

                System.out.println((char) line);
                line = in .read();
            }
        }
    }
}

以及包含内容的文件Test.txt

Hello
Java

当我在代码上面运行时,它只会显示Hello。我想仅使用FileReader读取多行。我不想使用BufferedReaderInputStreamReader等。这可能吗?

6 个答案:

答案 0 :(得分:3)

我认为此版本的代码不会打印“Hello”。

您正在致电:

int line = in.read();

这是做什么的?在Javadocs中查找Reader

  

public int read()

     

抛出IOException

     

读取单个字符。此方法将阻塞,直到字符可用,发生I / O错误或结束   到达了溪流。

(强调我的)

您的代码从'Hello'读取'H',其中ASCII为72。

然后它进入你的循环,行== 72,所以它进入循环:

  for(int i=0;i<line;i++)

......做出决定“是否小于72?是的,所以我将进入循环区块”。

然后每次读取一个字符时,line的值都会变为另一个整数,并且每个时间循环都会以i为增量。所以循环说“只要字符的ASCII值大于我计算的迭代次数,就继续”。

......每次它都会出现,它会将该字符打印在一条线上。

实际上,对于您的输入,它读取文件结束(-1),而作为-1 < i,则不符合循环继续条件。

但是对于较长的输入,它会在第97个字符后的第一个'a'或第98个字符后的第一个'b'停止,依此类推(因为ASCII'a'是97,等等)

H
e
l
l
o


J
a
v
a

这不是你想要的:

  • 你不希望你的循环重复,直到i&gt; =“我刚读过的角色”。您希望它重复,直到in.read()返回-1。你可能已经被教会如何循环直到满足条件。
  • 您不希望println()每个角色,因为这会添加您不想要的换行符。使用print()

您还应该查看Reader.read(byte[] buffer)方法,看看是否可以编写代码以更大的块工作。


您在编程生涯中一遍又一遍地使用的两种模式是:

  Type x = getSomehow();
  while(someCondition(x)) {
      doSomethingWith(x);
      x = getSomehow();
  }

......和......

  Type x = value_of_x_which_meets_condition;
  while(someCondition(x)) {
      x = getSomehow();
      doSomethingWith(x);
  }

看看你是否可以使用FileReader构建一些东西以及从中获得的值,填写“somehows”。

答案 1 :(得分:1)

您必须通过char读取内容char并解析新的行序列。

新的行序列可以是以下任何一种:

  1. 单一车位返回'\r'
  2. 单一换行'\n'
  3. 回车后跟换行"\r\n"
  4. 修改

    您可以尝试以下方法:

    public List<String> readLinesUsingFileReader(String filename) throws IOException {
        List<String> lines = null;
        try (FileReader fileReader = new FileReader(filename)) {
            lines = readLines(fileReader);
        }
        return lines;
    }
    
    private List<String> readLines(FileReader fileReader) throws IOException {
        List<String> lines = new ArrayList<>();
        boolean newLine = false;
        int c, p = 0;
        StringBuilder line = new StringBuilder();
        while(-1 != (c = fileReader.read())) {
            if(c == '\n' && p != '\r') {
                newLine = true;
            } else if(c == '\r') {
                newLine = true;
            } else {
                if(c != '\n' && c != '\r') {
                    line.append((char) c);  
                }
            }
            if(newLine) {
                lines.add(line.toString());
                line = new StringBuilder();
                newLine = false;
            }
            p = c;
        }
        if(line.length() > 0) {
            lines.add(line.toString());
        }
        return lines;
    }
    

    请注意,上面的代码将整个文件读入List,这可能不适合大文件!在这种情况下,您可能希望实现一种使用流式传输的方法,即一次读取一行,例如String readNextLine(FileReader fileReader) { ... }

    一些基本测试:

    创建要读取的测试文件

    private final static String txt0 = "testnl0.txt";
    private final static String txt1 = "testnl1.txt";
    private final static String txt2 = "testnl2.txt";
    
    @BeforeClass
    public static void genTestFile() throws IOException {
        try (OutputStream os = new FileOutputStream(txt0)) {
            os0.write((
                "Hello\n" +
                ",\r\n" +
                "World!" +
                "").getBytes());
        }
    
        try (OutputStream os = new FileOutputStream(txt1)) {
            os.write((
                "\n" +
                "\r\r" +
                "\r\n" +
                "").getBytes());
        }
    
        try (OutputStream os = new FileOutputStream(txt2)) {
            os.write(( 
                "").getBytes());
        }
    }
    

    使用创建的文件进行测试

    @Test
    public void readLinesUsingFileReader0() throws IOException {
        List<String> lines = readLinesUsingFileReader(txt0);
        Assert.assertEquals(3, lines.size());
        Assert.assertEquals("Hello", lines.get(0));
        Assert.assertEquals(",", lines.get(1));
        Assert.assertEquals("World!", lines.get(2));
    }
    
    @Test
    public void readLinesUsingFileReader1() throws IOException {
        List<String> lines = readLinesUsingFileReader(txt1);
        Assert.assertEquals(4, lines.size());
        Assert.assertEquals("", lines.get(0));
        Assert.assertEquals("", lines.get(1));
        Assert.assertEquals("", lines.get(2));
        Assert.assertEquals("", lines.get(3));
    }
    
    @Test
    public void readLinesUsingFileReader2() throws IOException {
        List<String> lines = readLinesUsingFileReader(txt2);
        Assert.assertTrue(lines.isEmpty());
    }
    

答案 2 :(得分:1)

逐个字符地读取文件而没有任何缓冲流是非常无效的。我可能会将FileReader包装在一些BufferedReader中,或者只是使用Scanner来读取文件的含义,但是如果你绝对想要/需要/只需要使用FileReader那么你可以试试

int line = in.read();
while (line != -1) {
    System.out.print((char) line);
    line = in.read();
}

而不是你的for (int i = 0; i < line; i++) {...}循环。

仔细阅读slims answer。简而言之:读取条件不应该关心您读取的字符数是否小于当前读取字符(i < line)的数字表示。就像

的情况一样
My name

is

not important now

此文件包含的字符很少,您通常看不到\r\n,实际上它看起来像

My name\r\n 
\r\n 
is\r\n 
\r\n 
not important now

其中\r的数字表示为10,因此在您阅读My name\r\n之后(由于\r为{9}个字符,而\n是单个字符表示行分隔符)您的i将变为10,因为您将尝试阅读的下一个字符\r也由10表示,您的条件i<line将失败( 10<10不是真的。)

因此,不应检查i<line,而应检查读取值是否为read method documentation中指定的-1表示的EoF(文件结束或流结束)所以你的情况应该像line != -1。而且因为您不需要i,只需在此使用while循环。

  

<强>返回:

     

读取的字符,如果已到达流的末尾,则返回-1

答案 3 :(得分:0)

如果您有新的换行符

    public static void main(String[]args) throws IOException{
        FileReader in = new FileReader("D:/test.txt");
        char [] a = new char[50];
        in.read(a); // reads the content to the array
        for(char c : a)
            System.out.print(c); //prints the characters one by one
        in.close();
    }

会打印

Hello 
Java

答案 4 :(得分:0)

Reader.read()返回单个char的int代码或者如果到达文件末尾则返回-1:

http://docs.oracle.com/javase/7/docs/api/java/io/Reader.html#read()

因此,通过char读取文件char并检查LF(换行,'\ n',0x0A,十进制10),CR(回车,'\ r',0x0D,十进制13)和结束线代码。

注意:Windows操作系统使用2个字符对行尾进行编码:“\ r \ n”。其他大多数包括Linux,MacOS等只使用“\ n”来编码行尾。

    final StringBuilder line = new StringBuilder(); // line buffer

    try (FileReader in = new FileReader("D:/test.txt")) {            
        int chAr, prevChar = 0x0A; // chAr - just read char, prevChar - previously read char
        while (prevChar != -1) { // until the last read char is EOF
            chAr = in.read(); // read int code of the next char
            switch (chAr) {
                case 0x0D: // CR - just
                    break; // skip
                case -1:   // EOF
                    if (prevChar == 0x0A) {
                        break; // no need a new line if EOF goes right after LF
                               // or no any chars were read before (prevChar isn't 
                               // changed from its initial 0x0A)
                    }
                case 0x0A: // or LF
                    System.out.println("line:" + line.toString()); // get string from the line buffer
                    line.setLength(0); // cleanup the line buffer
                    break;
                default: // if any other char code is read
                    line.append((char) chAr); // append to the line buffer
            }
            prevChar = chAr; // remember the current char as previous one for the next iteration
        }
    }

答案 5 :(得分:0)

我使用此代码解决了上述问题

     public class Reader 
      {
    public static void main(String[]args) throws IOException{
         try (FileReader in = new FileReader("D:/test.txt")) {
         int line = in.read();

         while(line!=-1)
         {
           System.out.print((char)line);
              line = in.read();
          }   }
     }
    }

但是,如果我为循环而不是像这样写

,还有一个问题
for(int i=0;i<line;i++)

它只打印第一行。有人可以告诉我为什么吗?