Sử dụng để đọc tệp văn bản tài nguyên sang Chuỗi (Java) [đã đóng]


215

Có tiện ích nào giúp đọc tệp văn bản trong tài nguyên thành Chuỗi không. Tôi cho rằng đây là một yêu cầu phổ biến, nhưng tôi không thể tìm thấy bất kỳ tiện ích nào sau khi Google.


1
vui lòng làm rõ ý của bạn bằng "tệp văn bản tài nguyên" so với "tệp văn bản trong tài nguyên" - không dễ để hiểu những gì bạn đang cố gắng đạt được.
Mat

Đó chỉ là một tệp văn bản dưới đường dẫn lớp như "classpath *: mytext / text.txt"
Loc Phan

Câu trả lời:


301

Vâng, ổi cung cấp điều này trong Resourceslớp. Ví dụ:

URL url = Resources.getResource("foo.txt");
String text = Resources.toString(url, StandardCharsets.UTF_8);

21
@JonSkeet Điều này thật tuyệt, tuy nhiên, đối với các ứng dụng web, nó có thể không phải là giải pháp tốt nhất, việc triển khai getResourceđang sử dụng Resource.class.getClassLoadernhưng trong các ứng dụng web, đây có thể không phải là trình tải lớp "của bạn", vì vậy nên sử dụng (ví dụ như trong [1]) Thread.currentThread().getContextClassLoader().getResourceAsStreamthay vào đó (tham khảo [1]: stackoverflow.com/questions/676250/ từ )
Eran Medan

2
@EranMedan: Có, nếu bạn muốn trình tải lớp ngữ cảnh bạn muốn sử dụng nó một cách rõ ràng.
Jon Skeet

6
Trong trường hợp đặc biệt khi tài nguyên nằm cạnh lớp của bạn, bạn có thể làm điều Resources.toString(MyClass.getResource("foo.txt"), Charsets.UTF_8)đó đảm bảo việc sử dụng trình nạp lớp chính xác.
Bogdan Calmac

2
com.google.common.io.Resourcesđược đánh dấu là không ổn định theo SonarQube
Ghilteras

1
guavađã thay đổi việc thực hiện. Đối với ổi 23 việc thực hiện như sau. ClassLoader loader = MoreObjects.firstNonNull( Thread.currentThread().getContextClassLoader(), Resources.class.getClassLoader());
xxy

170

Bạn có thể sử dụng thủ thuật Stool Scanner cũ để thực hiện điều đó mà không cần phụ thuộc thêm như ổi:

String text = new Scanner(AppropriateClass.class.getResourceAsStream("foo.txt"), "UTF-8").useDelimiter("\\A").next();

Các bạn, đừng sử dụng công cụ của bên thứ 3 trừ khi bạn thực sự cần điều đó. Có rất nhiều chức năng trong JDK.


41
Tránh bên thứ 3 là một nguyên tắc hợp lý. Thật không may, thư viện cốt lõi có vẻ dị ứng với việc mô hình hóa các trường hợp sử dụng thực tế. Hãy xem các tệp của Java 7 và cho tôi biết tại sao đọc mọi thứ từ tài nguyên đường dẫn không được bao gồm ở đó? Hoặc ít nhất là sử dụng một 'hệ thống tập tin' được tiêu chuẩn hóa.
Dilum Ranatunga

3
Là nó - hoặc là không - cần thiết để đóng luồng là tốt? Quả ổi bên trong đóng cửa suối.
virgo47

Làm việc rất đẹp cho tôi quá! Tôi cũng đồng ý về điều bên thứ 3: Trong nhiều câu trả lời, câu trả lời mặc định dường như luôn là sử dụng một số thư viện của bên thứ ba - có thể là từ Apache hoặc người khác.
Terje Dahl

1
thay đổi CartApplication.class.getResourceAsStreamđể CartApplication.class.getClassLoader().getResourceAsStreamtải tài nguyên trong jar hiện tại..như srm / test / resource
Chris DaMour 27/2/2016

5
Trong khi tôi đã sử dụng điều này, tôi hoàn toàn không đồng ý về việc tránh các gói của bên thứ 3. Thực tế là trong Java, cách duy nhất để dễ dàng đọc một tệp thành chuỗi là với thủ thuật quét là khá buồn. Thay thế cho việc sử dụng lib của bên thứ 3 là mọi người sẽ chỉ tạo các trình bao bọc của riêng họ. Quả ổi cho IO xuống tay thắng nếu bạn có nhiều nhu cầu cho loại hoạt động này. Trường hợp tôi đồng ý là bạn không nên nhập gói bên thứ 3 nếu bạn chỉ có một vị trí trong mã của mình, nơi bạn muốn làm điều này. Đó sẽ là imo quá mức.
Kenny Cason

90

Đối với java 7:

new String(Files.readAllBytes(Paths.get(getClass().getResource("foo.txt").toURI())));

3
Giải thích tại sao điều này hoạt động, tại sao nó tốt hơn các lựa chọn thay thế khác và bất kỳ cân nhắc về hiệu suất / mã hóa nào cần thiết.
nanofarad

5
Đây là nio 2 trong java 1.7. Đó là thai nhi bản địa của java. Để mã hóa, hãy sử dụng Chuỗi mới (byte, StandardCharsets.UTF_8)
Kovalsky Dmitryi

5
trong trường hợp của tôi, tôi cần getClass().getClassLoader()nhưng giải pháp tuyệt vời!
Emmanuel Touzery

3
Điều này sẽ không hoạt động, một khi ứng dụng được đóng gói vào một cái lọ.
Daniel Bo

65

Tinh khiết và đơn giản, thân thiện, giải pháp Java 8+

Phương pháp đơn giản dưới đây sẽ hoạt động tốt nếu bạn đang sử dụng Java 8 trở lên:

/**
 * Reads given resource file as a string.
 *
 * @param fileName path to the resource file
 * @return the file's contents
 * @throws IOException if read fails for any reason
 */
static String getResourceFileAsString(String fileName) throws IOException {
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    try (InputStream is = classLoader.getResourceAsStream(fileName)) {
        if (is == null) return null;
        try (InputStreamReader isr = new InputStreamReader(is);
             BufferedReader reader = new BufferedReader(isr)) {
            return reader.lines().collect(Collectors.joining(System.lineSeparator()));
        }
    }
}

Và nó cũng hoạt động với các tài nguyên trong các tệp jar .

Về mã hóa văn bản: InputStreamReadersẽ sử dụng bộ ký tự hệ thống mặc định trong trường hợp bạn không chỉ định. Bạn có thể muốn tự chỉ định nó để tránh các vấn đề giải mã, như thế này:

new InputStreamReader(isr, StandardCharsets.UTF_8);

Tránh phụ thuộc không cần thiết

Luôn luôn thích không phụ thuộc vào các thư viện lớn, chất béo. Trừ khi bạn đang sử dụng Guava hoặc Apache Commons IO cho các tác vụ khác, việc thêm các thư viện đó vào dự án của bạn chỉ để có thể đọc từ tệp có vẻ hơi quá.

Phương pháp "đơn giản"? Bạn phải đùa tôi

Tôi hiểu rằng Java thuần túy không làm tốt công việc khi thực hiện các nhiệm vụ đơn giản như thế này. Chẳng hạn, đây là cách chúng ta đọc từ một tệp trong Node.js:

const fs = require("fs");
const contents = fs.readFileSync("some-file.txt", "utf-8");

Đơn giản và dễ đọc (mặc dù mọi người vẫn thích dựa vào nhiều phụ thuộc dù sao, chủ yếu là do sự thiếu hiểu biết). Hoặc trong Python:

with open('some-file.txt', 'r') as f:
    content = f.read()

Thật đáng buồn, nhưng nó vẫn đơn giản đối với các tiêu chuẩn của Java và tất cả những gì bạn phải làm là sao chép phương thức trên vào dự án của bạn và sử dụng nó. Tôi thậm chí không yêu cầu bạn hiểu những gì đang diễn ra ở đó, bởi vì nó thực sự không quan trọng với bất cứ ai. Nó chỉ hoạt động, giai đoạn :-)


4
@zakmck hãy cố gắng giữ bình luận của bạn mang tính xây dựng. Khi bạn lớn lên như một nhà phát triển trưởng thành, bạn học được rằng đôi khi bạn muốn "phát minh lại bánh xe". Ví dụ, bạn có thể cần giữ nhị phân của mình dưới kích thước ngưỡng nào đó. Các thư viện thường làm cho kích thước ứng dụng của bạn tăng lên theo các đơn đặt hàng lớn. Người ta có thể tranh luận ngược lại với những gì bạn nói: "Không cần viết mã. Vâng, hãy nhập thư viện mỗi lần". Bạn có thực sự muốn nhập một thư viện chỉ để tiết kiệm cho bạn 3 dòng mã? Tôi cá rằng việc thêm thư viện sẽ tăng LỘC của bạn nhiều hơn thế. Chìa khóa là sự cân bằng.
Lucio Paiva

3
Chà, không phải ai cũng chạy thứ trên đám mây. Có các hệ thống nhúng ở khắp mọi nơi chạy Java, ví dụ. Tôi chỉ không thấy quan điểm của bạn khi chỉ trích các câu trả lời cung cấp các cách tiếp cận hoàn toàn hợp lệ, với điều kiện bạn đề cập đến bản thân rằng bạn sẽ chấp nhận đề xuất sử dụng JDK trực tiếp trong mã của riêng bạn. Dù sao, chúng ta hãy cố gắng giữ bình luận nghiêm túc để giúp cải thiện câu trả lời, không thảo luận về ý kiến.
Lucio Paiva

1
Giải pháp tốt chỉ dành cho JDK. Tôi sẽ chỉ làm tăng thêm kiểm tra nếu InputStreambiến isnullhay không.
Scrutari

2
Đẹp. Tôi đã sử dụng này. Bạn có thể xem xét đóng luồng / độc giả quá.
lúm đồng tiền

1
@RobertBain Tôi đã chỉnh sửa câu trả lời để thêm thông tin về cảnh báo bộ ký tự. Hãy cho tôi biết nếu bạn phát hiện ra sự cố xảy ra với trình nạp lớp trong AWS để tôi cũng có thể thêm nó vào câu trả lời. Cảm ơn!
Lucio Paiva

57

Quả ổi có phương thức "toString" để đọc tệp thành Chuỗi:

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

String content = Files.toString(new File("/home/x1/text.log"), Charsets.UTF_8);

Phương thức này không yêu cầu tệp phải nằm trong đường dẫn lớp (như trong câu trả lời trước của Jon Skeet ).


2
Hoặc nếu đó là một luồng đầu vào, ổi cũng có một cách hay cho việc nàyString stringFromStream = CharStreams.toString(new InputStreamReader(resourceAsStream, "UTF-8"));
Eran Medan

1
Điều này không được chấp nhận trong Guava 24.1
Andrey

47

yegor256 đã tìm thấy một giải pháp hay bằng cách sử dụng Apache Commons IO :

import org.apache.commons.io.IOUtils;

String text = IOUtils.toString(this.getClass().getResourceAsStream("foo.xml"),
                               "UTF-8");

Tôi thích "" hơn trong trường hợp này là không có sẵn
dùng833970

11
Cũng như nhỏ gọn, nhưng với việc đóng đúng luồng đầu vào : IOUtils.toString(this.getClass().getResource("foo.xml"), "UTF-8").
Bogdan Calmac

1
Nếu giải pháp này không hiệu quả, hãy thử thêm getClassLoader()vào chuỗi phương thức: String text = IOUtils.toString( getClass().getClassLoader().getResourceAsStream("foo.xml"), StandardCharsets.UTF_8);
Abdull

39

apache-commons-io có một tên tiện ích FileUtils:

URL url = Resources.getResource("myFile.txt");
File myFile = new File(url.toURI());

String content = FileUtils.readFileToString(myFile, "UTF-8");  // or any other encoding

1
Tại sao người ta phải chỉ định mã hóa, tôi không nhận được điều đó. Nếu tôi đọc tệp, tôi chỉ muốn những gì trong đó, nó sẽ tìm ra mã hóa giống như trình soạn thảo của tôi. Khi tôi mở trong Notepad hoặc ++, tôi không nói cho nó biết nên sử dụng mã hóa nào. Tôi đang sử dụng phương thức này và sau đó writeStringToFile ... nhưng nội dung khác nhau. Tôi nhận được mã thông báo lạ trong tệp được sao chép .. tôi không hiểu lý do tại sao tôi phải chỉ định mã hóa.
mmm

11
@ Hamidan, chọn mã hóa đúng là một thuật toán rất phức tạp. Nó thường được thực hiện trong trình soạn thảo văn bản nhưng đôi khi chúng không phát hiện được mã hóa chính xác. Tôi không mong đợi API đọc tệp sẽ nhúng thuật toán phức tạp như vậy để đọc tệp của tôi.
Vincent Robert

1
@SecretService Ngoài ra, các thuật toán đó sử dụng thông tin như ngôn ngữ, ngôn ngữ và các cài đặt khu vực khác của hệ điều hành, có nghĩa là đọc tệp mà không chỉ định mã hóa có thể hoạt động trên thiết lập của bạn nhưng không phải trên người khác.
Feuermurmel

Tệp tin Apache . readLines (tệp) & copyURLToFile (URL, tempFile).
Yash

2
Tôi không nghĩ rằng nó sẽ hoạt động nếu tài nguyên được tìm thấy trong một cái bình. Sau đó, nó sẽ không phải là một tập tin.
Ville Oikarinen

16

Tôi thường có vấn đề này bản thân mình. Để tránh phụ thuộc vào các dự án nhỏ, tôi thường viết một hàm tiện ích nhỏ khi tôi không cần commons io hoặc tương tự. Đây là mã để tải nội dung của tệp trong bộ đệm chuỗi:

StringBuffer sb = new StringBuffer();

BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("path/to/textfile.txt"), "UTF-8"));
for (int c = br.read(); c != -1; c = br.read()) sb.append((char)c);

System.out.println(sb.toString());   

Việc chỉ định mã hóa rất quan trọng trong trường hợp đó, bởi vì bạn có thể đã chỉnh sửa tệp của mình trong UTF-8, sau đó đặt nó vào một cái lọ và máy tính mở tệp có thể có mã CP-1251 làm mã hóa tệp gốc của nó (ví dụ) ; Vì vậy, trong trường hợp này, bạn không bao giờ biết mã hóa đích, do đó thông tin mã hóa rõ ràng là rất quan trọng. Ngoài ra, vòng lặp để đọc tệp char bằng char có vẻ không hiệu quả, nhưng nó được sử dụng trên BufferedReader, và do đó thực sự khá nhanh.


15

Bạn có thể sử dụng mẫu mã Java sau đây

new String(Files.readAllBytes(Paths.get(getClass().getResource("example.txt").toURI())));

Những câu lệnh nhập nào là cần thiết để kéo các lớp "Tệp" và "Đường dẫn"?
Steve Scherer

1
cả hai đều là một phần của gói java.nio.file có sẵn từ JDK 7+
Raghu K Nair

Không hoạt động khi trong một tệp jar.
Displee

4

Nếu bạn muốn lấy Chuỗi của mình từ tài nguyên dự án như tệp testcase / foo.json trong src / main / resource trong dự án của bạn, hãy làm điều này:

String myString= 
 new String(Files.readAllBytes(Paths.get(getClass().getClassLoader().getResource("testcase/foo.json").toURI())));

Lưu ý rằng phương thức getClassLoader () bị thiếu trong một số ví dụ khác.


2

Sử dụng FileUtils của Apache. Nó có một phương thức readFileToString


Tệp chỉ hoạt động đối với các tài nguyên đường dẫn là các tệp. Không phải nếu chúng là các phần tử trong tệp .jar hoặc một phần của tệp chất béo, một trong những triển khai trình nạp lớp khác.
toolforger

2

Tôi đang sử dụng cách sau để đọc các tệp tài nguyên từ classpath:

import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.Scanner;

public class ResourceUtilities
{
    public static String resourceToString(String filePath) throws IOException, URISyntaxException
    {
        try (InputStream inputStream = ResourceUtilities.class.getClassLoader().getResourceAsStream(filePath))
        {
            return inputStreamToString(inputStream);
        }
    }

    private static String inputStreamToString(InputStream inputStream)
    {
        try (Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"))
        {
            return scanner.hasNext() ? scanner.next() : "";
        }
    }
}

Không yêu cầu phụ thuộc của bên thứ ba.


1

Với bộ nhập tĩnh, giải pháp Guava có thể rất nhỏ gọn một lớp:

toString(getResource("foo.txt"), UTF_8);

Nhập khẩu sau đây là bắt buộc:

import static com.google.common.io.Resources.getResource
import static com.google.common.io.Resources.toString
import static java.nio.charset.StandardCharsets.UTF_8

1
package test;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        try {
            String fileContent = getFileFromResources("resourcesFile.txt");
            System.out.println(fileContent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //USE THIS FUNCTION TO READ CONTENT OF A FILE, IT MUST EXIST IN "RESOURCES" FOLDER
    public static String getFileFromResources(String fileName) throws Exception {
        ClassLoader classLoader = Main.class.getClassLoader();
        InputStream stream = classLoader.getResourceAsStream(fileName);
        String text = null;
        try (Scanner scanner = new Scanner(stream, StandardCharsets.UTF_8.name())) {
            text = scanner.useDelimiter("\\A").next();
        }
        return text;
    }
}

1

Ít nhất là với Apache commons-io 2.5, phương thức IOUtils.toString () hỗ trợ một đối số URI và trả về nội dung của các tệp nằm trong các tệp trên đường dẫn lớp:

IOUtils.toString(SomeClass.class.getResource(...).toURI(), ...)

1

Tôi thích câu trả lời của akosicki với Thủ thuật quét ngu ngốc. Đó là cách đơn giản nhất mà tôi thấy mà không cần phụ thuộc bên ngoài hoạt động trong Java 8 (và trên thực tế hoàn toàn trở lại Java 5). Đây là một câu trả lời thậm chí đơn giản hơn nếu bạn có thể sử dụng Java 9 trở lên (kể từ khi InputStream.readAllBytes()được thêm vào Java 9):

String text = new String(AppropriateClass.class.getResourceAsStream("foo.txt").readAllBytes());

0

Quả ổi cũng có Files.readLines()nếu bạn muốn giá trị trả về là List<String>từng dòng:

List<String> lines = Files.readLines(new File("/file/path/input.txt"), Charsets.UTF_8);

Vui lòng tham khảo tại đây để so sánh 3 cách ( BufferedReaderso với Guava Filesso với Guava Resources) để lấy Stringtừ tệp văn bản.


Lớp Charsets là gì? không phải là bản địa
điện tử

@ e-info128 Charsetscũng ở ổi. Xem điều này: google.github.io/guava/release/23.0/api/docs
philipjkim

0

Đây là cách tiếp cận của tôi làm việc tốt

public String getFileContent(String fileName) {
    String filePath = "myFolder/" + fileName+ ".json";
    try(InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath)) {
        return IOUtils.toString(stream, "UTF-8");
    } catch (IOException e) {
        // Please print your Exception
    }
}

2
IOUtils đến từ đâu? Nguồn nên được tham khảo rõ ràng.
ehecatl

0

Tôi đã viết các phương thức readResource () ở đây , để có thể thực hiện nó trong một lệnh gọi đơn giản. Nó phụ thuộc vào thư viện Guava, nhưng tôi thích các phương thức chỉ có JDK được đề xuất trong các câu trả lời khác và tôi nghĩ tôi sẽ thay đổi những cách này.


0

Nếu bạn bao gồm ổi, thì bạn có thể sử dụng:

String fileContent = Files.asCharSource(new File(filename), Charset.forName("UTF-8")).read();

(Các giải pháp khác đề cập đến phương pháp khác cho ổi nhưng chúng không được dùng nữa)


0

Các cod sau đây làm việc cho tôi:

compile group: 'commons-io', name: 'commons-io', version: '2.6'

@Value("classpath:mockResponse.json")
private Resource mockResponse;

String mockContent = FileUtils.readFileToString(mockResponse.getFile(), "UTF-8");

0

Đây là một giải pháp sử dụng Java 11 Files.readString:

public class Utils {
    public static String readResource(String name) throws URISyntaxException, IOException {
        var uri = Utils.class.getResource("/" + name).toURI();
        var path = Paths.get(uri);
        return Files.readString(path);
    }
}

0

Tôi đã thực hiện phương pháp tĩnh KHÔNG phụ thuộc như thế này:

import java.nio.file.Files;
import java.nio.file.Paths;

public class ResourceReader {
    public  static String asString(String resourceFIleName) {
        try  {
            return new String(Files.readAllBytes(Paths.get(new CheatClassLoaderDummyClass().getClass().getClassLoader().getResource(resourceFIleName).toURI())));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
class CheatClassLoaderDummyClass{//cheat class loader - for sql file loading
}

0

Tôi thích các tiện ích chung của Apache cho loại công cụ này và sử dụng rộng rãi trường hợp sử dụng chính xác này (đọc các tệp từ đường dẫn lớp) khi kiểm tra, đặc biệt là để đọc các tệp JSON từ /src/test/resourcesmột phần của kiểm tra đơn vị / tích hợp. ví dụ

public class FileUtils {

    public static String getResource(String classpathLocation) {
        try {
            String message = IOUtils.toString(FileUtils.class.getResourceAsStream(classpathLocation),
                    Charset.defaultCharset());
            return message;
        }
        catch (IOException e) {
            throw new RuntimeException("Could not read file [ " + classpathLocation + " ] from classpath", e);
        }
    }

}

Đối với mục đích thử nghiệm, thật tuyệt khi bắt IOExceptionvà ném RuntimeException- lớp thử nghiệm của bạn có thể trông giống như, ví dụ

    @Test
    public void shouldDoSomething () {
        String json = FileUtils.getResource("/json/input.json");

        // Use json as part of test ...
    }

-2
public static byte[] readResoureStream(String resourcePath) throws IOException {
    ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
    InputStream in = CreateBffFile.class.getResourceAsStream(resourcePath);

    //Create buffer
    byte[] buffer = new byte[4096];
    for (;;) {
        int nread = in.read(buffer);
        if (nread <= 0) {
            break;
        }
        byteArray.write(buffer, 0, nread);
    }
    return byteArray.toByteArray();
}

Charset charset = StandardCharsets.UTF_8;
String content = new   String(FileReader.readResoureStream("/resource/...*.txt"), charset);
String lines[] = content.split("\\n");

Vui lòng thêm lời giải thích ngắn gọn cho câu trả lời của bạn.
Nikolay Mihaylov
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.