可变长度的字节数组
1.string
- 字符串可以通过" "或者' '来表示字符串的值,solidity中的string不像c语言一样以\0结束。
- 它是可变长度的字节数组,为引用类型
- string字符串不能通过length方法取其长度
2.byte
- 动态字节数组,引用类型
3.string to byte
string字符串中没有提供length方法获取字符串长度,也没有提供方法修改某个索引的字节码,不过我们可以将string转换为bytes,再调用length方法获取字节长度,当然可以修改某个索引的字节码。
pragma solidity ^0.4.4;
contract C {
bytes9 public g = 0x6c697975656368756e;
string public name = "melodyluo";
function gByteLength() constant returns (uint) {
return g.length;
}
// 转换string 到 bytes, 返回字节如g的内容,一共9字节,
// 也就是一个英文字母对应一个字节
function nameBytes() constant returns (bytes) {
return bytes(name);
}
// 通过转换string to byte来调用length
function nameLength() constant returns (uint) {
return bytes(name).length;
}
function setNameFirstByteForL(bytes1 z) {
// 0x4c => "L"
bytes(name)[0] = z;
}
}
4.汉字对应的字节数
- 1个汉字对应3个字节
- 1个英文字母、数字、特殊符号 对应1个字节
pragma solidity ^0.4.4;
contract C {
string public name = "罗雪";
function nameBytes() constant returns (bytes) {
return bytes(name);//0x e7 bd 97 e9 9b aa
// 1 chinese word equals to 3 bytes
}
function nameLength() constant returns (uint) {
return bytes(name).length;
}
}
- so不要混用字母、汉字之类的
5.创建bytes字节数组
- new bytes(想要的字节长度)
- 长度可变, length
- 内容可变, name[index] = data
- 可以通过让length=0来清空bytes数组,也可以直接delete;
pragma solidity ^0.4.4;
contract C {
bytes public name = new bytes(1);
function setNameLength(uint length) {
name.length = length;
}
function nameLength() constant returns (uint) {
return name.length;
}
function setIndextyte(byte data, uint indxe){
name[indxe] = data;
}
function clearBBytes(uint len){
delete name;
}
}
6.push
- 在字节数组的最后插入("0xbb")
pragma solidity ^0.4.4;
contract C {
// 0x6c697975656368756e
// 初始化一个两个字节空间的字节数组
bytes public name = new bytes(2);
// 设置字节数组的长度
function setNameLength(uint len) {
name.length = len;
}
// 返回字节数组的长度
function nameLength() constant returns (uint) {
return name.length;
}
// 往字节数组中添加字节
function pushAByte(byte b) {
name.push(b);
}
}
-说明:当字节数组的长度只有2时,如果你通过push往里面添加了一个字节,那么它的长度将变为3,当字节数组里面有3个字节,但是你通过length方法将其长度修改为2时,字节数组中最后一个字节将被从字节数组中移除
固定长度的字节数组(byte1-byte32)
固定大小字节数组可以通过 bytes1, bytes2, bytes3, …, bytes32来进行声明。PS:byte的别名就是 byte1。
bytes1只能存储一个字节,也就是二进制8位的内容。
bytes2只能存储两个字节,也就是二进制16位的内容。
bytes3只能存储三个字节,也就是二进制24位的内容。
……
bytes32能存储三十二个字节,也就是二进制32 * 8 = 256 位的内容。
- 根据经验,在我们不确定字节数据大小的情况下,我们可以使用string或者bytes,而如果我们清楚的知道或者能够将字节书控制在bytes1 ~ bytes32,那么我们就使用bytes1 ~ bytes32,这样的话能够降低存储成本。
运算
- 比较运算符:<=, <, ==, !=, >=, >
- 位操作符:&, |, ^(异或), ~ (取反), << (左移), >> (右移)
- 注意左移与右移,如下
pragma solidity ^0.4.4;
contract test{
bytes1 b10 = 0x6c;
bytes1 b11 = 0x69;
function leftshift1() constant returns(bytes1) {
return b10 << 1;
// 0110 1100 -> 0x6c
// 1101 1000 -> 0xd8
}
function fightshift1() constant returns(bytes1) {
return b10 >> 1;
// 0110 1100 -> 0x6c
// 0011 0110 -> 0x36
}
}
- 索引访问:如果x是一个bytes I,那么可以通过x[k](0 < k < I)获取对应索引的字节,PS:x[k]是只读,不可写。
pragma solidity ^0.4.4;
contract c{
bytes9 b9 = 0x6c697975656368756e;
function readIndex5Byte() constant returns(byte){
return b9[5];
}
}
成员函数
length
- 返回字节的个数
- 只可读不可写
pragma solidity ^0.4.4;
contract C {
bytes9 public g = 0x6c697975656368756e;
function gByteLength() constant returns (uint) {
return g.length;
}
}
固定大小深度理解
- 存储的字节数不可修改(不能通过length修改)
- 内部字节不可修改
如
pragma solidity ^0.4.4;
contract C {
bytes9 name = 0x6c697975656368756e;
function setNameFirstByte(byte b) {
name[0] = b;
}
}
总结
bytesI(1 <= I <= 32)可以声明固定字节大小的字节数组变量,一旦声明,内部的字节和字节数组长度不可修改,当然可以通过索引读取(只读)对应索引的字节,或者通过length读取字节数组的字节数。
不可变字节数组
我们之前的文章中提到过如果我们清楚我们存储的字节大小,那么我们可以通过bytes1,bytes2,bytes3,bytes4,……,bytes32来声明字节数组变量,不过通过bytesI来声明的字节数组为不可变字节数组,字节不可修改,字节数组长度不可修改。
可变字节数组
我们可以通过bytes name = new bytes(length) - length为字节数组长度,来声明可变大小和可修改字节内容的可变字节数组。
固定与可变字节数组之间的转换
1.固定字节数组之间的转换
- 赋值是在最后,如
pragma solidity ^0.4.4;
contract C {
bytes10 public b1 = 0x6c;
function b_length() constant returns(uint){
return b1.length;
}
}
- byte9 -> byte2 的转换, 低位截断,后面截掉
-
byte9 -> byte12 的转换,低位补齐,后面补零
2.固定转可变
- 不能直接强转成动态数组
- 正确姿势如下
pragma solidity ^0.4.4;
contract C {
bytes9 name9 = 0x6c697975656368756e;
function transfer(bytes9) constant returns(bytes) {
bytes memory names = new bytes(name9.length);
for (uint i =0;i
3.固定不能直接转string
需要先固定转动态,动态转string
但是这种方法会有问题,如果将一个byte32(实际内容为9个字节)转string,string长度也是32,会有很多00000。这样不好,标准方法见5
pragma solidity ^0.4.4;
contract C {
bytes9 name9 = 0x6c697975656368756e;
function bytes9transfertostring(byte9 name9) constant returns(string){
bytes memory names = new bytes(name9.length);
for (uint i =0; i
4.动态字节转string
- 如果有现成的动态字节数组如names,直接强转
pragma solidity ^0.4.4;
contract C {
bytes9 name9 = 0x6c697975656368756e;
bytes names = new bytes(2);
function C(){
names[0]=0x6c;
names[1]=0x69;
}
function transfer() constant returns(string){
return string(names);
}
}
5.标准的固定字节转string方法
注意当要改变状态变量时不能用constant
pragma solidity ^0.4.4;
contract C {
function bytes32ToString(bytes32 x) constant returns (string) {
bytes memory bytesString = new bytes(32);
uint charCount = 0;
for (uint j = 0; j < 32; j++) {
byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (j = 0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
return string(bytesStringTrimmed);
}
function bytes32ArrayToString(bytes32[] data) constant returns (string) {
bytes memory bytesString = new bytes(data.length * 32);
uint urlLength;
for (uint i = 0; i< data.length; i++) {
for (uint j = 0; j < 32; j++) {
byte char = byte(bytes32(uint(data[i]) * 2 ** (8 * j)));
if (char != 0) {
bytesString[urlLength] = char;
urlLength += 1;
}
}
}
bytes memory bytesStringTrimmed = new bytes(urlLength);
for (i = 0; i < urlLength; i++) {
bytesStringTrimmed[i] = bytesString[i];
}
return string(bytesStringTrimmed);
}
}
- bytes32(uint(0x6c) * 2 ** (8 * 31));左移31位
bytes32(uint(0x6c) * 2 ** (8 * 1)); 左移1位
- 通过byte(bytes32(uint(x) * 2 ** (8 * j)))获取到的始终是第0个字节。
string本身是一个特殊的动态字节数组,所以它只能和bytes之间进行转换,不能和固定大小字节数组进行直接转换,如果是固定字节大小数组,需要将其转换为动态字节大小数组才能进行转换。