Java 中 InputStream 转换为 String 的几种方法

我们在 Java 中经常会碰到如何把 InputStream 转换成 String 的情形,比如从文件或网络得到一个 InputStream,需要转换成字符串输出或赋给别的变量。

未真正关注这个问题之前我常用的办法就是按字节一次次读到缓冲区,或是建立 BufferedReader 逐行读取。其实大可不必费此周折,我们可以用 Apache commons IOUtils,或者是 JDK 1.5 后的 Scanner,还可用 Google Guava 库的 CharStreams。到了 JDK7,若要从文件中直接得到字符串还能用 java.nio.file.Files#readAllLines 和 java.nio.file.Files#readAllBytes 方法。

下面看各个例子,为能够实际用运,例子写在 main 方法里,并从文件获得一个 InputStream,代码中把可能要捕获的异常抛出来。再就是注意处理输入输出流时有涉及到字符集,字符集乱了就乱码了,默认字符集是 System.getProperty("file.encoding"),通常我们都用 UTF-8,异常 UnsupportedEncodingException 继承自 IOException。

使用 Scanner

package com.daimafans.io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Scanner;


public class Test
{
    public static void main(String[] args) throws FileNotFoundException
    {
        InputStream inputStream = new FileInputStream("/opt/test/sample.txt");
        Scanner scanner = new Scanner(inputStream, "UTF-8");
        String text = scanner.useDelimiter("\\A").next();
        System.out.println(text);
        scanner.close();
    }
}

使用 BufferedReader

package com.daimafans.io;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class Test
{
    public static void main(String[] args) throws IOException
    {
        InputStream inputStream = new FileInputStream("/opt/test/sample.txt");
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        boolean firstLine = true;
        String line = null; ;
        while((line = bufferedReader.readLine()) != null)
        {
            if(!firstLine)
            {
                stringBuilder.append(System.getProperty("line.separator"));
            }
            else
            {
                firstLine = false;
            }
            stringBuilder.append(line);
        }
        System.out.println(stringBuilder.toString());
    }
}

使用 readBytes

package com.daimafans.io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class Test
{
    public static void main(String[] args) throws IOException
    {
        InputStream inputStream = new FileInputStream("/opt/test/sample.txt");

        byte[] buffer = new byte[2048];
        int readBytes = 0;
        StringBuilder stringBuilder = new StringBuilder();
        while((readBytes = inputStream.read(buffer)) > 0)
        {
            stringBuilder.append(new String(buffer, 0, readBytes));
        }
        System.out.println(stringBuilder.toString());
    }
}

缓冲区的大小自己根据实际来调,比 BufferedReader 还简洁些,不需管换行符的事情。

使用 Apache commons IOUtils.toString

package com.daimafans.io;

import java.io.*;
import org.apache.commons.io.IOUtils;

public class Test
{
    public static void main(String[] args) throws IOException
    {
        InputStream inputStream = new FileInputStream("/opt/test/sample.txt");
        String text = IOUtils.toString(inputStream);
        System.out.println(text);
    }
}

第三方库就是第三方库,人家充分考虑到了你的感受,你对 JDK 库的抱怨,多简洁,一行搞定。IOUtils 还能把内容拷入其他的 Writer 中,如 IOUtils.copy(inputStream, new StringWriter())。

使用 Google guava 的 CharStreams 方法

package com.daimafans.io;

import java.io.*;
import com.google.common.io.CharStreams;

public class Test
{
    public static void main(String[] args) throws IOException
    {
        InputStream inputStream = new FileInputStream("/opt/test/sample.txt");
        String text = CharStreams.toString(new InputStreamReader(inputStream, "UTF-8"));
        System.out.println(text);
    }
}

CharSteams 不是直接作用在 InputSteam 上的,还要靠 InputStreamReader 拱个桥。

使用 JDK 7 的 NIO readAllBytes

package com.daimafans.io;

import java.io.IOException;
import java.nio.file.*;

public class Test
{
    public static void main(String[] args) throws IOException
    {
        byte[] bytes = Files.readAllBytes(Paths.get("/opt/test/sample.txt"));
        String text = new String(bytes);
        System.out.println(text);
    }
}

这让我们相信 JDK 一直还有人在管,虽然不可能象动态语言的方法那么快捷,上面的 readAllBytes 在处理大文件时肯定会很被动的。而 Files.readAllLines 会把文件的内容读入一个 List 对象中,往内存不断放东西就得掂量下内存会不会被爆。在 java.nio.file.* 还有很多新事物可供发掘。

小结

不管怎么样,问题最终都能被解决。但是 java 的 io 确实是个硬伤,复杂而又低智商,与其简化 io 操作的初衷简直是背道而驰。

与此同时,还需要关注是否会导致内存溢出,和乱码的问题。文中的例子系从网络搜集和验证,并未考虑此处的问题。

希望本文能带给读者一点灵感。

如果觉得这对你有用,请随意赞赏,给与作者支持
评论 0
最新评论