Làm thế nào để đọc một dòng cụ thể bằng cách sử dụng số dòng cụ thể từ một tệp trong Java?


90

Trong Java, có phương pháp nào để đọc một dòng cụ thể từ một tệp không? Ví dụ: đọc dòng 32 hoặc bất kỳ số dòng nào khác.


5
Tôi biết mình đến rất muộn, nhưng trong trường hợp ai đó tìm thấy câu hỏi này: Lưu ý rằng một tệp chỉ là một chuỗi các byte. Và một dòng mới chỉ là một ký tự (hai trên Windows) và một ký tự chỉ là một (hoặc nhiều, tùy thuộc vào mã hóa) byte. Và, nếu không có chỉ mục về các vị trí của byte trong tệp, cách duy nhất để biết các byte đó ở đâu là đọc nó và tìm kiếm nó. (điều này không có nghĩa là bạn phải tải toàn bộ tệp vào bộ nhớ).
szgal

Câu trả lời:


71

Trừ khi bạn đã có kiến ​​thức trước đó về các dòng trong tệp, không có cách nào để truy cập trực tiếp vào dòng thứ 32 mà không đọc 31 dòng trước đó.

Điều đó đúng cho tất cả các ngôn ngữ và tất cả các hệ thống tệp hiện đại.

Vì vậy, hiệu quả là bạn sẽ chỉ cần đọc các dòng cho đến khi bạn tìm thấy dòng thứ 32.


4
"Kiến thức trước đây" có thể đơn giản như mẫu kích thước dòng chính xác trong tệp để bạn có thể tìm kiếm một vị trí nhất định.
VP Murali

2
@Murali: tất nhiên. Nó cũng có thể là một chỉ số của số dòng để bù byte hoặc bất kỳ sự kết hợp nào của chúng.
Joachim Sauer

103

Đối với các tệp nhỏ:

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

Đối với các tệp lớn:

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

Điều này trả về java.nio.charset.MalformedInputException: Chiều dài đầu vào = 1 khi được sử dụng với giá trị bỏ qua là 1
canadiancreed

Đó không phải là vấn đề với mã tôi đã đăng, mà là vấn đề với mã hóa tệp của bạn. Xem bài đăng này .
aioobe

3
"Lưu ý rằng phương pháp này dành cho các trường hợp đơn giản, nơi thuận tiện để đọc tất cả các dòng trong một thao tác. Nó không nhằm mục đích đọc trong các tệp lớn." - Javadoc của Files.readAllLines ()
PragmaticProgrammer

Đoạn mã sau yêu cầu API cấp 26 trong android. Nếu phút của chúng tôi là ít hơn mức này thì nó không làm việc
Ali Azaz Alam

38

Tôi không biết, nhưng những gì bạn có thể làm là lặp lại 31 dòng đầu tiên mà không làm gì bằng cách sử dụng hàm readline () của 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
Xin lưu ý rằng ở đây bạn đọc dòng số 30, là dòng thứ 31 vì chúng tôi bắt đầu số dòng từ 0. Vì vậy, tôi khuyên bạn nên thay đổi nó để khớp chính xác với câu hỏi.
kiedysktos

15

Tất nhiên, Joachim đã đúng và một cách triển khai thay thế cho Chris '(chỉ dành cho các tệp nhỏ vì nó tải toàn bộ tệp) có thể là sử dụng commons-io từ Apache (mặc dù có thể bạn không muốn giới thiệu một phần phụ thuộc mới chỉ dành cho điều này, nếu bạn thấy nó cũng hữu ích cho những thứ khác, nó có thể có ý nghĩa).

Ví dụ:

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
Mặc dù điều đó sẽ tải toàn bộ tệp vào bộ nhớ trước khi chuyển đến dòng thứ 32, điều này có thể không thực tế với tệp lớn và sẽ chậm hơn so với việc đọc bằng cách tìm dòng thứ 32 ...
beny23

Điểm khá, có. Tôi đã sử dụng điều đó trong các trường hợp thử nghiệm, nơi các tệp thử nghiệm nhỏ, tuy nhiên, trên các tệp lớn, được đồng ý, không phải là một cách tiếp cận tốt.
Charlie Collins

Bài đăng câu trả lời được cập nhật để phản ánh rằng FileUtils là một ý tưởng tồi trong trường hợp sử dụng này nếu bạn có một tệp lớn và không cần bất kỳ thứ gì ngoài dòng X.
Charlie Collins

11

Bạn có thể thử lập chỉ mục-tệp-đọc (Giấy phép Apache 2.0). Lớp IndexedFileReader có một phương thức gọi là readLines (int from, int to) trả về một Bản đồ sắp xếp có khóa là số dòng và giá trị là dòng đã được đọc.

Thí dụ:

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]"));      

Ví dụ trên đọc một tệp văn bản gồm 50 dòng ở định dạng sau:

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

Disclamer: Tôi đã viết thư viện này


6

Mặc dù như đã nói trong các câu trả lời khác, không thể đến dòng chính xác mà không biết phần bù (con trỏ) trước đó. Vì vậy, tôi đã đạt được điều này bằng cách tạo một tệp chỉ mục tạm thời sẽ lưu trữ các giá trị bù đắp của mọi dòng. Nếu tệp đủ nhỏ, bạn có thể chỉ lưu các chỉ mục (offset) trong bộ nhớ mà không cần tệp riêng cho nó.

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();

Ngoài ra, hãy truy cập tài liệu java để biết thêm thông tin: https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html#mode

Độ phức tạp : Đây là O (n) vì nó đọc toàn bộ tệp một lần. Vui lòng lưu ý các yêu cầu về bộ nhớ. Nếu nó quá lớn để có trong bộ nhớ, hãy tạo một tệp tạm thời để lưu trữ các phần bù thay vì ArrayList như được hiển thị ở trên.

Lưu ý : Nếu tất cả những gì bạn muốn trong dòng '32', bạn chỉ cần gọi readLine () cũng có sẵn thông qua các lớp khác '32' lần. Cách tiếp cận trên rất hữu ích nếu bạn muốn lấy một dòng cụ thể (tất nhiên là dựa trên số dòng) nhiều lần.

Cảm ơn !


Có điều gì đó cần chỉnh sửa để làm cho đoạn mã trên hoạt động. Và nếu ai đó cần bộ ký tự liên quan, bạn thực sự nên đọc cái này: stackoverflow.com/a/37275357/3198960
rufushuang 22/02/18

5

Cách khác.

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
Thanh lịch, tôi thích nó.
Florescu Cătălin

4

Không, trừ khi ở định dạng tệp đó, độ dài dòng được xác định trước (ví dụ: tất cả các dòng có độ dài cố định), bạn sẽ phải lặp lại từng dòng để đếm chúng.


2

Nếu bạn đang nói về một tệp văn bản, thì thực sự không có cách nào để làm điều này mà không đọc tất cả các dòng đứng trước nó - Sau cùng, các dòng được xác định bởi sự hiện diện của một dòng mới, vì vậy nó phải được đọc.

Sử dụng một luồng hỗ trợ dòng đọc và chỉ đọc những dòng X-1 đầu tiên và kết xuất kết quả, sau đó xử lý dòng tiếp theo.


2

Trong Java 8,

Đối với các tệp nhỏ:

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

Đối với các tệp lớn:

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

Trong 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();
}

Nguồn: Đang đọc dòng thứ n từ tệp


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

Nó hoạt động cho tôi: Tôi đã kết hợp câu trả lời của Đọc một tệp văn bản đơn giản

Nhưng thay vì trả về một Chuỗi, tôi đang trả về một Danh sách chuỗi liên kết. Sau đó, tôi có thể chọn dòng mà tôi muốn.

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;
}

Sau đó, bạn có thể làm: linkedlist.get (31)
Tachenko

1

Sử dụng mã này:

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

Bạn có thể sử dụng LineNumberReader thay vì BufferedReader. Đi qua api. Bạn có thể tìm thấy các phương thức setLineNumber và getLineNumber.


không giúp đỡ - bạn vẫn phải đọc tất cả các dòng trước ... ngoại trừ bạn gian lận và thiết lập các dòng đầu tiên đến 32 <g>
Kleopatra

0

Bạn cũng có thể xem qua LineNumberReader, lớp con của BufferedReader. Cùng với phương thức readline, nó cũng có các phương thức setter / getter để truy cập số dòng. Rất hữu ích để theo dõi số dòng đã đọc, trong khi đọc dữ liệu từ tệp.


0

bạn có thể sử dụng chức năng bỏ qua () để bỏ qua các dòng bắt đầu.

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

Tất cả đều sai Tôi chỉ viết điều này trong khoảng 10 giây. Với điều này, tôi quản lý để chỉ gọi object.getQuestion ("linenumber") trong phương thức chính để trả về bất kỳ dòng nào tôi muốn.

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; }}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.