จะอ่านบรรทัดเฉพาะโดยใช้หมายเลขบรรทัดเฉพาะจากไฟล์ใน Java ได้อย่างไร


91

ใน Java มีวิธีการอ่านบรรทัดใดบรรทัดหนึ่งจากไฟล์หรือไม่? ตัวอย่างเช่นอ่านบรรทัดที่ 32 หรือหมายเลขบรรทัดอื่น ๆ


5
ฉันรู้ว่าฉันมาช้ามาก แต่ในกรณีที่มีคนพบคำถามนี้: โปรดทราบว่าไฟล์เป็นเพียงลำดับไบต์ และขึ้นบรรทัดใหม่เป็นเพียงอักขระ (สองตัวบน Windows) และอักขระเป็นเพียงหนึ่งไบต์ (หรือมากกว่าขึ้นอยู่กับการเข้ารหัส) และหากไม่มีดัชนีของตำแหน่งไบต์ในไฟล์วิธีเดียวที่จะทราบว่าไบต์เหล่านั้นอยู่ที่ใดคือการอ่านและค้นหา (ไม่ได้หมายความว่าคุณต้องโหลดทั้งไฟล์ลงในหน่วยความจำ)
szgal

คำตอบ:


72

หากคุณไม่มีความรู้มาก่อนเกี่ยวกับบรรทัดในไฟล์ไม่มีวิธีใดที่จะเข้าถึงบรรทัดที่ 32 ได้โดยตรงโดยไม่ต้องอ่าน 31 บรรทัดก่อนหน้านี้

นั่นเป็นความจริงสำหรับทุกภาษาและระบบไฟล์สมัยใหม่ทั้งหมด

อย่างมีประสิทธิภาพคุณจะอ่านบรรทัดจนกว่าคุณจะพบบรรทัดที่ 32


4
"ความรู้เดิม" สามารถทำได้ง่ายเพียงแค่รูปแบบขนาดเส้นที่แน่นอนในไฟล์เพื่อให้คุณสามารถค้นหาตำแหน่งที่ต้องการได้
Murali VP

2
@ มูราลี: แน่นอน นอกจากนี้ยังอาจเป็นดัชนีของหมายเลขบรรทัดเพื่อชดเชยไบต์หรือชุดค่าผสมใด ๆ
Joachim Sauer

104

สำหรับไฟล์ขนาดเล็ก:

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: Input length = 1 เมื่อใช้โดยใช้ค่า skip เป็น 1
canadiancreed

นั่นไม่ใช่ปัญหากับรหัสที่ฉันโพสต์ แต่เป็นปัญหาเกี่ยวกับการเข้ารหัสไฟล์ของคุณ ดูโพสต์นี้
aioobe

4
"โปรดทราบว่าวิธีนี้มีไว้สำหรับกรณีง่ายๆที่สะดวกในการอ่านทุกบรรทัดในการดำเนินการเดียวไม่ได้มีไว้สำหรับการอ่านไฟล์ขนาดใหญ่" - Javadoc ของ Files.readAllLines ()
PragmaticProgrammer

รหัสต่อไปนี้ต้องใช้ API ระดับ 26 ใน Android ถ้าขั้นต่ำของเราน้อยกว่าระดับนี้ก็จะไม่ทำงาน
Ali Azaz Alam

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

แน่นอนว่า Joachim เปิดอยู่และการใช้งานทางเลือกกับ 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 ซึ่งอาจใช้ไม่ได้กับไฟล์ขนาดใหญ่และจะช้ากว่าการอ่าน util เพื่อค้นหาบรรทัดที่ 32 ...
beny23

จุดยุติธรรมใช่ ฉันได้ใช้สิ่งนั้นในกรณีทดสอบซึ่งไฟล์ทดสอบมีขนาดเล็ก แต่ในไฟล์ขนาดใหญ่ก็เห็นด้วยไม่ใช่แนวทางที่ดี
Charlie Collins

โพสต์คำตอบที่อัปเดตเพื่อแสดงว่า FileUtils เป็นความคิดที่ไม่ดีในกรณีการใช้งานนี้หากคุณมีไฟล์ขนาดใหญ่และไม่ต้องการอะไรเลยนอกเหนือจากบรรทัด X
Charlie Collins

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' ครั้ง วิธีการข้างต้นมีประโยชน์หากคุณต้องการรับบรรทัดเฉพาะ (ตามจำนวนบรรทัดของหลักสูตร) ​​หลาย ๆ ครั้ง

ขอบคุณ!


มีบางอย่างที่ต้องแก้ไขเพื่อให้โค้ดด้านบนใช้งานได้ และหากมีคนต้องการ charset ที่เกี่ยวข้องคุณควรอ่านสิ่งนี้จริงๆ: 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
สง่างามฉันชอบมัน
Florescu Cătălin

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 of Strings จากนั้นก็เลือกเส้นที่ต้องการได้เลย

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)
Tachenko

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 นอกจากวิธีการอ่านแล้วยังมีวิธี 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);

}

0

วิธีง่ายๆ - การอ่านบรรทัดโดยใช้หมายเลขบรรทัด สมมติว่าหมายเลขบรรทัดเริ่มต้นจาก 1 ถึง null

public class TextFileAssignmentOct {
    
    private void readData(int rowNum, BufferedReader br) throws IOException {
        int n=1;                                    //Line number starts from 1
        String row;
        while((row=br.readLine()) != null)  {       // Reads every line
            if (n == rowNum) {                      // When Line number matches with which you want to read
                System.out.println(row);
            }
            n++;                                    //This increments Line number
        }
    }

    public static void main(String[] args) throws IOException {
        File f = new File("../JavaPractice/FileRead.txt");
        FileReader fr = new FileReader(f);
        BufferedReader br = new BufferedReader(fr);
        
        TextFileAssignmentOct txf = new TextFileAssignmentOct();
        txf.readData(4, br);    //Read a Specific Line using Line number and Passing buffered reader
    }
}

-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; }}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.