Як прочитати певний рядок, використовуючи конкретний номер рядка з файлу на 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.