java char数组编码_关于Java:将char数组转换为byte数组然后再次返回

我正在寻找将Java char数组转换为字节数组而不创建中间String的方法,因为char数组包含密码。 我查看了几种方法,但是它们似乎都失败了:

char[] password ="password".toCharArray();

byte[] passwordBytes1 = new byte[password.length*2];

ByteBuffer.wrap(passwordBytes1).asCharBuffer().put(password);

byte[] passwordBytes2 = new byte[password.length*2];

for(int i=0; i

passwordBytes2[2*i] = (byte) ((password[i]&0xFF00)>>8);

passwordBytes2[2*i+1] = (byte) (password[i]&0x00FF);

}

String passwordAsString = new String(password);

String passwordBytes1AsString = new String(passwordBytes1);

String passwordBytes2AsString = new String(passwordBytes2);

System.out.println(passwordAsString);

System.out.println(passwordBytes1AsString);

System.out.println(passwordBytes2AsString);

assertTrue(passwordAsString.equals(passwordBytes1) || passwordAsString.equals(passwordBytes2));

断言总是失败的(并且,至关重要的是,当在生产中使用该代码时,密码被拒绝),但是print语句会打印出三次密码。 为什么passwordBytes1AsString和passwordBytes2AsString与passwordAsString不同,却显得相同? 我是否错过了空终止符之类的东西? 我怎样做才能使转换和未转换工作?

为什么要避免创建中间字符串?

Sun建议将它作为最佳实践:download.oracle.com/javase/1.5.0/docs/guide/security/jce/字符串是不可变的,因此不能像char数组一样清零-相反,您的密码在内存中徘徊, 不确定的时间。

char和byte之间的转换是字符集的编码和解码。我更喜欢在代码中尽可能地使其清晰。这实际上并不意味着额外的代码量:

Charset latin1Charset = Charset.forName("ISO-8859-1");

charBuffer = latin1Charset.decode(ByteBuffer.wrap(byteArray)); // also decode to String

byteBuffer = latin1Charset.encode(charBuffer);                 // also decode from String

在旁边:

java.nio类和java.io Reader / Writer类使用ByteBuffer和CharBuffer(它们使用byte []和char []作为后备数组)。因此,通常最好直接使用这些类。但是,您始终可以执行以下操作:

byteArray = ByteBuffer.array();  byteBuffer = ByteBuffer.wrap(byteArray);

byteBuffer.get(byteArray);       charBuffer.put(charArray);

charArray = CharBuffer.array();  charBuffer = ByteBuffer.wrap(charArray);

charBuffer.get(charArray);       charBuffer.put(charArray);

问题是您使用String(byte[])构造函数,该构造函数使用平台默认编码。这几乎从来都不是您应该做的-如果您通过" UTF-16"作为字符编码正常工作,则测试可能会通过。目前,我怀疑passwordBytes1AsString和passwordBytes2AsString的长度均为16个字符,其他每个字符均为U + 0000。

我只是尝试了(即String passwordBytes1AsString = new String(passwordBytes1,"UTF-16");)而没有任何变化。我还尝试检查字符串的长度-String.length()返回8。它将计算U + 0000个字符吗?

@Scott:尝试打印出字符串的长度以及各个字符(作为int值)。 Thatll向您显示差异之处。

原始版本和转换版本都为112,97,115,115,119,111,114,100。

@斯科特:在那种情况下,断言现在应该过去了...

刚刚注意到我在断言中对equals()使用了错误的参数。 * facepalm *您最初的假设确实是正确的。非常感谢。

原始答案

public byte[] charsToBytes(char[] chars){

Charset charset = Charset.forName("UTF-8");

ByteBuffer byteBuffer = charset.encode(CharBuffer.wrap(chars));

return Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());

}

public char[] bytesToChars(byte[] bytes){

Charset charset = Charset.forName("UTF-8");

CharBuffer charBuffer = charset.decode(ByteBuffer.wrap(bytes));

return Arrays.copyOf(charBuffer.array(), charBuffer.limit());

}

编辑以使用StandardCharsets

public byte[] charsToBytes(char[] chars)

{

final ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(chars));

return Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());

}

public char[] bytesToChars(byte[] bytes)

{

final CharBuffer charBuffer = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes));

return Arrays.copyOf(charBuffer.array(), charBuffer.limit());

}

这是StandardCharsets的JavaDoc页面。

在JavaDoc页面上注意这一点:

These charsets are guaranteed to be available on every implementation of the Java platform.

很好地使用ByteBuffer。但是,如果没有另外说明,密码是Unicode,因此StandardCharset.UTF_8优于通过将数据简化为ASCII来破坏数据。

您可以使用所需的任何字符集

我的观点是,ASCII通常是错误的。

我编辑了从US-ASCII更改为UTF-8的帖子。你是对的。想法是保持相同的编码。例如,US-ASCII的字符数不及UTF-8的字符数-没有带重音符号的字母,如果使用的是第一个UTF-8,然后使用US-ASCII,则会丢失一些信息。

将敏感数据存储在char []或byte []中后,您需要清除敏感数据,如Andrii在此处说明的用法一样stackoverflow.com/a/9670279/1582089

很好的例子。但就我而言,它可以与Charset charset = Charset.forName(" ISO-8859-1");一起使用。

我要做的是使用一个循环将其转换为字节,然后将另一个转换为char。

char[] chars ="password".toCharArray();

byte[] bytes = new byte[chars.length*2];

for(int i=0;i

bytes[i*2] = (byte) (chars[i] >> 8);

bytes[i*2+1] = (byte) chars[i];

}

char[] chars2 = new char[bytes.length/2];

for(int i=0;i

chars2[i] = (char) ((bytes[i*2] << 8) + (bytes[i*2+1] & 0xFF));

String password = new String(chars2);

如果要使用ByteBuffer和CharBuffer,请不要执行简单的.asCharBuffer(),它只是执行UTF-16(LE或BE,具体取决于您的系统-您可以使用order方法设置字节顺序)转换(由于Java字符串,因此您的char[]在内部使用此编码)。

使用Charset.forName(charsetName),然后使用其encode或decode方法或newEncoder / newDecoder。

将byte []转换为String时,还应指出编码方式(且应相同)。

这是彼得·劳瑞(Peter Lawrey)答案的延伸。为了在整个字符范围内正确进行向后(字节到字符)转换,代码应如下所示:

char[] chars = new char[bytes.length/2];

for (int i = 0; i < chars.length; i++) {

chars[i] = (char) (((bytes[i*2] & 0xff) << 8) + (bytes[i*2+1] & 0xff));

}

我们需要在使用(& 0xff)之前对字节进行"取消签名"。否则,所有可能的char值的一半将无法正确返回。例如,[0x80..0xff]范围内的字符将受到影响。

您应该使用getBytes()而不是toCharArray()

更换线

char[] password ="password".toCharArray();

byte[] password ="password".getBytes();

不要在未指定编码的情况下使用String#getBytes(),这将使您陷入各种可移植性麻烦。

不适合用例:在此示例中,此行只是获取char []的简单方法。

当您使用Java中的"从字符串获取GetBytes"时,返回结果将取决于计算机设置的默认编码(例如:StandardCharsetsUTF-8或StandardCharsets.ISO_8859_1etc ...)。

因此,每当您想要从字符串对象获取字节数时。确保提供一个编码。喜欢 :

String sample ="abc";

Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8);

让我们检查一下代码发生了什么。

在java中,名为sample的字符串由Unicode存储。字符串中的每个字符按2个字节存储。

sample :  value:"abc"   in Memory(Hex):  00 61 00 62 00 63

a -> 00 61

b -> 00 62

c -> 00 63

但是,当我们从字符串中获取字节时,

Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_8)

//result is : 61 62 63

//length: 3 bytes

Byte[] a_byte = sample .getBytes(StandardCharsets.UTF_16BE)

//result is : 00 61 00 62 00 63

//length: 6 bytes

为了获得字符串的原始字节。我们可以读取字符串的Memory并获取String的每个字节,下面是示例代码:

public static byte[] charArray2ByteArray(char[] chars){

int length = chars.length;

byte[] result = new byte[length*2+2];

int i = 0;

for(int j = 0 ;j

result[i++] = (byte)( (chars[j] & 0xFF00) >> 8 );

result[i++] = (byte)((chars[j] & 0x00FF)) ;

}

return result;

}

用途:

String sample ="abc";

//First get the chars of the String,each char has two bytes(Java).

Char[] sample_chars = sample.toCharArray();

//Get the bytes

byte[] result = charArray2ByteArray(sample_chars).

//Back to String.

//Make sure we use UTF_16BE. Because we read the memory of Unicode of

//the String from Left to right. That's the same reading

//sequece of  UTF-16BE.

String sample_back= new String(result , StandardCharsets.UTF_16BE);

这个问题没有提到getBytes,所以这并没有真正的意义。您是否要评论其他答案之一?

只想声明String的getBytes函数的用法。并且在使用new String(Byte [])时应该注意什么。希望能帮助到你。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值