如何在GWT中将字节数组转换为字符串,以及将字符串转换为字节数组?

4

String(byte[] bytes)构造函数和String.getBytes()方法在GWT JRE模拟String类中未实现。

有没有人知道有什么实现?我不想使用char[],但似乎没有其他解决方案。


你想要实现什么?你从哪里获取到 byte[] 的数据? - Peter Knego
我已经为Swing客户端实现了一种空间高效的序列化协议,现在我正在尝试将这个协议应用到GWT客户端。 - Gursel Koca
你的字节数组使用了哪种字符编码?或者你希望转换是灵活的吗? - LINEMAN78
如果我可以支持UTF-8,那就没问题了。 - Gursel Koca
3个回答

2
以下代码应该可以工作,只需指定每个字符的字节数。
public class GwtPlayground implements EntryPoint
{
    static final Logger logger = Logger.getLogger("");

    @Override
    public void onModuleLoad()
    {
        VerticalPanel loggerArea = new VerticalPanel();
        logger.addHandler(new HasWidgetsLogHandler(loggerArea));
        RootPanel.get().add(loggerArea);

        String original = new String("A" + "\uffea" + "\u00f1" + "\u00fc" + "C");

        logger.info("original = " + original);
        byte[] utfBytes = getBytes(original, 2);

        String roundTrip = getString(utfBytes, 2);
        logger.info("roundTrip = " + roundTrip);
    }

    public static byte[] getBytes(String string, int bytesPerChar)
    {
        char[] chars = string.toCharArray();
        byte[] toReturn = new byte[chars.length * bytesPerChar];
        for (int i = 0; i < chars.length; i++)
        {
            for (int j = 0; j < bytesPerChar; j++)
                toReturn[i * bytesPerChar + j] = (byte) (chars[i] >>> (8 * (bytesPerChar - 1 - j)));
        }
        return toReturn;
    }

    public static String getString(byte[] bytes, int bytesPerChar)
    {
        char[] chars = new char[bytes.length / bytesPerChar];
        for (int i = 0; i < chars.length; i++)
        {
            for (int j = 0; j < bytesPerChar; j++)
            {
                int shift = (bytesPerChar - 1 - j) * 8;
                chars[i] |= (0x000000FF << shift) & (((int) bytes[i * bytesPerChar + j]) << shift);
            }
        }
        return new String(chars);
    }
}

正如@Per Wiklander所指出的那样,这并不真正支持UTF-8。这里有一个真正的UTF-8解码器从C中移植而来此处

private static class UTF8Decoder
{
    final byte[] the_input;
    int the_index, the_length;

    protected UTF8Decoder( byte[] bytes )
    {
        super();
        this.the_input = bytes;
        this.the_index = 0;
        this.the_length = bytes.length;
    }


    /*
    Get the next byte. It returns UTF8_END if there are no more bytes.
    */
    int get()
    {
        int c;
        c = the_input[the_index] & 0xFF;
        the_index += 1;
        return c;
    }


    /*
        Get the 6-bit payload of the next continuation byte.
        Return UTF8_ERROR if it is not a contination byte.
    */
    int cont()
    {
        int c = get();
        if( (c & 0xC0) == 0x80 )
            return (c & 0x3F);
        else
            throw new IllegalArgumentException( "Failed to pass strict UTF-8" );
    }

    CharSequence getStringUTF8()
    {
        StringBuilder sb = new StringBuilder( the_input.length ); // allocate a maximum size
        while( the_index < the_length )
        {
            int c; /* the first byte of the character */
            int r; /* the result */

            c = get();
            /*
                Zero continuation (0 to 127)
            */
            if( (c & 0x80) == 0 )
            {
                sb.append( (char) c );
            }
            /*
                One continuation (128 to 2047)
            */
            else if( (c & 0xE0) == 0xC0 )
            {
                int c1 = cont();
                if( c1 >= 0 )
                {
                    r = ((c & 0x1F) << 6) | c1;
                    if( r >= 128 )
                        sb.append( (char) r );
                    else
                        throw new IllegalArgumentException();
                }
            }
            /*
            Two continuation (2048 to 55295 and 57344 to 65535)
            */
            else if( (c & 0xF0) == 0xE0 )
            {
                int c1 = cont();
                int c2 = cont();
                if( (c1 | c2) >= 0 )
                {
                    r = ((c & 0x0F) << 12) | (c1 << 6) | c2;
                    if( r >= 2048 && (r < 55296 || r > 57343) )
                        sb.append( (char) r );
                    else
                        throw new IllegalArgumentException();
                }
            }
            /*
            Three continuation (65536 to 1114111)
            */
            else if( (c & 0xF8) == 0xF0 )
            {
                int c1 = cont();
                int c2 = cont();
                int c3 = cont();
                if( (c1 | c2 | c3) >= 0 )
                    sb.append( (char) ((((c & 0x0F) << 18) | (c1 << 12) | (c2 << 6) | c3) + 65536) ); // TODO this might not work as it is being cast to a char
            }
            else
                throw new IllegalArgumentException( "Failed strict UTF8 parsing" );
        }
        return sb;
    }
}

问题在于我们不知道使用UTF-8编码一个字符需要多少字节。如果使用UTF-16,那么没问题,因为我们知道每个字符都用2个字节表示。 - Gursel Koca
UTF-8 的定义是每个字符 1 个字节。因此,8 表示 8 个比特,16 表示 16 个比特。这就是为什么我将字节数量设为可变的原因。 - LINEMAN78
1
@LINEMAN78 这只适用于映射到 ASCII 的字符。我引用 Joel Spolsky 的话:“在 UTF-8 中,从 0 到 127 的每个代码点都存储在单个字节中。只有 128 及以上的代码点使用 2、3,事实上,最多使用 6 个字节进行存储。”http://www.joelonsoftware.com/articles/Unicode.html - Per Wiklander

2
如果您在Chrome中创建了大型数组,可能会遇到 Uncaught RangeError:Maximum call stack size exceeded异常。可以修改LINEMAN78的代码以使用StringBuilder,从而避免此问题。
public static String getString(byte[] bytes, int bytesPerChar)
{
    if (bytes == null) throw new IllegalArgumentException("bytes cannot be null");
    if (bytesPerChar < 1) throw new IllegalArgumentException("bytesPerChar must be greater than 1");

    final int length = bytes.length / bytesPerChar;
    final StringBuilder retValue = new StringBuilder();

    for (int i = 0; i < length; i++)
    {
        char thisChar = 0;

        for (int j = 0; j < bytesPerChar; j++)
        {
            int shift = (bytesPerChar - 1 - j) * 8;
            thisChar |= (0x000000FF << shift) & (((int) bytes[i * bytesPerChar + j]) << shift);
        }

        retValue.append(thisChar);
    }

    return retValue.toString();
}

1

好问题。我之前没有意识到。

据我所知,只有两种主要方法可以将字节数组转换为字符串

  1. 你提到的方法
  2. 使用java.io包的绝妙方式,但无法在客户端使用

这是我的实现。我认为它可能对你有帮助

public static String convertByteArrayToString(byte[] byteArray) {
    String s = "";

    for (int i = 0; i < byteArray.length; i++) {
        s += (char) (byteArray[i]);
    }

    return s;
}

你可以测试它:
byte[] byteArray = new byte[] { 87, 79, 87, 46, 46, 46 };

System.out.println(convertByteArrayToString(byteArray));
System.out.println(new String(byteArray));

1
你的方法只适用于ascii字符,而不适用于unicode字符。我建议你阅读http://www.joelonsoftware.com/articles/Unicode.html。 - Gursel Koca

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