[英]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.