如果您有java.io.InputStream对象,应该如何处理该对象并生成String?


假设我有一个包含文本数据的InputStream,我想将其转换为String,例如,我可以将其写入日志文件。

获取InputStream并将其转换为字符串的最简单方法是什么?

public String convertStreamToString(InputStream is) {
// ???
}

当前回答

使用java 9中支持的java.io.InputStream.transferTo(OutputStream)和ByteArrayOutputStream.toString(String),该字符串采用字符集名称:

public static String gobble(InputStream in, String charsetName) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    in.transferTo(bos);
    return bos.toString(charsetName);
}

其他回答

Use:

InputStream in = /* Your InputStream */;
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String read;

while ((read=br.readLine()) != null) {
    //System.out.println(read);
    sb.append(read);
}

br.close();
return sb.toString();

我已经创建了这段代码,并且它有效。没有所需的外部插件。

有一个字符串到流和流到字符串的转换器:

import java.io.ByteArrayInputStream;
import java.io.InputStream;

public class STRINGTOSTREAM {

    public static void main(String[] args)
    {
        String text = "Hello Bhola..!\nMy Name Is Kishan ";

        InputStream strm = new ByteArrayInputStream(text.getBytes());    // Convert String to Stream

        String data = streamTostring(strm);

        System.out.println(data);
    }

    static String streamTostring(InputStream stream)
    {
        String data = "";

        try
        {
            StringBuilder stringbuld = new StringBuilder();
            int i;
            while ((i=stream.read())!=-1)
            {
                stringbuld.append((char)i);
            }
            data = stringbuld.toString();
        }
        catch(Exception e)
        {
            data = "No data Streamed.";
        }
        return data;
    }

这个问题的解决方案不是最简单的,但由于没有提到NIO流和通道,这里有一个使用NIO通道和ByteBuffer将流转换为字符串的版本。

public static String streamToStringChannel(InputStream in, String encoding, int bufSize) throws IOException {
    ReadableByteChannel channel = Channels.newChannel(in);
    ByteBuffer byteBuffer = ByteBuffer.allocate(bufSize);
    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    WritableByteChannel outChannel = Channels.newChannel(bout);
    while (channel.read(byteBuffer) > 0 || byteBuffer.position() > 0) {
        byteBuffer.flip();  //make buffer ready for write
        outChannel.write(byteBuffer);
        byteBuffer.compact(); //make buffer ready for reading
    }
    channel.close();
    outChannel.close();
    return bout.toString(encoding);
}

下面是如何使用它的示例:

try (InputStream in = new FileInputStream("/tmp/large_file.xml")) {
    String x = streamToStringChannel(in, "UTF-8", 1);
    System.out.println(x);
}

对于大型文件,此方法的性能应该很好。

异-8859-1

如果您知道输入流的编码是ISO-8859-1或ASCII,这里有一种非常高效的方法来实现这一点。它(1)避免了StringWriter的内部StringBuffer中存在的不必要的同步,(2)避免了InputStreamReader的开销,(3)最小化了必须复制StringBuilder的内部字符数组的次数。

public static String iso_8859_1(InputStream is) throws IOException {
    StringBuilder chars = new StringBuilder(Math.max(is.available(), 4096));
    byte[] buffer = new byte[4096];
    int n;
    while ((n = is.read(buffer)) != -1) {
        for (int i = 0; i < n; i++) {
            chars.append((char)(buffer[i] & 0xFF));
        }
    }
    return chars.toString();
}

UTF-8型

对于使用UTF-8编码的流,可以使用相同的通用策略:

public static String utf8(InputStream is) throws IOException {
    StringBuilder chars = new StringBuilder(Math.max(is.available(), 4096));
    byte[] buffer = new byte[4096];
    int n;
    int state = 0;
    while ((n = is.read(buffer)) != -1) {
        for (int i = 0; i < n; i++) {
            if ((state = nextStateUtf8(state, buffer[i])) >= 0) {
                chars.appendCodePoint(state);
            } else if (state == -1) { //error
                state = 0;
                chars.append('\uFFFD'); //replacement char
            }
        }
    }
    return chars.toString();
}

其中nextStateUtf8()函数定义如下:

/**
 * Returns the next UTF-8 state given the next byte of input and the current state.
 * If the input byte is the last byte in a valid UTF-8 byte sequence,
 * the returned state will be the corresponding unicode character (in the range of 0 through 0x10FFFF).
 * Otherwise, a negative integer is returned. A state of -1 is returned whenever an
 * invalid UTF-8 byte sequence is detected.
 */
static int nextStateUtf8(int currentState, byte nextByte) {
    switch (currentState & 0xF0000000) {
        case 0:
            if ((nextByte & 0x80) == 0) { //0 trailing bytes (ASCII)
                return nextByte;
            } else if ((nextByte & 0xE0) == 0xC0) { //1 trailing byte
                if (nextByte == (byte) 0xC0 || nextByte == (byte) 0xC1) { //0xCO & 0xC1 are overlong
                    return -1;
                } else {
                    return nextByte & 0xC000001F;
                }
            } else if ((nextByte & 0xF0) == 0xE0) { //2 trailing bytes
                if (nextByte == (byte) 0xE0) { //possibly overlong
                    return nextByte & 0xA000000F;
                } else if (nextByte == (byte) 0xED) { //possibly surrogate
                    return nextByte & 0xB000000F;
                } else {
                    return nextByte & 0x9000000F;
                }
            } else if ((nextByte & 0xFC) == 0xF0) { //3 trailing bytes
                if (nextByte == (byte) 0xF0) { //possibly overlong
                    return nextByte & 0x80000007;
                } else {
                    return nextByte & 0xE0000007;
                }
            } else if (nextByte == (byte) 0xF4) { //3 trailing bytes, possibly undefined
                return nextByte & 0xD0000007;
            } else {
                return -1;
            }
        case 0xE0000000: //3rd-to-last continuation byte
            return (nextByte & 0xC0) == 0x80 ? currentState << 6 | nextByte & 0x9000003F : -1;
        case 0x80000000: //3rd-to-last continuation byte, check overlong
            return (nextByte & 0xE0) == 0xA0 || (nextByte & 0xF0) == 0x90 ? currentState << 6 | nextByte & 0x9000003F : -1;
        case 0xD0000000: //3rd-to-last continuation byte, check undefined
            return (nextByte & 0xF0) == 0x80 ? currentState << 6 | nextByte & 0x9000003F : -1;
        case 0x90000000: //2nd-to-last continuation byte
            return (nextByte & 0xC0) == 0x80 ? currentState << 6 | nextByte & 0xC000003F : -1;
        case 0xA0000000: //2nd-to-last continuation byte, check overlong
            return (nextByte & 0xE0) == 0xA0 ? currentState << 6 | nextByte & 0xC000003F : -1;
        case 0xB0000000: //2nd-to-last continuation byte, check surrogate
            return (nextByte & 0xE0) == 0x80 ? currentState << 6 | nextByte & 0xC000003F : -1;
        case 0xC0000000: //last continuation byte
            return (nextByte & 0xC0) == 0x80 ? currentState << 6 | nextByte & 0x3F : -1;
        default:
            return -1;
    }
}

自动检测编码

如果您的输入流是使用ASCII、ISO-8859-1或UTF-8编码的,但您不确定是哪一种,我们可以使用与上一种方法类似的方法,但使用额外的编码检测组件在返回字符串之前自动检测编码。

public static String autoDetect(InputStream is) throws IOException {
    StringBuilder chars = new StringBuilder(Math.max(is.available(), 4096));
    byte[] buffer = new byte[4096];
    int n;
    int state = 0;
    boolean ascii = true;
    while ((n = is.read(buffer)) != -1) {
        for (int i = 0; i < n; i++) {
            if ((state = nextStateUtf8(state, buffer[i])) > 0x7F)
                ascii = false;
            chars.append((char)(buffer[i] & 0xFF));
        }
    }

    if (ascii || state < 0) { //probably not UTF-8
        return chars.toString();
    }
    //probably UTF-8
    int pos = 0;
    char[] charBuf = new char[2];
    for (int i = 0, len = chars.length(); i < len; i++) {
        if ((state = nextStateUtf8(state, (byte)chars.charAt(i))) >= 0) {
            boolean hi = Character.toChars(state, charBuf, 0) == 2;
            chars.setCharAt(pos++, charBuf[0]);
            if (hi) {
                chars.setCharAt(pos++, charBuf[1]);
            }
        }
    }
    return chars.substring(0, pos);
}

如果您的输入流的编码既不是ISO-8859-1,也不是ASCII,也不是UTF-8,那么我就遵从已经存在的其他答案。

如果不能使用Commons IO(FileUtils/IOUtils/CopyUtils),下面是一个使用BufferedReader逐行读取文件的示例:

public class StringFromFile {
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
        InputStream is = StringFromFile.class.getResourceAsStream("file.txt");
        BufferedReader br = new BufferedReader(new InputStreamReader(is/*, "UTF-8"*/));
        final int CHARS_PER_PAGE = 5000; //counting spaces
        StringBuilder builder = new StringBuilder(CHARS_PER_PAGE);
        try {
            for(String line=br.readLine(); line!=null; line=br.readLine()) {
                builder.append(line);
                builder.append('\n');
            }
        } 
        catch (IOException ignore) { }

        String text = builder.toString();
        System.out.println(text);
    }
}

或者,如果你想要原始速度,我会根据Paul de Vrieze的建议(避免使用StringWriter(内部使用StringBuffer))提出一个变体:

public class StringFromFileFast {
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
        InputStream is = StringFromFileFast.class.getResourceAsStream("file.txt");
        InputStreamReader input = new InputStreamReader(is/*, "UTF-8"*/);
        final int CHARS_PER_PAGE = 5000; //counting spaces
        final char[] buffer = new char[CHARS_PER_PAGE];
        StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
        try {
            for(int read = input.read(buffer, 0, buffer.length);
                    read != -1;
                    read = input.read(buffer, 0, buffer.length)) {
                output.append(buffer, 0, read);
            }
        } catch (IOException ignore) { }

        String text = output.toString();
        System.out.println(text);
    }
}