繁体   English   中英

如何仅使用FileReader读取多行?

[英]How to read multiple lines using FileReader only?

我有以下代码:

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等。这可能吗?

我认为此版本的代码不会显示“ Hello”。

您正在致电:

int line = in.read();

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

公共诠释read()

引发IOException

读取一个字符 该方法将一直阻塞,直到有字符可用,发生I / O错误或到达流的末尾为止。

(强调我的)

您的代码从“ Hello”中读取“ H”,ASCII中为72。

然后以line == 72进入循环,因此进入循环:

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

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

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

...并且每次旋转时,它都会在一行上打印该字符。

碰巧的是,对于您的输入,它将读取文件结尾(-1),并且当-1 < i ,不满足循环继续条件。

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

H
e
l
l
o


J
a
v
a

这不是您想要的:

  • 您不希望您的循环重复进行,直到i> =“我刚刚读过的字符”为止。 您希望它重复执行,直到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构造某些东西,并从中获取值,并填写“某种方式”。

您将必须逐字符读取内容char并解析新的行序列。

新行顺序可以是以下任意一种:

  1. 单个回车符'\\r'
  2. 单个换行符'\\n'
  3. 回车符后跟换行符"\\r\\n"

编辑

您可以尝试以下方法:

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());
}

逐字符读取文件而没有任何缓冲流是极其无效的。 我可能会将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++) {...}循环。

仔细阅读苗条答案 简而言之:如果阅读的字符数少于当前阅读的字符的数字表示形式( 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 (这是9个字符,因为\\r\\n是表示行分隔符的单个字符)后,您的i将变为10并且由于下一个字符,读取的是\\r ,它也由10表示,您的条件i<line将失败( 10<10不正确)。

因此,而不是检查i<line您应该检查读取的值是否不是EoF(文件末尾或流末尾的out),该值由read方法文档中指定的-1表示,因此您的条件应类似于line != -1 而且因为您不需要, iwhile这里使用while循环。

返回:

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

如果您有换行符

    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

Reader.read()返回单字符的int代码,如果到达文件末尾,则返回-1:

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

因此,按字符读取文件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
        }
    }

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

     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循环而不是像这样

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM