读取Assets文件作为字符串

70

我希望能够读取Assets中的文件内容并将其作为字符串处理。例如,一个位于src/main/assets/目录下的文本文件。

原始问题
我发现这个问题大多被用作“常见问题解答”,以便阅读Assets文件,因此我总结了上面的问题。以下是我的原始问题。

我试图将一个Assets文件读取为字符串。我在我的assets文件夹中有一个名为data.opml的文件,并想将其作为字符串读取。

我尝试过一些方法:

 AssetFileDescriptor descriptor = getAssets().openFd("data.opml");
 FileReader reader = new FileReader(descriptor.getFileDescriptor());

还有:

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

但是没有成功,因此需要一个完整的例子以便理解。

4个回答

154

getAssets().open()会返回一个InputStream。使用标准的Java I/O从中读取:

Java:

StringBuilder sb = new StringBuilder();
InputStream is = getAssets().open("book/contents.json");
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8 ));
String str;
while ((str = br.readLine()) != null) {
    sb.append(str);
}
br.close();

Kotlin:
val str = assets.open("book/contents.json").bufferedReader().use { it.readText() }

1
非常感谢!但是(可能是我自己的问题),我无法使用OPML.importFromFile(xxx,MainTabActivity.this)使其工作;并将xxx替换为buf或in(不适用于stringbuilder参数(以及bufferedreader)。) - Mdlc
4
“无法使其工作”是一个没有用的陈述,就像您问题中的“不工作”一样。除非您完全而准确地解释您的意思,否则任何人都很难为您提供进一步的帮助。 - CommonsWare
1
@Mark029348:将protected static void importFromInputStream()方法更改为public,然后使用从getAssets().open()获取的InputStreamimportFromFile()需要一个文件名,不能直接用于资产。 - CommonsWare
1
@Mark029348: 我猜你修改了 OPML 类中的 importFromFile() 方法。但我没有告诉过你这样做。请回滚到原始文件状态。将文本编辑光标直接放置在 protected static void importFromInputStream() 方法中 protected 关键字中的 d 字母右侧,按下退格键 8 次。然后输入 ublic。这将把 protected 关键字更改为 public。保存文件。现在你可以在自己的代码中使用刚刚修改为公共方法的 importFromInputStream() 方法来替换之前想使用的 importFromFile() 方法。 - CommonsWare
@berserk:资产不需要权限。 - CommonsWare
显示剩余9条评论

35

CommonsWare的代码中存在一个小错误 - 换行符被丢弃了并且没有添加到字符串中。以下是一些已修复的代码,可以直接复制粘贴:

private String loadAssetTextAsString(Context context, String name) {
        BufferedReader in = null;
        try {
            StringBuilder buf = new StringBuilder();
            InputStream is = context.getAssets().open(name);
            in = new BufferedReader(new InputStreamReader(is));

            String str;
            boolean isFirst = true;
            while ( (str = in.readLine()) != null ) {
                if (isFirst)
                    isFirst = false;
                else
                    buf.append('\n');
                buf.append(str);
            }
            return buf.toString();
        } catch (IOException e) {
            Log.e(TAG, "Error opening asset " + name);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    Log.e(TAG, "Error closing asset " + name);
                }
            }
        }

        return null;
    }

2
简而言之 - 为什么不这样做,何时适用,应该使用什么替代方案? - Michael Litvin
39
我认为你混淆了finally和finalize。finally是try catch块的结束,是关闭资源的最佳位置。finalize是对象引用的一部分,当对象被垃圾回收时可能会调用它,而它并不是关闭资源的正确位置。 - Andrew Kelly

5
您也可以不使用循环来实现,这很简单。
AssetManager assetManager = getAssets();
InputStream input;
String text = "";

    try {
        input = assetManager.open("test.txt");

        int size = input.available();
        byte[] buffer = new byte[size];
        input.read(buffer);
        input.close();

        // byte buffer into a string
        text = new String(buffer);

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    Log.v("TAG", "Text File: " + text);

3
我认为你不能安全地像那样使用available()方法。请参见http://developer.android.com/reference/java/io/InputStream.html#available()。 - realh
实际上,available() 函数只适用于可以适应 SO I/O 缓冲区的小文件。 - Renascienza
在实践中可能会在某些情况下起作用,但我不想成为那个必须找到并修复错误的开发人员,尤其是当你的文件稍微有点太大时。 - Mark McKenna
5
虽然这篇文章有些陈旧,但我还是会给它点个踩。"available() 返回一个估计值,表示在下一次调用该输入流的方法时,可以从该输入流中读取(或跳过)多少字节而不会被阻塞。"严肃地说,请不要以这种方式使用 available()。 - wndxlori

5

你好,这是我认为最简洁的方法:

  public static String loadTextFromAssets(Context context, String assetsPath, Charset charset) throws IOException {
        InputStream is = context.getResources().getAssets().open(assetsPath);
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for (int length = is.read(buffer); length != -1; length = is.read(buffer)) {
            baos.write(buffer, 0, length);
        }
        is.close();
        baos.close();
        return charset == null ? new String(baos.toByteArray()) : new String(baos.toByteArray(), charset);
    }

由于读者可能会因为换行符问题而感到困扰。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接