Làm cách nào để tạo một chuỗi Java từ nội dung của tệp?


1513

Tôi đã sử dụng thành ngữ dưới đây một thời gian. Và nó dường như là phổ biến rộng rãi nhất, ít nhất là trên các trang web tôi đã truy cập.

Có cách nào tốt hơn / khác nhau để đọc tệp thành một chuỗi trong Java không?

private String readFile(String file) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader (file));
    String         line = null;
    StringBuilder  stringBuilder = new StringBuilder();
    String         ls = System.getProperty("line.separator");

    try {
        while((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }

        return stringBuilder.toString();
    } finally {
        reader.close();
    }
}

7
Ai đó có thể giải thích cho tôi một cách rất đơn giản những gì với NIO không? Mỗi lần tôi đọc về nó, tôi bị lạc trong đề cập thứ n của kênh :(
OscarRyz

7
hãy nhớ rằng không đảm bảo rằng dấu tách dòng trong tệp không cần thiết giống như dấu tách dòng của hệ thống.
Henrik Paul

138
Bạn có thể vui lòng chèn một thử thích hợp cuối cùng mà đóng độc giả? Ai đó thực sự có thể sử dụng ví dụ này và giới thiệu một lỗi vào mã của mình.
Hans-Peter Störr

6
Mã ở trên có lỗi thêm char dòng mới ở dòng cuối cùng. Nó sẽ là một cái gì đó như sau if (line = reader.readLine ())! = Null) {stringBuilder.append (line); } while (line = reader.readLine ())! = null) {stringBuilder.append (ls); chuỗiBuilder.append (dòng); }
Sâu

27
Java 7 giới thiệu byte[] Files.readAllBytes(file);với những người, những người đề xuất giải pháp Máy quét 'một dòng': Bạn không cần phải đóng nó?
Val

Câu trả lời:


1535

Đọc tất cả văn bản từ một tập tin

Java 11 đã thêm phương thức readString () để đọc các tệp nhỏ dưới dạng String, bảo toàn các đầu cuối dòng:

String content = Files.readString(path, StandardCharsets.US_ASCII);

Đối với các phiên bản giữa Java 7 và 11, đây là một thành ngữ nhỏ gọn, mạnh mẽ, được gói gọn trong một phương thức tiện ích:

static String readFile(String path, Charset encoding) 
  throws IOException 
{
  byte[] encoded = Files.readAllBytes(Paths.get(path));
  return new String(encoded, encoding);
}

Đọc các dòng văn bản từ một tập tin

Java 7 đã thêm một phương thức tiện lợi để đọc tệp dưới dạng các dòng văn bản, được biểu diễn dưới dạng a List<String>. Cách tiếp cận này là "mất mát" vì các dấu phân cách dòng bị tước khỏi cuối mỗi dòng.

List<String> lines = Files.readAllLines(Paths.get(path), encoding);

Java 8 đã thêm Files.lines()phương thức để tạo ra a Stream<String>. Một lần nữa, phương pháp này là mất mát vì dải phân cách bị tước. Nếu một IOExceptiongặp phải trong khi đọc tệp, nó được bọc trong một UncheckedIOException, vì Streamkhông chấp nhận lambdas ném ngoại lệ được kiểm tra.

try (Stream<String> lines = Files.lines(path, encoding)) {
  lines.forEach(System.out::println);
}

Điều này Streamkhông cần một close()cuộc gọi; đây là tài liệu kém về API và tôi nghi ngờ nhiều người thậm chí không nhận thấy Streamclose() phương pháp. Hãy chắc chắn sử dụng một khối ARM như được hiển thị.

Nếu bạn đang làm việc với một nguồn khác ngoài một tệp, bạn có thể sử dụng lines()phương thức BufferedReaderthay thế.

Sử dụng bộ nhớ

Phương thức đầu tiên, bảo toàn ngắt dòng, có thể tạm thời yêu cầu bộ nhớ nhiều lần kích thước của tệp, vì trong một thời gian ngắn, nội dung tệp thô (một mảng byte) và các ký tự được giải mã (mỗi ký tự là 16 bit ngay cả khi được mã hóa như 8 bit trong tệp) nằm trong bộ nhớ cùng một lúc. An toàn nhất là áp dụng cho các tệp mà bạn biết là nhỏ so với bộ nhớ khả dụng.

Phương thức thứ hai, đọc các dòng, thường hiệu quả hơn về bộ nhớ, bởi vì bộ đệm byte đầu vào để giải mã không cần phải chứa toàn bộ tệp. Tuy nhiên, nó vẫn không phù hợp với các tệp rất lớn so với bộ nhớ khả dụng.

Để đọc các tệp lớn, bạn cần một thiết kế khác cho chương trình của mình, một tệp đọc một đoạn văn bản từ một luồng, xử lý nó và sau đó chuyển sang tiếp theo, sử dụng lại khối bộ nhớ có kích thước cố định tương tự. Ở đây, "lớn" phụ thuộc vào thông số kỹ thuật của máy tính. Ngày nay, ngưỡng này có thể là nhiều gigabyte RAM. Phương pháp thứ ba, sử dụng một Stream<String>là một cách để làm điều này, nếu "bản ghi" đầu vào của bạn là các dòng riêng lẻ. (Sử dụng readLine()phương pháp BufferedReaderlà thủ tục tương đương với phương pháp này.)

Mã hóa ký tự

Một điều còn thiếu từ mẫu trong bài viết gốc là mã hóa ký tự. Có một số trường hợp đặc biệt trong đó mặc định nền tảng là những gì bạn muốn, nhưng chúng rất hiếm và bạn có thể biện minh cho sự lựa chọn của mình.

Các StandardCharsetslớp định nghĩa một số hằng số cho mã hóa cần thiết của tất cả các runtimes Java:

String content = readFile("test.txt", StandardCharsets.UTF_8);

Giá trị mặc định nền tảng có sẵn từ các Charsetlớp học riêng của mình:

String content = readFile("test.txt", Charset.defaultCharset());

Lưu ý: Câu trả lời này phần lớn thay thế phiên bản Java 6 của tôi. Tiện ích của Java 7 đơn giản hóa mã một cách an toàn và câu trả lời cũ, sử dụng bộ đệm byte được ánh xạ, ngăn không cho tệp đã đọc bị xóa cho đến khi bộ đệm được ánh xạ được thu gom rác. Bạn có thể xem phiên bản cũ thông qua liên kết "đã chỉnh sửa" trong câu trả lời này.


3
Về mặt kỹ thuật, đó là O (n) trong thời gian và không gian. Về mặt định tính, do yêu cầu bất biến của String, nó khá khó về bộ nhớ; tạm thời có hai bản sao của dữ liệu char trong bộ nhớ, cộng với chỗ cho các byte được mã hóa. Giả sử một số mã hóa một byte, nó sẽ (tạm thời) yêu cầu 5 byte bộ nhớ cho mỗi ký tự trong tệp. Vì câu hỏi yêu cầu cụ thể đối với Chuỗi, đó là những gì tôi hiển thị, nhưng nếu bạn có thể làm việc với CharBuffer được trả về bằng "giải mã", thì yêu cầu bộ nhớ sẽ ít hơn nhiều. Theo thời gian, tôi không nghĩ bạn sẽ tìm thấy bất cứ điều gì nhanh hơn trong các lib Java cốt lõi.
erickson

5
Lỗi đánh máy có thể xảy ra? NIO có một lớp Charset (không phải Char set) được gọi là java.nio.charset.Charset. Đây có phải là những gì CharSet nên có?
Jonathan Wright

31
Lưu ý: sau khi thực hiện một chút mã đó, tôi phát hiện ra rằng bạn không thể xóa tệp một cách đáng tin cậy ngay sau khi đọc nó bằng phương pháp này, có thể không phải là vấn đề trong một số trường hợp, nhưng không phải là của tôi. Nó có liên quan đến vấn đề này không: bug.sun.com/ormsdatabase/view_orms.do?orms_id=4715154 ? Cuối cùng tôi đã đi với đề xuất của Jon Skeet, người không mắc phải lỗi này. Dù sao, tôi chỉ muốn cung cấp thông tin, cho người khác, chỉ trong trường hợp ...
Sébastien Nussbaumer

5
@ Sébastien Nussbaumer: Tôi cũng gặp phải vấn đề này. Thật ngạc nhiên khi lỗi đã được đánh dấu "Sẽ không sửa". Điều này về cơ bản có nghĩa FileChannel#maplà, nói chung, không thể sử dụng.
Joonas Pulakka

4
@ Sébastien Nussbaumer: Lỗi đã bị xóa khỏi Cơ sở dữ liệu lỗi của Oracle / Sun: "Lỗi này không khả dụng." Google lưu trữ trang web tại webcache.googleusercontent.com/search?q=cache:bugs.sun.com/ từ
bobndrew

351

Nếu bạn sẵn sàng sử dụng thư viện bên ngoài, hãy xem Apache Commons IO (200KB JAR). Nó chứa một org.apache.commons.io.FileUtils.readFileToString()phương thức cho phép bạn đọc toàn bộ Filethành mộtString dòng mã.

Thí dụ:

import java.io.*;
import java.nio.charset.*;
import org.apache.commons.io.*;

public String readFile() throws IOException {
    File file = new File("data.txt");
    return FileUtils.readFileToString(file, StandardCharsets.UTF_8);
}

Tôi không tìm thấy phương pháp đó trong URL bạn cung cấp.
OscarRyz

2
Nó thuộc lớp org.apache.commons.io.FileUtils
Cyrille Ka

2
Tôi cũng đang sử dụng FileUtils, nhưng tôi tự hỏi betwwen nào tốt hơn khi sử dụng FileUtils hoặc câu trả lời nio được chấp nhận?
Guillaume

4
@Guillaume: Câu hỏi lớn nhất là liệu bạn có cảm thấy thoải mái khi có sự phụ thuộc vào thư viện của bên thứ 3 hay không. Nếu bạn có Commons IO hoặc Guava trong dự án của mình, thì hãy sử dụng nó (chỉ để đơn giản mã; nếu không, có thể sẽ không có sự khác biệt đáng chú ý).
Jonik

183

Một giải pháp rất nạc dựa trên Scanner:

Scanner scanner = new Scanner( new File("poem.txt") );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

Hoặc, nếu bạn muốn đặt bộ ký tự:

Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

Hoặc, với khối tài nguyên dùng thử , sẽ gọi scanner.close()cho bạn:

try (Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" )) {
    String text = scanner.useDelimiter("\\A").next();
}

Hãy nhớ rằng các nhà Scannerxây dựng có thể ném một IOException. Và đừng quên nhập khẩu java.iojava.util .

Nguồn: blog của Pat Niemeyer


4
\\ A hoạt động vì không có "phần đầu khác của tệp", vì vậy trên thực tế bạn đang đọc mã thông báo cuối cùng ... cũng là phần đầu tiên. Chưa bao giờ thử với \\ Z. Cũng lưu ý rằng bạn có thể đọc bất cứ thứ gì có thể đọc được, như Tệp, InputStream, kênh ... Đôi khi tôi sử dụng mã này để đọc từ cửa sổ hiển thị của nhật thực, khi tôi không chắc là tôi đang đọc tệp này hay tệp khác .. .yes, classpath làm tôi bối rối.
Pablo Grisafi

1
Với tư cách là người đăng, tôi có thể nói rằng tôi thực sự không biết liệu khi nào và khi tập tin được đóng đúng cách ... Tôi không bao giờ viết cái này trong mã sản xuất, tôi chỉ sử dụng nó để kiểm tra hoặc gỡ lỗi.
Pablo Grisafi

2
Nó có giới hạn 1024 ký tự tôi nghĩ
Whimusical

20
Máy quét thực hiện Closable (nó gọi gần nguồn) - vì vậy, trong khi thanh lịch, nó không thực sự là một lớp lót. Kích thước mặc định của bộ đệm là 1024, nhưng Máy quét sẽ tăng kích thước khi cần thiết (xem Máy quét # makeSpace ())
Earcam

8
Điều này không thành công cho các tập tin trống với a java.util.NoSuchElementException.
SpaceTrucker

117
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;

String content = new String(Files.readAllBytes(Paths.get("readMe.txt")), StandardCharsets.UTF_8);

Kể từ java 7, bạn có thể làm theo cách này.


Điều này nên được chấp nhận như câu trả lời - dòng đơn, không có libs bên ngoài.
Cherry

Điều này đã thêm một ký tự dòng mới vào cuối, ngay cả khi nó không có trong tệp
Stefan Haberl

79

Nếu bạn đang tìm kiếm một giải pháp thay thế không liên quan đến thư viện của bên thứ ba (ví dụ: Commons I / O ), bạn có thể sử dụng lớp Máy quét :

private String readFile(String pathname) throws IOException {

    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int)file.length());        

    try (Scanner scanner = new Scanner(file)) {
        while(scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine() + System.lineSeparator());
        }
        return fileContents.toString();
    }
}

2
Tôi nghĩ rằng đây là cách tốt nhất. Hãy xem java.sun.com/docs/books/tutorial/essential/io/scanning.html
Tarski

3
Hàm tạo của Trình quét chấp nhận Chuỗi không coi chuỗi là tên của tệp cần đọc mà là văn bản cần quét. Tôi mắc lỗi đó mọi lúc. : - /
Alan Moore

@Alan, bắt tốt. Tôi đã chỉnh sửa câu trả lời của Don một chút để khắc phục điều đó (tôi hy vọng).
Jonik

3
fileContents.append (Scanner.nextLine ()). append (lineSpayator);
ban-geengineering

1
Thay đổi câu lệnh khởi tạo thành Scanner scanner = new Scanner((Readable) new BufferedReader(new FileReader(file)));. Nếu không, bạn chỉ có thể chụp một phần của tập tin.
Wei Yang

71

Quả ổi có một phương pháp tương tự như phương pháp từ Commons IOUtils mà Willi aus Rohr đã đề cập:

import com.google.common.base.Charsets;
import com.google.common.io.Files;

// ...

String text = Files.toString(new File(path), Charsets.UTF_8);

EDIT bởi PiggyPiglet
Files#toString không được chấp nhận và do xóa Octobor 2019. Thay vào đó hãy sử dụng Files.asCharSource(new File(path), StandardCharsets.UTF_8).read();

EDIT của Oscar Reyes

Đây là mã cơ bản (đơn giản hóa) trên thư viện được trích dẫn:

InputStream in = new FileInputStream(file);
byte[] b  = new byte[file.length()];
int len = b.length;
int total = 0;

while (total < len) {
  int result = in.read(b, total, len - total);
  if (result == -1) {
    break;
  }
  total += result;
}

return new String( b , Charsets.UTF_8 );

Chỉnh sửa (bởi Jonik): Phần trên không khớp với mã nguồn của các phiên bản Guava gần đây. Đối với nguồn hiện tại, hãy xem các lớp Tệp , CharStreams , ByteSourceCharSource trong gói com.google.common.io .


Mã này đã truyền từ dài đến int có thể bật lên một số hành vi điên rồ với các tệp lớn. Có thêm không gian và bạn đóng đầu vào ở đâu?
Mohamed Taher Alrefaie

@MTA: Con suối được đóng cửa, chú ý sử dụng Closertrong CharSource . Mã trong câu trả lời không phải là nguồn ổi hiện tại, thực tế.
Jonik

54
import java.nio.file.Files;

.......

 String readFile(String filename) {
            File f = new File(filename);
            try {
                byte[] bytes = Files.readAllBytes(f.toPath());
                return new String(bytes,"UTF-8");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
    }

6
Hoặc thậm chí đơn giản hơn:new String(Files.readAllBytes(FileSystems.getDefault().getPath( filename)));

12
hoặc new String(Files.readAllBytes(Paths.get(filename)));:-)
assafmo

1
Vâng chơi, và để tiết kiệm các chàng bên cạnh các Googling, Pathsdường như 1.7+ như là FileSystems. (Đăng nó!)
ruffin

4
Thật xấu hổ vì câu trả lời này không có nhiều phiếu hơn. Tôi đang tìm cách nhanh nhất và đơn giản nhất để đưa tệp văn bản vào Chuỗi. Đây là nó và nếu tôi không cuộn xuống và xuống, tôi sẽ bỏ lỡ nó. OP nên xem xét chấp nhận câu trả lời này để đưa nó lên đầu.
Thorn

@Thorn Câu trả lời này có xử lý lỗi khủng khiếp. Đừng sử dụng phương pháp này trong mã sản xuất, hoặc tốt hơn: không bao giờ.
xehpuk

51

Nếu bạn cần xử lý chuỗi (xử lý song song), Java 8 có API Stream tuyệt vời.

String result = Files.lines(Paths.get("file.txt"))
                    .parallel() // for parallel processing 
                    .map(String::trim) // to change line   
                    .filter(line -> line.length() > 2) // to filter some lines by a predicate                        
                    .collect(Collectors.joining()); // to join lines

Các ví dụ khác có sẵn trong các mẫu JDK sample/lambda/BulkDataOperationscó thể được tải xuống từ trang tải xuống Oracle Java SE 8

Một ví dụ khác

String out = String.join("\n", Files.readAllLines(Paths.get("file.txt")));

Liệu .pool () có xảy ra sau khi bạn đọc các dòng hoặc trước đó không?
Istvan

Công việc thực sự bắt đầu kể từ khi hoạt động thu thập thiết bị đầu cuối (...) được gọi. Stream là dòng dân cư lười biếng theo dòng. Không cần phải đọc toàn bộ tệp trong bộ nhớ trước khi xử lý (ví dụ: lọc và ánh xạ).
Andrei N

cắt trước khi chọn dòng không trống?
Thorbjørn Ravn Andersen

50

Mã đó sẽ bình thường hóa ngắt dòng, có thể hoặc không thể là những gì bạn thực sự muốn làm.

Đây là một giải pháp thay thế không làm điều đó và (IMO) đơn giản hơn để hiểu hơn mã NIO (mặc dù nó vẫn sử dụng java.nio.charset.Charset):

public static String readFile(String file, String csName)
            throws IOException {
    Charset cs = Charset.forName(csName);
    return readFile(file, cs);
}

public static String readFile(String file, Charset cs)
            throws IOException {
    // No real need to close the BufferedReader/InputStreamReader
    // as they're only wrapping the stream
    FileInputStream stream = new FileInputStream(file);
    try {
        Reader reader = new BufferedReader(new InputStreamReader(stream, cs));
        StringBuilder builder = new StringBuilder();
        char[] buffer = new char[8192];
        int read;
        while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
            builder.append(buffer, 0, read);
        }
        return builder.toString();
    } finally {
        // Potential issue here: if this throws an IOException,
        // it will mask any others. Normally I'd use a utility
        // method which would log exceptions and swallow them
        stream.close();
    }        
}

1
Hãy tha thứ cho tôi vì đã làm sống lại một bình luận cũ này, nhưng ý bạn là truyền vào một đối tượng Chuỗi có tên là "tệp", hay đó phải là một đối tượng Tệp thay thế?
Bryan Larson

28

Tập hợp tất cả các cách có thể để đọc Tệp dưới dạng Chuỗi từ Đĩa hoặc Mạng.

  • Quả ổi: Google sử dụng các lớp họcResources ,Files

    static Charset charset = com.google.common.base.Charsets.UTF_8;
    public static String guava_ServerFile( URL url ) throws IOException {
        return Resources.toString( url, charset );
    }
    public static String guava_DiskFile( File file ) throws IOException {
        return Files.toString( file, charset );
    }

  • APACHE - CAM KẾT IO sử dụng các lớp IOUtils, FileUtils

    static Charset encoding = org.apache.commons.io.Charsets.UTF_8;
    public static String commons_IOUtils( URL url ) throws IOException {
        java.io.InputStream in = url.openStream();
        try {
            return IOUtils.toString( in, encoding );
        } finally {
            IOUtils.closeQuietly(in);
        }
    }
    public static String commons_FileUtils( File file ) throws IOException {
        return FileUtils.readFileToString( file, encoding );
        /*List<String> lines = FileUtils.readLines( fileName, encoding );
        return lines.stream().collect( Collectors.joining("\n") );*/
    }

  • Bộ đệm Java 8 sử dụng API Stream

    public static String streamURL_Buffer( URL url ) throws IOException {
        java.io.InputStream source = url.openStream();
        BufferedReader reader = new BufferedReader( new InputStreamReader( source ) );
        //List<String> lines = reader.lines().collect( Collectors.toList() );
        return reader.lines().collect( Collectors.joining( System.lineSeparator() ) );
    }
    public static String streamFile_Buffer( File file ) throws IOException {
        BufferedReader reader = new BufferedReader( new FileReader( file ) );
        return reader.lines().collect(Collectors.joining(System.lineSeparator()));
    }

  • Lớp máy quét với regex \A . phù hợp với sự bắt đầu của đầu vào.

    static String charsetName = java.nio.charset.StandardCharsets.UTF_8.toString();
    public static String streamURL_Scanner( URL url ) throws IOException {
        java.io.InputStream source = url.openStream();
        Scanner scanner = new Scanner(source, charsetName).useDelimiter("\\A");
        return scanner.hasNext() ? scanner.next() : "";
    }
    public static String streamFile_Scanner( File file ) throws IOException {
        Scanner scanner = new Scanner(file, charsetName).useDelimiter("\\A");
        return scanner.hasNext() ? scanner.next() : "";
    }

  • Java 7 (java.nio.file.Files.readAllBytes )

    public static String getDiskFile_Java7( File file ) throws IOException {
        byte[] readAllBytes = java.nio.file.Files.readAllBytes(Paths.get( file.getAbsolutePath() ));
        return new String( readAllBytes );
    }

  • BufferedReadersử dụng InputStreamReader.

    public static String getDiskFile_Lines( File file ) throws IOException {
        StringBuffer text = new StringBuffer();
        FileInputStream fileStream = new FileInputStream( file );
        BufferedReader br = new BufferedReader( new InputStreamReader( fileStream ) );
        for ( String line; (line = br.readLine()) != null; )
            text.append( line + System.lineSeparator() );
        return text.toString();
    }

Ví dụ với phương thức chính để truy cập các phương thức trên.

public static void main(String[] args) throws IOException {
    String fileName = "E:/parametarisation.csv";
    File file = new File( fileName );

    String fileStream = commons_FileUtils( file );
            // guava_DiskFile( file );
            // streamFile_Buffer( file );
            // getDiskFile_Java7( file );
            // getDiskFile_Lines( file );
    System.out.println( " File Over Disk : \n"+ fileStream );


    try {
        String src = "https://code.jquery.com/jquery-3.2.1.js";
        URL url = new URL( src );

        String urlStream = commons_IOUtils( url );
                // guava_ServerFile( url );
                // streamURL_Scanner( url );
                // streamURL_Buffer( url );
        System.out.println( " File Over Network : \n"+ urlStream );
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}

@xem


26

Nếu đó là một tệp văn bản tại sao không sử dụng apache commons-io ?

Nó có phương pháp sau

public static String readFileToString(File file) throws IOException

Nếu bạn muốn các dòng như một danh sách sử dụng

public static List<String> readLines(File file) throws IOException

25

Kể từ JDK 11:

String file = ...
Path path = Paths.get(file);
String content = Files.readString(path);
// Or readString(path, someCharset), if you need a Charset different from UTF-8

Tại sao, oh tại sao, giới thiệu các phương pháp mới dựa trên bảng mã mặc định trong năm 2018?
mryan

2
@mryan phương pháp này không dựa vào bộ ký tự hệ thống mặc định. Nó mặc định là UTF-8, điều đó là tốt.
leventov

@leventov bạn nói đúng! Files.read ALLLines cũng vậy! điều đó làm cho API tệp không nhất quán với các phương thức cũ hơn nhưng nó tốt hơn :)
mryan

17

Để đọc một tệp dưới dạng nhị phân và chuyển đổi ở cuối

public static String readFileAsString(String filePath) throws IOException {
    DataInputStream dis = new DataInputStream(new FileInputStream(filePath));
    try {
        long len = new File(filePath).length();
        if (len > Integer.MAX_VALUE) throw new IOException("File "+filePath+" too large, was "+len+" bytes.");
        byte[] bytes = new byte[(int) len];
        dis.readFully(bytes);
        return new String(bytes, "UTF-8");
    } finally {
        dis.close();
    }
}

16

Với Java 7, đây là tùy chọn ưa thích của tôi để đọc tệp UTF-8:

String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");

Kể từ Java 7, JDK có java.nio.fileAPI mới , cung cấp nhiều phím tắt, do đó không phải lúc nào các thư viện của bên thứ 3 cũng được yêu cầu cho các hoạt động tệp đơn giản.


15

Java cố gắng cực kỳ chung chung và linh hoạt trong tất cả những gì nó làm. Kết quả là, một thứ tương đối đơn giản trong ngôn ngữ kịch bản (mã của bạn sẽ được thay thế bằng " open(file).read()" trong python) thì phức tạp hơn nhiều. Dường như không có cách nào ngắn hơn để làm điều đó, ngoại trừ sử dụng một thư viện bên ngoài (như Willi aus Rohr đã đề cập). Lựa chọn của bạn:

  • Sử dụng một thư viện bên ngoài.
  • Sao chép mã này vào tất cả các dự án của bạn.
  • Tạo thư viện nhỏ của riêng bạn chứa các chức năng bạn thường sử dụng.

Đặt cược tốt nhất của bạn có lẽ là thứ 2, vì nó có ít phụ thuộc nhất.


4
Yeap. Nó làm cho ngôn ngữ cấp độ "cao" mang một ý nghĩa khác. Java ở mức cao so với C nhưng thấp so với Python hoặc Ruby
OscarRyz

3
Đồng ý rằng Java dài về trừu tượng cấp cao nhưng ngắn về các phương thức tiện lợi
Dónal

3
Đúng, Java có một số cách điên rồ để xử lý các Tệp và nhiều trong số chúng có vẻ phức tạp. Nhưng điều này khá gần với những gì chúng ta có trong các ngôn ngữ cấp cao hơn:byte[] bytes = Files.readAllBytes(someFile.toPath());
Thorn

11

Sử dụng JDK 8 trở lên:

không sử dụng thư viện bên ngoài

Bạn có thể tạo một đối tượng String mới từ nội dung tệp (Sử dụng các lớp từ java.nio.filegói):

public String readStringFromFile(String filePath) throws IOException {
    String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
    return fileContent;
}

Bản sao câu trả lời của Moritz Petersen đã viết: String content = new String (Files.read ALLBytes (Paths.get (tên tệp)), "UTF-8");
Jean-Barshe Blanchard

8

Có một biến thể trên cùng một chủ đề sử dụng vòng lặp for, thay vì vòng lặp while, để giới hạn phạm vi của biến dòng. Cho dù đó là "tốt hơn" là một vấn đề của sở thích cá nhân.

for(String line = reader.readLine(); line != null; line = reader.readLine()) {
    stringBuilder.append(line);
    stringBuilder.append(ls);
}

3
Điều này sẽ thay đổi các dòng mới thành lựa chọn dòng mới mặc định. Điều này có thể là mong muốn, hoặc ngoài ý muốn.
Peter Lawrey

Quay lại chỉnh sửa cho câu trả lời này vì vấn đề là thu hẹp phạm vi của linebiến. Bản chỉnh sửa đã khai báo hai lần, đó sẽ là một lỗi biên dịch.
Dan Dyer

7

Nếu bạn không có quyền truy cập vào Fileslớp, bạn có thể sử dụng giải pháp gốc.

static String readFile(File file, String charset)
        throws IOException
{
    FileInputStream fileInputStream = new FileInputStream(file);
    byte[] buffer = new byte[fileInputStream.available()];
    int length = fileInputStream.read(buffer);
    fileInputStream.close();
    return new String(buffer, 0, length, charset);
}

ví dụ ký tự để gọi?
Thufir

4

Một giải pháp linh hoạt sử dụng IOUtils từ Apache commons-io kết hợp với StringWriter :

Reader input = new FileReader();
StringWriter output = new StringWriter();
try {
  IOUtils.copy(input, output);
} finally {
  input.close();
}
String fileContents = output.toString();

Nó hoạt động với bất kỳ trình đọc hoặc luồng đầu vào nào (không chỉ với các tệp), ví dụ như khi đọc từ một URL.


3

Lưu ý khi sử dụng fileInputStream.available()số nguyên được trả về không phải biểu thị kích thước tệp thực tế mà thay vào đó là số lượng byte được đoán mà hệ thống có thể đọc được từ luồng mà không chặn IO. Một cách an toàn và đơn giản có thể trông như thế này

public String readStringFromInputStream(FileInputStream fileInputStream) {
    StringBuffer stringBuffer = new StringBuffer();
    try {
        byte[] buffer;
        while (fileInputStream.available() > 0) {
            buffer = new byte[fileInputStream.available()];
            fileInputStream.read(buffer);
            stringBuffer.append(new String(buffer, "ISO-8859-1"));
        }
    } catch (FileNotFoundException e) {
    } catch (IOException e) { }
    return stringBuffer.toString();
}

Cần xem xét rằng cách tiếp cận này không phù hợp với mã hóa ký tự nhiều byte như UTF-8.


1
Mã này có thể cho kết quả không thể đoán trước. Theo tài liệu của available()phương thức, không có gì đảm bảo rằng phần cuối của tệp đạt được trong trường hợp phương thức trả về 0. Trong trường hợp đó bạn có thể kết thúc bằng một tệp không hoàn chỉnh. Tệ hơn nữa, số byte thực sự đọc có thể nhỏ hơn giá trị được trả về available(), trong trường hợp đó bạn nhận được đầu ra bị hỏng.
wau 15/03/13

3

Cái này sử dụng phương thức RandomAccessFile.readFully, nó dường như có sẵn từ JDK 1.0!

public static String readFileContent(String filename, Charset charset) throws IOException {
    RandomAccessFile raf = null;
    try {
        raf = new RandomAccessFile(filename, "r");
        byte[] buffer = new byte[(int)raf.length()];
        raf.readFully(buffer);
        return new String(buffer, charset);
    } finally {
        closeStream(raf);
    }
} 


private static void closeStream(Closeable c) {
    if (c != null) {
        try {
            c.close();
        } catch (IOException ex) {
            // do nothing
        }
    }
}

3

Bạn có thể thử lớp Scanner và File, một vài giải pháp dòng

 try
{
  String content = new Scanner(new File("file.txt")).useDelimiter("\\Z").next();
  System.out.println(content);
}
catch(FileNotFoundException e)
{
  System.out.println("not found!");
}

3

Người dùng java.nio.Filesđể đọc tất cả các dòng của tập tin.

public String readFile() throws IOException {
        File fileToRead = new File("file path");
        List<String> fileLines = Files.readAllLines(fileToRead.toPath());
        return StringUtils.join(fileLines, StringUtils.EMPTY);
}

3
public static String slurp (final File file)
throws IOException {
    StringBuilder result = new StringBuilder();

    BufferedReader reader = new BufferedReader(new FileReader(file));

    try {
        char[] buf = new char[1024];

        int r = 0;

        while ((r = reader.read(buf)) != -1) {
            result.append(buf, 0, r);
        }
    }
    finally {
        reader.close();
    }

    return result.toString();
}

Tôi nghĩ rằng điều này có sự bất tiện os sử dụng mã hóa mặc định nền tảng. +1 dù sao :)
OscarRyz

7
Tôi dường như với tôi rằng khối cuối cùng không biết các biến được định nghĩa trong khối thử. javac 1.6.0_21 ném lỗi cannot find symbol.
ceving

Bạn thậm chí đã thử mã của riêng bạn? Bạn đã xác định trình đọc trong khối try / Catch, vì vậy cuối cùng nó sẽ không thể truy cập được.
mauron85

2

Tôi chưa thể bình luận các mục khác, vì vậy tôi sẽ để nó ở đây.

Một trong những câu trả lời hay nhất tại đây ( https://stackoverflow.com/a/326448/1521167 ):

private String readFile(String pathname) throws IOException {

File file = new File(pathname);
StringBuilder fileContents = new StringBuilder((int)file.length());
Scanner scanner = new Scanner(file);
String lineSeparator = System.getProperty("line.separator");

try {
    while(scanner.hasNextLine()) {        
        fileContents.append(scanner.nextLine() + lineSeparator);
    }
    return fileContents.toString();
} finally {
    scanner.close();
}
}

vẫn còn một lỗ hổng. Nó luôn đặt dòng char mới ở cuối chuỗi, điều này có thể gây ra một số lỗi kỳ lạ. Đề nghị của tôi là thay đổi nó thành:

    private String readFile(String pathname) throws IOException {
    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int) file.length());
    Scanner scanner = new Scanner(new BufferedReader(new FileReader(file)));
    String lineSeparator = System.getProperty("line.separator");

    try {
        if (scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine());
        }
        while (scanner.hasNextLine()) {
            fileContents.append(lineSeparator + scanner.nextLine());
        }
        return fileContents.toString();
    } finally {
        scanner.close();
    }
}

Trong trường hợp đầu tiên, bạn có thể thêm một dòng mới vào cuối. trong trường hợp thứ hai bạn có thể bỏ qua một. Vậy là cả hai đều sai như nhau. Xem bài viết này
Patrick Parker

2

Sau Ctrl + F'ing sau Máy quét, tôi nghĩ rằng giải pháp Máy quét cũng nên được liệt kê. Trong thời trang dễ đọc nhất, nó diễn ra như sau:

public String fileToString(File file, Charset charset) {
  Scanner fileReader = new Scanner(file, charset);
  fileReader.useDelimiter("\\Z"); // \Z means EOF.
  String out = fileReader.next();
  fileReader.close();
  return out;
}

Nếu bạn sử dụng Java 7 hoặc mới hơn (và bạn thực sự nên) hãy cân nhắc sử dụng tài nguyên dùng thử để làm cho mã dễ đọc hơn. Không còn những thứ gần gũi rải rác mọi thứ. Nhưng đó chủ yếu là một methinks lựa chọn phong cách.

Tôi đang đăng bài này chủ yếu để hoàn thành, vì nếu bạn cần làm điều này nhiều, nên có những thứ trong java.nio.file.Files nên làm công việc tốt hơn.

Đề xuất của tôi sẽ là sử dụng Files # readAllBytes (Đường dẫn) để lấy tất cả các byte và đưa nó vào Chuỗi mới (byte [] Charset) để lấy Chuỗi ra khỏi chuỗi mà bạn có thể tin tưởng. Bộ ký tự sẽ có ý nghĩa với bạn trong suốt cuộc đời của bạn, vì vậy hãy cẩn thận với những thứ này ngay bây giờ.

Những người khác đã đưa ra mã và công cụ, và tôi không muốn đánh cắp vinh quang của họ. ;)


2

Sử dụng thư viện này , nó là một dòng:

String data = IO.from(new File("data.txt")).toString();

1
nếu các dòng bên trong thư viện không được tính.
Ari

2

Ngoài ra nếu tập tin của bạn nằm trong một cái bình, bạn cũng có thể sử dụng cái này:

public String fromFileInJar(String path) {
    try ( Scanner scanner 
            = new Scanner(getClass().getResourceAsStream(path))) {
        return scanner.useDelimiter("\\A").next();
    }
}

Đường dẫn nên bắt đầu bằng / ví dụ nếu jar của bạn là

my.jar/com/some/thing/a.txt

Sau đó, bạn muốn gọi nó như thế này:

String myTxt = fromFileInJar("/com/com/thing/a.txt");

2

Trong một dòng (Java 8), giả sử bạn có Trình đọc:

String sMessage = String.join("\n", reader.lines().collect(Collectors.toList()));

2

Dựa trên câu trả lời của @ erickson, bạn có thể sử dụng:

public String readAll(String fileName) throws IOException {
    List<String> lines = Files.readAllLines(new File(fileName).toPath());
    return String.join("\n", lines.toArray(new String[lines.size()]));
}
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.