Як прочитати певний рядок, використовуючи конкретний номер рядка з файлу на Java?


90

У Java існує спосіб читання певного рядка з файлу? Наприклад, прочитайте рядок 32 або будь-який інший номер рядка.


5
Я знаю, що запізнився, але на випадок, якщо хтось знайшов це запитання: Зверніть увагу, що файл - це просто послідовність байтів. І новий рядок - це просто символ (два у Windows), а символ - це лише один (або більше, залежно від кодування) байт. І, не маючи індексу позицій байта у файлі, єдиний спосіб дізнатись, де ці байти, - це прочитати його та шукати. (це не означає, що вам потрібно завантажувати весь файл в пам'ять).
— szgal

Відповіді:


71

Якщо у вас немає попередніх знань про рядки у файлі, немає можливості безпосередньо отримати доступ до 32-го рядка, не прочитавши 31 попередній рядок.

Це справедливо для всіх мов та всіх сучасних файлових систем.

Так ефективно ви просто будете читати рядки, поки не знайдете 32-й.


4
"Попередні знання" можуть бути простими, як точний шаблон розміру рядка у файлі, щоб ви могли шукати певної позиції.
— Murali VP

2
@Murali: звичайно. Це також може бути індекс номера рядка для зміщення байтів або будь-якої їх комбінації.
— Йоахім Зауер

103

Для невеликих файлів:

String line32 = Files.readAllLines(Paths.get("file.txt")).get(32)

Для великих файлів:

try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    line32 = lines.skip(31).findFirst().get();
}

Це повертає java.nio.charset.MalformedInputException: Вхідна довжина = 1 при використанні зі значенням пропуску 1
— canadiancreed

Це не проблема з кодом, який я опублікував, це проблема з кодуванням вашого файлу. Дивіться цю публікацію .
— aioobe

3
"Зауважте, що цей метод призначений для простих випадків, коли зручно читати всі рядки за одну операцію. Він не призначений для читання у великих файлах." - Javadoc of Files.readAllLines ()
— PragmaticProgrammer

Наступний код вимагає API рівня 26 в android. Якщо наш хв менше, ніж цей рівень, це не спрацьовує
— Алі Азаз Алам

38

Не те, що я знаю, але те, що ви могли б зробити, це прокрутити перші 31 рядок, не роблячи нічого, використовуючи функцію readline () BufferedReader

FileInputStream fs= new FileInputStream("someFile.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
for(int i = 0; i < 31; ++i)
  br.readLine();
String lineIWant = br.readLine();

1
Зверніть увагу, що тут ви читаєте рядок номер 30, який є 31-м рядком, оскільки ми починаємо номери рядків з 0. Тому я пропоную змінити його, щоб точно відповідати питанню.
— kiedysktos

15

Звичайно, Йоахім правий, і альтернативною реалізацією Chris '(для невеликих файлів лише тому, що він завантажує весь файл) може бути використання commons-io від Apache (хоча, можливо, ви не хочете вводити нову залежність лише для це, якщо ви вважаєте це корисним для інших речей, хоча це може мати сенс).

Наприклад:

String line32 = (String) FileUtils.readLines(file).get(31);

http://commons.apache.org/io/api-release/org/apache/commons/io/FileUtils.html#readLines(java.io.File , java.lang.String)


2
Хоча це завантажило б цілий файл у пам'ять перед переходом до 32-го рядка, що, можливо, не було б практичним для великого файлу і було б повільнішим, ніж читання утиліти для пошуку 32-го рядка ...
— beny23

Справедливий момент, так. Я використовував це у тестових випадках, коли файли тестування були невеликими, але для великих файлів, погоджуюсь, це невдалий підхід.
— Чарлі Коллінз,

Оновлений пост відповідей, який відображає, що FileUtils - погана ідея в цьому випадку, якщо у вас великий файл і вам не потрібно нічого за лінією X.
— Чарлі Коллінз,

11

Ви можете спробувати програму для читання індексованих файлів (Apache License 2.0). Клас IndexedFileReader має метод readLines (int from, int to) який повертає SortedMap, ключем якого є номер рядка, а значенням є рядок, який було прочитано.

Приклад:

File file = new File("src/test/resources/file.txt");
reader = new IndexedFileReader(file);

lines = reader.readLines(6, 10);
assertNotNull("Null result.", lines);
assertEquals("Incorrect length.", 5, lines.size());
assertTrue("Incorrect value.", lines.get(6).startsWith("[6]"));
assertTrue("Incorrect value.", lines.get(7).startsWith("[7]"));
assertTrue("Incorrect value.", lines.get(8).startsWith("[8]"));
assertTrue("Incorrect value.", lines.get(9).startsWith("[9]"));
assertTrue("Incorrect value.", lines.get(10).startsWith("[10]"));      

У наведеному вище прикладі читається текстовий файл із 50 рядків у наступному форматі:

[1] The quick brown fox jumped over the lazy dog ODD
[2] The quick brown fox jumped over the lazy dog EVEN

Disclamer: Я написав цю бібліотеку


6

Хоча, як сказано в інших відповідях, неможливо дійти до точної лінії, не знаючи зсуву (покажчика) раніше. Отже, я досяг цього, створивши тимчасовий файл індексу, який зберігав би значення зміщення кожного рядка. Якщо файл досить малий, ви можете просто зберігати індекси (зміщення) в пам'яті, не потребуючи для цього окремого файлу.

The offsets can be calculated by using the RandomAccessFile

    RandomAccessFile raf = new RandomAccessFile("myFile.txt","r"); 
    //above 'r' means open in read only mode
    ArrayList<Integer> arrayList = new ArrayList<Integer>();
    String cur_line = "";
    while((cur_line=raf.readLine())!=null)
    {
    arrayList.add(raf.getFilePointer());
    }
    //Print the 32 line
    //Seeks the file to the particular location from where our '32' line starts
    raf.seek(raf.seek(arrayList.get(31));
    System.out.println(raf.readLine());
    raf.close();

Також відвідайте java-документи для отримання додаткової інформації: https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html#mode

Складність : це O (n), оскільки він читає весь файл один раз. Зверніть увагу на вимоги до пам'яті. Якщо він занадто великий, щоб бути в пам'яті, створіть тимчасовий файл, який зберігає зміщення замість ArrayList, як показано вище.

Примітка : Якщо все, що вам потрібно в рядку '32', вам просто потрібно зателефонувати readLine (), який також доступний у інших класах '32' разів. Вищевказаний підхід корисний, якщо ви хочете отримати конкретний рядок (на основі номера рядка, звичайно) кілька разів.

Дякую !


Існує щось, що потрібно редагувати, щоб згаданий код працював. І якщо хто - то стосується необхідності кодувань, у дійсно повинен прочитати: stackoverflow.com/a/37275357/3198960
— rufushuang

5

Інший спосіб.

try (BufferedReader reader = Files.newBufferedReader(
        Paths.get("file.txt"), StandardCharsets.UTF_8)) {
    List<String> line = reader.lines()
                              .skip(31)
                              .limit(1)
                              .collect(Collectors.toList());

    line.stream().forEach(System.out::println);
}

1
Елегантно, мені це подобається.
— Флореску Каталін,

4

Ні, якщо у цьому форматі файлу довжини рядків не визначені заздалегідь (наприклад, усі рядки з фіксованою довжиною), вам доведеться перебирати рядки за рядками, щоб підрахувати їх.


2

Якщо ви говорите про текстовий файл, то насправді неможливо це зробити, не прочитавши всі рядки, що передують йому - Зрештою, рядки визначаються наявністю нового рядка, тому його доводиться читати.

Використовуйте потік, який підтримує лінію зчитування, і просто прочитайте перші рядки X-1 і скиньте результати, а потім обробіть наступний.


2

У Java 8,

Для невеликих файлів:

String line = Files.readAllLines(Paths.get("file.txt")).get(n);

Для великих файлів:

String line;
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    line = lines.skip(n).findFirst().get();
}

У Java 7

String line;
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    for (int i = 0; i < n; i++)
        br.readLine();
    line = br.readLine();
}

Джерело: Читання n-го рядка з файлу


1
public String readLine(int line){
        FileReader tempFileReader = null;
        BufferedReader tempBufferedReader = null;
        try { tempFileReader = new FileReader(textFile); 
        tempBufferedReader = new BufferedReader(tempFileReader);
        } catch (Exception e) { }
        String returnStr = "ERROR";
        for(int i = 0; i < line - 1; i++){
            try { tempBufferedReader.readLine(); } catch (Exception e) { }
        }
        try { returnStr = tempBufferedReader.readLine(); }  catch (Exception e) { }

        return returnStr;
    }

1

Це працює для мене: я поєднав відповідь читання простого текстового файлу

Але замість повернення рядка я повертаю LinkedList рядків. Тоді я можу вибрати потрібний рядок.

public static LinkedList<String> readFromAssets(Context context, String filename) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(context.getAssets().open(filename)));
    LinkedList<String>linkedList = new LinkedList<>();
    // do reading, usually loop until end of file reading
    StringBuilder sb = new StringBuilder();
    String mLine = reader.readLine();
    while (mLine != null) {
        linkedList.add(mLine);
        sb.append(mLine); // process line
        mLine = reader.readLine();


    }
    reader.close();
    return linkedList;
}

Потім ви можете зробити: linkedlist.get (31)
— Таченко

1

Використовуйте цей код:

import java.nio.file.Files;

import java.nio.file.Paths;

public class FileWork 
{

    public static void main(String[] args) throws IOException {

        String line = Files.readAllLines(Paths.get("D:/abc.txt")).get(1);

        System.out.println(line);  
    }

}

0

Ви можете використовувати LineNumberReader замість BufferedReader. Пройдіть через api. Ви можете знайти методи setLineNumber та getLineNumber.


не допомагає - вам все одно доведеться прочитати всі рядки до ... крім того, що ви обдурите і встановите для першого рядка значення 32 <g>
— kleopatra

0

Ви також можете поглянути на LineNumberReader, підклас BufferedReader. Поряд із методом readline, він також має методи setter / getter для доступу до номера рядка. Дуже корисно відстежувати кількість прочитаних рядків під час зчитування даних із файлу.


0

Ви можете використовувати функцію skip (), щоб пропустити рядки з початку.

public static void readFile(String filePath, long lineNum) {
    List<String> list = new ArrayList<>();
    long totalLines, startLine = 0;

    try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
        totalLines = Files.lines(Paths.get(filePath)).count();
        startLine = totalLines - lineNum;
        // Stream<String> line32 = lines.skip(((startLine)+1));

        list = lines.skip(startLine).collect(Collectors.toList());
        // lines.forEach(list::add);
    } catch (IOException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

    list.forEach(System.out::println);

}

-7

Вони всі помиляються. Я щойно написав це приблизно за 10 секунд. За допомогою цього мені вдалося просто викликати object.getQuestion ("linenumber") в основному методі, щоб повернути будь-який рядок, який я хочу.

public class Questions {

File file = new File("Question2Files/triviagame1.txt");

public Questions() {

}

public String getQuestion(int numLine) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(file));
    String line = "";
    for(int i = 0; i < numLine; i++) {
        line = br.readLine();
    }
    return line; }}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.