简体   繁体   English

如何从“assets”目录中读取文本文件作为字符串?

[英]How to read a text file from “assets” directory as a string?

I have a file in my assets folder... how do I read it? 我的资产文件夹中有一个文件...如何阅读?

Now I'm trying: 现在我正在尝试:

      public static String readFileAsString(String filePath)
        throws java.io.IOException{
            StringBuffer fileData = new StringBuffer(1000);
            BufferedReader reader = new BufferedReader(
                    new FileReader(filePath));
            char[] buf = new char[1024];
            int numRead=0;
            while((numRead=reader.read(buf)) != -1){
                String readData = String.valueOf(buf, 0, numRead);
                fileData.append(readData);
                buf = new char[1024];
            }
            reader.close();
            return fileData.toString();
        }

But it cast a null pointer exception... 但它抛出一个空指针异常......

the file is called "origin" and it is in folder assets 该文件名为“origin”,它位于文件夹资产中

I tried to cast it with: 我试着把它投射:

readFileAsString("file:///android_asset/origin");

and

readFileAsString("asset/origin");``

but both failed... any advice? 但都失败了......有什么建议吗?

BufferedReader's readLine() method returns a null when the end of the file is reached, so you'll need to watch for it and avoid trying to append it to your string. 当达到文件末尾时,BufferedReader的readLine()方法返回null,因此您需要注意它并避免尝试将其附加到字符串中。

The following code should be easy enough: 以下代码应该很简单:

public static String readFileAsString(String filePath) throws java.io.IOException
{
    BufferedReader reader = new BufferedReader(new FileReader(filePath));
    String line, results = "";
    while( ( line = reader.readLine() ) != null)
    {
        results += line;
    }
    reader.close();
    return results;
}

Simple and to-the-point. 简单而重要。

Short answer, do this: 简短的回答,这样做:

public static String readFile( String filePath ) throws IOException
{
    Reader reader = new FileReader( filePath );
    StringBuilder sb = new StringBuilder(); 
    char buffer[] = new char[16384];  // read 16k blocks
    int len; // how much content was read? 
    while( ( len = reader.read( buffer ) ) > 0 ){
        sb.append( buffer, 0, len ); 
    }
    reader.close();
    return sb.toString();
}

It's very straight forward, very fast, and works well for unreasonable large textfiles (100+ MB) 它非常直接,非常快,适用于不合理的大型文本文件(100 + MB)


Long answer: 答案很长:

(Code at the end) (最后的代码)

Many times it won't matter, but this method is pretty fast and quite readable. 很多时候它并不重要,但这种方法非常快,而且非常易读。 In fact its an order of complexity faster than @Raceimation's answer -- O(n) instead of O(n^2). 事实上,它的复杂度比@ Raceimation的答案更快--O(n)而不是O(n ^ 2)。

I've tested six methods (from slow to fast): 我测试了六种方法(从慢到快):

  • concat: reading line by line, concat with str += ... * This is alarmingly slow even for smaller files (takes ~70 seconds for a 3MB file) * concat:逐行读取,使用str + = ... * 即使对于较小的文件也是如此(对于3MB文件需要约70秒),这速度非常慢*
  • strbuilder guessing length: StringBuilder, initialized with the files size. strbuilder猜测长度:StringBuilder,用文件大小初始化。 i'm guessing that its slow because it really tries to find such a huge chunk of linear memory. 我猜它很慢,因为它真的试图找到如此庞大的线性记忆块。
  • strbuilder with line buffer: StringBuilder, file is read line by line 带行缓冲区的strbuilder:StringBuilder,逐行读取文件
  • strbuffer with char[] buffer: Concat with StringBuffer, read the file in 16k blocks strbuffer with char [] buffer:Concat with StringBuffer,读取16k块的文件
  • strbuilder with char[] buffer: Concat with StringBuilder, read the file in 16k blocks 使用char []缓冲区的strbuilder:使用StringBuilder进行Concat,以16k块读取文件
  • preallocate byte[filesize] buffer: Allocate a byte[] buffer the size of the file and let the java api decide how to buffer individual blocks. preallocate byte [filesize] buffer:分配一个byte []缓冲区大小的文件,让java api决定如何缓冲各个块。

Conclusion: 结论:

Preallocating the buffer entirely is the fastest on very large files, but the method isn't very versatile because the total filesize must be known ahead of time. 完全预分配缓冲区对于非常大的文件是最快的,但是该方法不是非常通用,因为必须提前知道总文件大小。 Thats why i suggest using strBuilder with char[] buffers, its still simple and if needed easily changable to accept any input stream instead of just files. 这就是为什么我建议使用带有char []缓冲区的strBuilder,它仍然很简单,如果需要,可以轻松地接受任何输入流而不仅仅是文件。 Yet its certainly fast enough for all reasonable cases. 然而,它对于所有合理的案例来说肯定足够快

Test Results + Code 测试结果+代码

import java.io.*; 

public class Test
{

    static final int N = 5; 

    public final static void main( String args[] ) throws IOException{
        test( "1k.txt", true ); 
        test( "10k.txt", true ); 
        // concat with += would take ages here, so we skip it
        test( "100k.txt", false ); 
        test( "2142k.txt", false ); 
        test( "pruned-names.csv", false ); 
        // ah, what the heck, why not try a binary file
        test( "/Users/hansi/Downloads/xcode46graphicstools6938140a.dmg", false );
    }

    public static void test( String file, boolean includeConcat ) throws IOException{

        System.out.println( "Reading " + file + " (~" + (new File(file).length()/1024) + "Kbytes)" ); 
        strbuilderwithchars( file ); 
        strbuilderwithchars( file ); 
        strbuilderwithchars( file ); 
        tick( "Warm up... " ); 

        if( includeConcat ){
            for( int i = 0; i < N; i++ )
                concat( file ); 
            tick( "> Concat with +=                  " ); 
        }
        else{
            tick( "> Concat with +=   **skipped**    " ); 
        }

        for( int i = 0; i < N; i++ )
            strbuilderguess( file ); 
        tick( "> StringBuilder init with length  " ); 

        for( int i = 0; i < N; i++ )
            strbuilder( file ); 
        tick( "> StringBuilder with line buffer  " );

        for( int i = 0; i < N; i++ )
            strbuilderwithchars( file ); 
        tick( "> StringBuilder with char[] buffer" );

        for( int i = 0; i < N; i++ )
            strbufferwithchars( file ); 
        tick( "> StringBuffer with char[] buffer " );

        for( int i = 0; i < N; i++ )
            singleBuffer( file ); 
        tick( "> Allocate byte[filesize]         " );

        System.out.println(); 
    }

    public static long now = System.currentTimeMillis(); 
    public static void tick( String message ){
        long t = System.currentTimeMillis(); 
        System.out.println( message + ": " + ( t - now )/N + " ms" ); 
        now = t; 
    }


    // StringBuilder with char[] buffer
    // + works if filesize is unknown
    // + pretty fast 
    public static String strbuilderwithchars( String filePath ) throws IOException
    {
        Reader reader = new FileReader( filePath );
        StringBuilder sb = new StringBuilder(); 
        char buffer[] = new char[16384];  // read 16k blocks
        int len; // how much content was read? 
        while( ( len = reader.read( buffer ) ) > 0 ){
            sb.append( buffer, 0, len ); 
        }
        reader.close();
        return sb.toString();
    }

    // StringBuffer with char[] buffer
    // + works if filesize is unknown
    // + faster than stringbuilder on my computer
    // - should be slower than stringbuilder, which confuses me 
    public static String strbufferwithchars( String filePath ) throws IOException
    {
        Reader reader = new FileReader( filePath );
        StringBuffer sb = new StringBuffer(); 
        char buffer[] = new char[16384];  // read 16k blocks
        int len; // how much content was read? 
        while( ( len = reader.read( buffer ) ) > 0 ){
            sb.append( buffer, 0, len ); 
        }
        reader.close();
        return sb.toString();
    }

    // StringBuilder init with length
    // + works if filesize is unknown
    // - not faster than any of the other methods, but more complicated
    public static String strbuilderguess(String filePath) throws IOException
    {
        File file = new File( filePath ); 
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line;
        StringBuilder sb = new StringBuilder( (int)file.length() ); 
        while( ( line = reader.readLine() ) != null)
        {
            sb.append( line ); 
        }
        reader.close();
        return sb.toString();
    }

    // StringBuilder with line buffer
    // + works if filesize is unknown
    // + pretty fast 
    // - speed may (!) vary with line length
    public static String strbuilder(String filePath) throws IOException
    {
        BufferedReader reader = new BufferedReader(new FileReader(filePath));
        String line;
        StringBuilder sb = new StringBuilder(); 
        while( ( line = reader.readLine() ) != null)
        {
            sb.append( line ); 
        }
        reader.close();
        return sb.toString();
    }


    // Concat with += 
    // - slow
    // - slow
    // - really slow
    public static String concat(String filePath) throws IOException
    {
        BufferedReader reader = new BufferedReader(new FileReader(filePath));
        String line, results = "";
    int i = 0; 
        while( ( line = reader.readLine() ) != null)
        {
            results += line;
            i++; 
        }
        reader.close();
        return results;
    }

    // Allocate byte[filesize]
    // + seems to be the fastest for large files
    // - only works if filesize is known in advance, so less versatile for a not significant performance gain
    // + shortest code
    public static String singleBuffer(String filePath ) throws IOException{
        FileInputStream in = new FileInputStream( filePath );
        byte buffer[] = new byte[(int) new File( filePath).length()];  // buffer for the entire file
        int len = in.read( buffer ); 
        return new String( buffer, 0, len ); 
    }
}


/**
 *** RESULTS ***

Reading 1k.txt (~31Kbytes)
Warm up... : 0 ms
> Concat with +=                  : 37 ms
> StringBuilder init with length  : 0 ms
> StringBuilder with line buffer  : 0 ms
> StringBuilder with char[] buffer: 0 ms
> StringBuffer with char[] buffer : 0 ms
> Allocate byte[filesize]         : 1 ms

Reading 10k.txt (~313Kbytes)
Warm up... : 0 ms
> Concat with +=                  : 708 ms
> StringBuilder init with length  : 2 ms
> StringBuilder with line buffer  : 2 ms
> StringBuilder with char[] buffer: 1 ms
> StringBuffer with char[] buffer : 1 ms
> Allocate byte[filesize]         : 1 ms

Reading 100k.txt (~3136Kbytes)
Warm up... : 7 ms
> Concat with +=   **skipped**    : 0 ms
> StringBuilder init with length  : 19 ms
> StringBuilder with line buffer  : 21 ms
> StringBuilder with char[] buffer: 9 ms
> StringBuffer with char[] buffer : 9 ms
> Allocate byte[filesize]         : 8 ms

Reading 2142k.txt (~67204Kbytes)
Warm up... : 181 ms
> Concat with +=   **skipped**    : 0 ms
> StringBuilder init with length  : 367 ms
> StringBuilder with line buffer  : 372 ms
> StringBuilder with char[] buffer: 208 ms
> StringBuffer with char[] buffer : 202 ms
> Allocate byte[filesize]         : 199 ms

Reading pruned-names.csv (~11200Kbytes)
Warm up... : 23 ms
> Concat with +=   **skipped**    : 0 ms
> StringBuilder init with length  : 54 ms
> StringBuilder with line buffer  : 57 ms
> StringBuilder with char[] buffer: 32 ms
> StringBuffer with char[] buffer : 31 ms
> Allocate byte[filesize]         : 32 ms

Reading /Users/hansi/Downloads/xcode46graphicstools6938140a.dmg (~123429Kbytes)
Warm up... : 1665 ms
> Concat with +=   **skipped**    : 0 ms
> StringBuilder init with length  : 2899 ms
> StringBuilder with line buffer  : 2978 ms
> StringBuilder with char[] buffer: 2702 ms
> StringBuffer with char[] buffer : 2684 ms
> Allocate byte[filesize]         : 1567 ms


**/

Ps. PS。 You might have noticed that StringBuffer is slightly faster than StringBuilder. 您可能已经注意到StringBuffer比StringBuilder稍快。 This is a bit nonsense because the classes are the same, except StringBuilder is not synchronized. 这有点无稽之谈,因为类是相同的,除了StringBuilder不同步。 If anyone can (or) can't reproduce this... I'm most curious :) 如果有人可以(或)不能重现这个......我最好奇:)

You can open an input stream using AssetsManager . 您可以使用AssetsManager打开输入流。

InputStream input = getAssets().open("origin");
Reader reader = new InputStreamReader(input, "UTF-8");

getAssets() is a method of the Context class. getAssets()Context类的一个方法。

Also note that you shouldn't recreate a buffer of characters ( buf = new char[1024] , the last line of your cycle). 另请注意,您不应重新创建字符缓冲区( buf = new char[1024] ,循环的最后一行)。

You should try org.appache.commons.io.IOUtils.toString(InputStream is) to get file content as string. 您应该尝试使用org.appache.commons.io.IOUtils.toString(InputStream is)将文件内容作为字符串。 There you can pass InputStream object which you will get from 在那里你可以传递你将从中获得的InputStream对象

getAssets().open("xml2json.txt")

in your Activity . 在你的Activity

To get String use this: 要获取String使用:

String xml = IOUtils.toString((getAssets().open("xml2json.txt")));

I wrote a function that does the same thing as yours. 我写了一个与你的功能相同的功能。 I wrote it a while back but I believe it still works correctly. 我写了一段时间,但我相信它仍然可以正常工作。

public static final String grabAsSingleString(File fileToUse) 
            throws FileNotFoundException {

        BufferedReader theReader = null;
        String returnString = null;

        try {
            theReader = new BufferedReader(new FileReader(fileToUse));
            char[] charArray = null;

            if(fileToUse.length() > Integer.MAX_VALUE) {
                // TODO implement handling of large files.
                System.out.println("The file is larger than int max = " +
                        Integer.MAX_VALUE);
            } else {
                charArray = new char[(int)fileToUse.length()];

                // Read the information into the buffer.
                theReader.read(charArray, 0, (int)fileToUse.length());
                returnString = new String(charArray);

            }
        } catch (FileNotFoundException ex) {
            throw ex;
        } catch(IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                theReader.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return returnString;
    }

Now you can use this function if you wish, but when you're passing in the file either through a file object or a string, make sure you either give the full path of the file such as "C:\\Program Files\\test.dat" OR you pass in the relative link from your working directory. 现在您可以根据需要使用此功能,但是当您通过文件对象或字符串传递文件时,请确保提供文件的完整路径,例如“C:\\ Program Files \\ test”。 dat“或者您传入工作目录中的相对链接。 Your working directory is commonly the directory you launch the application from (unless you change it). 您的工作目录通常是您启动应用程序的目录(除非您更改它)。 So if the file was in a folder called data, you would pass in "./data/test.dat" 因此,如果文件位于名为data的文件夹中,您将传入“./data/test.dat”

Yes, I know this is working with android so the Windows URI isn't applicable, but you should get my point. 是的,我知道这是在使用android,所以Windows URI不适用,但你应该明白我的观点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM