簡體   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