[英]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
讀取多行。 我不想使用BufferedReader
或InputStreamReader
等。這可能嗎?
我認為此版本的代碼不會顯示“ 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
這不是您想要的:
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並解析新的行序列。
新行順序可以是以下任意一種:
'\\r'
'\\n'
"\\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
。 而且因為您不需要, i
只while
這里使用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.