BufferedReader читается по токену вместо readLine()?

Есть ли способ читать из BufferedReader до тех пор, пока он не достигнет символа, который не является новой строкой или возвратом каретки (что и делает readLine())?

Я не хочу читать строку, а затем разделять. Я хочу игнорировать символы новой строки и возврата каретки и рассматривать только конкретный токен (например, вкладку) с производительностью BufferedReader.


person Leo    schedule 29.05.2016    source источник
comment
Вы можете обернуть BufferedReader в сканер.   -  person matt    schedule 31.05.2016


Ответы (4)


Для чего-то подобного вам даже не следует использовать BufferedReader. Я бы использовал NIO так:

public String[] splitContentsBy(String split, File file){
    try{
        byte[] bytes = Files.readAllBytes(file);
        String contents = new String(bytes);
        String[] array = contents.spilt(split);
    }catch(IOException e){
        e.printStackTrace();
    }
}

Если вам нужен только персонаж, вы можете иметь:

char c = '?'; //A question mark, as an example.
String[] parts = splitContentsBy(String.valueOf(c), new File("file.txt");
person JD9999    schedule 31.05.2016
comment
хорошо, я согласен, что это решает проблему, но может быть не очень хорошей идеей для достаточно большого текстового файла (например, журналов сервера) - person Leo; 31.05.2016

Да конечно.

 BufferedReader br = ...
 StringBuilder sb = new StringBuilder(ESTIMATED_LENGTH);
 int ch;
 while ((ch = br.read()) != -1 && ch != '\t') {
     sb.append(ch);
 } 

В лучшем случае вы получите производительность, почти не уступающую BufferedReader.readLine(). В худшем случае вы выполняете одну-две дополнительные копии символов (я думаю)... что не так уж плохо для производительности1.

Чтобы получить производительность, которая не уступает BufferedReader, потребуется взломать сам код BufferedReader... или переписать его.

(Ваша попытка расширения BufferedReader не работает, потому что вы вызываете private методы из родительского класса. Это не разрешено! Если вы собираетесь «исправить» это, изменив доступ к методу, вы можете просто «клонировать» BufferedReader и добавьте к нему свои методы. Конечно, ваш класс больше не является java.io.BufferedReader или его подклассом.)


1 – Для обоснования рассмотрите общую картину. Если вы откуда-то читаете большие объемы данных, узким местом производительности, вероятно, будет либо ввод-вывод, либо то, что вы делаете с токенами после их чтения. Если это не так, то вам, вероятно, следует использовать 1) java.nio / CharBuffer, 2) собственный стек ввода-вывода или 3) другой язык программирования.

person Stephen C    schedule 29.05.2016
comment
на самом деле шаги с 1 по 4 показывают, что я работаю с копией BufferedReader, и все частные атрибуты и методы были изменены на защищенные (шаг 3) - person Leo; 29.05.2016

это не очень элегантно, но может сработать.

  1. скопируйте исходный код openjdk BufferedReader в другой пакет (JVM не позволит вашему загрузчику классов переопределить какой-либо класс Java по умолчанию). Вы можете получить его отсюда -- http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/io/BufferedReader.java/?v=source
  2. создайте подкласс, который расширяется от этой копии BufferedReader, создайте конструкторы и переопределите readLine(). Скопируйте исходную реализацию readLine() и вставьте как переопределенную readLine().
  3. изменить все атрибуты и методы из суперкласса (BufferedReader), которые блокируют компиляцию, с закрытых на защищенные
  4. вместо этого замените все упоминания на \n и ]r в подклассе readLine() на \t (так что вы собираетесь разделить по табуляции)

вуаля :-)

вот так это будет выглядеть в итоге

import java.io.IOException;
import java.io.Reader;

public class MyBufferedReader extends BufferedReader {

    /**
     * 
     * @param in
     */
    public MyBufferedReader(Reader in) {
        super(in);

    }

    @Override
    String readLine(boolean ignoreLF) throws IOException {
        StringBuffer s = null;
        int startChar;

        synchronized (lock) {
            ensureOpen();
            boolean omitLF = ignoreLF || skipLF;

        bufferLoop:
            for (;;) {

                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
                boolean eol = false;
                char c = 0;
                int i;

                /* Skip a leftover '\n', if necessary */
                if (omitLF && (cb[nextChar] == '\t'))
                    nextChar++;
                skipLF = false;
                omitLF = false;

            charLoop:
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if (c == '\t') {
                        eol = true;
                        break charLoop;
                    }
                }

                startChar = nextChar;
                nextChar = i;

                if (eol) {
                    String str;
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    if (c == '\t') {
                        skipLF = true;
                    }
                    return str;
                }

                if (s == null)
                    s = new StringBuffer(defaultExpectedLineLength);
                s.append(cb, startChar, i - startChar);
            }
        }
    }

}

вы можете использовать это так

    MyBufferedReader my = new MyBufferedReader(new InputStreamReader(Main.class.getResourceAsStream("fileWithTabs.txt")));
    String line = null;
    while((line = my.readLine())!=null) {
        System.out.println(line);
    }
    my.close();

для такого входа

some string some other string
some third string after a newline   some forth  
and so on

результат

some string
some other string
some third string after a newline
some forth

and so on

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

person Leo    schedule 29.05.2016

Чтобы игнорировать новую строку и вернуться, просто установите для аргумента readLine значение: String readLine(true); И реализуйте условие для обнаружения вкладок.

person user3771889    schedule 27.10.2017