У меня такой код:

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 и т. Д. Возможно ли это?

0
TruePS 5 Мар 2014 в 17:30

6 ответов

Лучший ответ

Чтение файла посимвольно без какого-либо потока буферизации крайне неэффективно. Я бы, вероятно, обернул FileReader в некоторый BufferedReader или просто использовал Scanner для чтения condent файла, но если вы абсолютно хотите / вам / нужно использовать только FileReader, вы можете попробовать с

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

Вместо вашего цикла for (int i = 0; i < line; i++) {...}.

Внимательно ознакомьтесь с ответом на slims. Вкратце: условие чтения не должно волновать, если количество символов, которые вы читаете, меньше числового представления текущего прочитанного символа (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), который представлен -1, как указано в read документацию по методу, чтобы ваша состояние должно выглядеть как line != -1. И поскольку вам не нужен i, просто используйте здесь цикл while.

Возврат:

Прочитанный символ или -1, если достигнут конец потока

1
Community 20 Июн 2020 в 09:12

Я не думаю, что эта версия кода печатает «Привет».

Ты звонишь:

int line = in.read();

Что это значит? Поищите в Javadocs Reader:

public int read ()

выбрасывает IOException

Читает один символ . Этот метод будет блокироваться до тех пор, пока не станет доступен символ, пока не возникнет ошибка ввода-вывода или не будет конца. потока достигнута.

(курсив мой)

Ваш код читает "H" из "Hello", что составляет 72 в ASCII.

Затем он переходит в ваш цикл со строкой == 72, поэтому он входит в цикл:

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

... принятие решения «0 меньше 72? Да, я перейду к блоку цикла».

Затем каждый раз, когда он считывает символ, значение line изменяется на другое целое число, и каждый временной цикл увеличивается на i. Таким образом, цикл говорит: «Продолжайте, пока значение ASCII символа больше, чем количество итераций, которые я подсчитал».

... и каждый раз, когда он проходит, он печатает этот символ в отдельной строке.

Как это часто бывает, для вашего ввода он считывает конец файла (-1), и, поскольку -1 < i, условие продолжения цикла не выполняется.

Но для более длинных входов он останавливается на первом 'a' после 97-го символа или на первом 'b' после 98-го символа и так далее (потому что 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 и значением, которое вы получите от него, заполнив «как-то».

3
slim 6 Мар 2014 в 10:29

Вам нужно будет прочитать содержимое char по 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());
}
1
A4L 6 Мар 2014 в 13:05

Если у вас есть символ новой строки

    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
0
Zhenxiao Hao 5 Мар 2014 в 13:41

Я решил вышеуказанную проблему, используя этот код

     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(int i=0;i<line;i++)

Он печатает только первую строку. Кто-нибудь может сказать мне, почему?

0
TruePS 6 Мар 2014 в 05:46

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
        }
    }
0
AnatolyG 10 Мар 2014 в 10:19