简体   繁体   English

如何仅使用FileReader读取多行?

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

I have the following code: 我有以下代码:

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

and a file Test.txt with the content: 还有一个文件Test.txt ,其内容为:

 Hello Java 

When I run above code it only reads Hello . 当我运行以上代码时,它只会读取Hello I would like to read multiple lines using FileReader only. 我只想使用FileReader读取多行。 I don't want to use BufferedReader or InputStreamReader etc. Is that possible? 我不想使用BufferedReaderInputStreamReader等。这可能吗?

I don't think this version of the code prints "Hello". 我认为此版本的代码不会显示“ Hello”。

You are calling: 您正在致电:

int line = in.read();

What does this do? 这是做什么的? Look in the Javadocs for Reader : 在Javadocs中查找Reader

public int read() 公共诠释read()

throws IOException 引发IOException

Reads a single character . 读取一个字符 This method will block until a character is available, an I/O error occurs, or the end of the stream is reached. 该方法将一直阻塞,直到有字符可用,发生I / O错误或到达流的末尾为止。

(emphasis mine) (强调我的)

Your code reads the 'H' from 'Hello', which is 72 in ASCII. 您的代码从“ Hello”中读取“ H”,ASCII中为72。

Then it goes into your loop, with line==72, so it goes into the loop: 然后以line == 72进入循环,因此进入循环:

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

... making the decision "is 0 less than 72? Yes, so I'll go into the loop block". ...做出决定“ 0是否小于72?是的,所以我将进入循环块”。

Then each time it reads a character the value of line changes to another integer, and each time loop goes around i increments. 然后,每次读取一个字符时, line的值都会更改为另一个整数,并且每次循环都以i增量。 So the loop says "Keep going for as long as the ASCII value of the character is greater than the number of iterations I've counted". 因此循环显示“只要字符的ASCII值大于我所计算的迭代次数,就继续运行”。

... and each time it goes around, it prints that character on a line of its own. ...并且每次旋转时,它都会在一行上打印该字符。

As it happens, for your input, it reads end-of-file (-1), and as -1 < i , the loop continue condition is not met. 碰巧的是,对于您的输入,它将读取文件结尾(-1),并且当-1 < i ,不满足循环继续条件。

But for longer inputs it stop on the first 'a' after the 97th character, or the first 'b' after the 98th character, and so on (because ASCII 'a' is 97, etc.) 但是对于更长的输入,它停在第97个字符后的第一个“ a”或第98个字符后的第一个“ b”,依此类推(因为ASCII“ a”为97,依此类推)

H
e
l
l
o


J
a
v
a

This isn't what you want: 这不是您想要的:

  • You don't want your loop to repeat until i >= "the character I just read". 您不希望您的循环重复进行,直到i> =“我刚刚读过的字符”为止。 You want it to repeat until in.read() returns -1 . 您希望它重复执行,直到in.read()返回-1为止。 You have probably been taught how to loop until a condition is met. 您可能已经被教过如何循环直到满足条件。
  • You don't want to println() each character, since that adds newlines you don't want. 您不需要println()每个字符,因为这会添加不需要的换行符。 Use print() . 使用print()

You should also look at the Reader.read(byte[] buffer) method, and see if you can write the code to work in bigger chunks. 您还应该查看Reader.read(byte[] buffer)方法,看看是否可以编写代码以在更大的块中工作。


Two patterns you'll use over and over again in your programming career are: 您将在编程生涯中反复使用两种模式:

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

... and ... ...和...

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

See if you can construct something with FileReader and the value you get from it, filling in the "somehows". 看看是否可以使用FileReader构造某些东西,并从中获取值,并填写“某种方式”。

You will have to read the content char by char and parse for a new line sequence. 您将必须逐字符读取内容char并解析新的行序列。

A new line sequence can be any of the following: 新行顺序可以是以下任意一种:

  1. a single cariage return '\\r' 单个回车符'\\r'
  2. a single line feed '\\n' 单个换行符'\\n'
  3. a carriage return followed by a line feed "\\r\\n" 回车符后跟换行符"\\r\\n"

EDIT 编辑

You could try the following: 您可以尝试以下方法:

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

Note that the code above reads the whole file into a List , this might not be well suited for large files! 请注意,上面的代码将整个文件读入List ,这可能不适用于大文件! You may want in such a case to implement an approach which uses streaming, ie read one line at a time, for example String readNextLine(FileReader fileReader) { ... } . 在这种情况下,您可能希望实现一种使用流传输的方法,即一次读取一行,例如String readNextLine(FileReader fileReader) { ... }

Some basic tests: 一些基本测试:

Create test files to read 创建要读取的测试文件

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 using the created files 使用创建的文件进行测试

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

Reading file character by character without any buffering stream is extremely ineffective. 逐字符读取文件而没有任何缓冲流是极其无效的。 I would probably wrap FileReader in some BufferedReader or simply used Scanner to read condent of file, but if you absolutely want/need/have to use only FileReader then you can try with 我可能会将FileReader包装在某些BufferedReader中,或者仅使用Scanner来读取文件的内容,但是如果您绝对希望/需要/只使用FileReader,则可以尝试使用

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

instead of your for (int i = 0; i < line; i++) {...} loop. 而不是for (int i = 0; i < line; i++) {...}循环。

Read carefully slims answer . 仔细阅读苗条答案 In short: reading condition shouldn't care if number of characters you read is less then numeric representation of currently read character ( i < line ). 简而言之:如果阅读的字符数少于当前阅读的字符的数字表示形式( i < line ),则阅读条件无关紧要。 Like in case of 就像在

My name

is

not important now

This file has few characters which you normally will not see like \\r and \\n and in reality it looks like 该文件中的几个字符通常不会像\\r\\n一样出现,实际上,它看起来像

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

where numeric representation of \\r is 10 , so after you read My name\\r\\n (which is 9 characters because \\r and \\n are single character representing line separator) your i will become 10 and since next character you will try to read is \\r which is also represented by 10 your condition i<line will fail ( 10<10 is not true). 其中\\r数字表示为10 ,因此在阅读My name\\r\\n (这是9个字符,因为\\r\\n是表示行分隔符的单个字符)后,您的i将变为10并且由于下一个字符,读取的是\\r ,它也由10表示,您的条件i<line将失败( 10<10不正确)。

So instead of checking i<line you should check if read value is not EoF (End of File, or End of Stream in out case) which is represented by -1 as specified in read method documentation so your condition should look like line != -1 . 因此,而不是检查i<line您应该检查读取的值是否不是EoF(文件末尾或流末尾的out),该值由read方法文档中指定的-1表示,因此您的条件应类似于line != -1 And because you don't need i just use while loop here. 而且因为您不需要, iwhile这里使用while循环。

Returns: 返回:

The character read, or -1 if the end of the stream has been reached 读取的字符;如果已到达流的末尾,则返回-1

If you have the new line character 如果您有换行符

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

It will print 它将打印

Hello 
Java

Reader.read() returns int code of single char or -1 if end of the file is reached: Reader.read()返回单字符的int代码,如果到达文件末尾,则返回-1:

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

So, read the file char by char and check LF (Line feed, '\\n', 0x0A, 10 in decimal), CR (Carriage return, '\\r', 0x0D, 13 in decimal)and end-of-line codes. 因此,按字符读取文件char并检查LF(换行,'\\ n',0x0A,十进制为10),CR(回车,'\\ r',0x0D,十进制为13)和行尾代码。

Note: Windows OS uses 2 chars to encode the end of line: "\\r\\n". 注意:Windows操作系统使用2个字符对行尾进行编码:“ \\ r \\ n”。 The most of others including Linux, MacOS, etc. use only "\\n" to encode the end of line. 其他大多数语言(包括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
        }
    }

I solved the above problem by using this code 我通过使用此代码解决了上述问题

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

But there is one more question if I write for loop instead of while like this 但是还有一个问题,如果我写for循环而不是像这样

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

It prints only first line.Could anybody tell me why? 它只打印第一行。有人可以告诉我为什么吗?

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

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