【跟乐乐学solidity】一 基础:字节数组/普通数组与字符串操作

前言

在solidity中,数组分为两大类,一类是字节数组,一类是普通数组。
这与java不同,java中无论是字节还是其它数据类型的数组,都是一个分类,只有固定长度和动态长度的区别。
而在solidity中字节数组下面又分为固定长度字节数组和动态长度字节数组。普通数组下面也有分为动态数组和固定数组。
其中,字节数组和string字符串类型常常结合到一起应用。

一.字节数组与字符串

一.固定长度字节数组

在学习solidity的固定长度数组之前,首先需要明白的一点是,字节是用16进制表示的

在16进制所表示的字节规则中,两个数字/字母占一个字节。
如 aa 所占的16进制字节数组长度为1。
b7ab 所占的16进制字节数组长度为2.
f9a8b7b9c521 所占的16进制字节数组长度为6.

变量类型的bytes1和bytes2等等是代表定义固定长度的字节数组。
其后面的数字代表了固定的长度,如bytes1则代表固定的数组长度为1bytes10则代表固定的数组长度为10.

需要注意的是,在solidity中,在为其字节类型的变量赋值时,值前面必须要加上’0x'才可以,否则编译失败。
而且,长度的计算标准,也是忽略了’0x’。

而string字符串等数据,可以转换为16进制(也就是字节)来进行变量值的赋值和定义。

而在智能合约中,经常会出现用字节数组代替字符串去声明变量值的用法。

pragma solidity ^0.4.16;

contract bytesArray {
    // 字节数组长度1,超过则报错     ‘aa'占长度1.
    bytes1 public length1 = 0xaa; // (二进制)10101010 --> aa  
    
    //字节数组长度2,超过则报错      ‘b7ab'占长度2.
    bytes2 public length2 = 0xb7ab;//(二进制) 1011011110101011 --> b7ab
    //字节数组长度6,超过则报错      ‘f9a8b7b9c521'占长度6.
    bytes6 public length6 = 0xf9a8b7b9c521;//(二进制) 111110011010100010110111101110011100010100100001 --> f9a8b7b9c521
    
    //字节数组长度11,超过则报错     ’68656c6c6f20776f726c64‘占长度11
    bytes11  public length10 = 0x68656c6c6f20776f726c64;// (字符串) hello world --> 68656c6c6f20776f726c64
    
    
}

在这里插入图片描述

附上两个页面:
1,在线进制转换:
https://tool.oschina.net/hexconvert

2,字符串与16进制转换:http://tools.bugscaner.com/text/zifuchuanzhuanhex.html

二. 动态长度字节数组

所谓动态长度字节数组,是指长度可以改变的数组。
比如我一开始定义一个数组的长度为3,但是后来我可以把它的长度改为5.

pragma solidity ^0.4.16;

contract bytesDynamicArray {
    
    bytes dynamicArray = new bytes(3);// 定义一个长度为3的动态字节数组成员对象。
    
    //获取长度
    function getDynamicArrayLength() view returns(uint){
        return dynamicArray.length;
    }
    
    //设置0,1,2这三个数组索引的内容。
    function setDynamicArrayData() {
        dynamicArray[0] = 0xa7;
        dynamicArray[1] = 0xbf;
        dynamicArray[2] = 0xdd;
    }
    
    //获取字节数组内容。
    function getDynamicArrayData() view returns(bytes) {
        return dynamicArray;
    }
    
    //设置字节数组成员对象的长度为5.
    //多出来的长度内容,用‘00’填空每个索引数据。
    function setDynamicArrayLength5(){
        dynamicArray.length = 5;
    }
    
    
}

上面的代码中,我们可以看到,我们先是声明了一个长度为3的字节数组成员变量对象(也就是动态字节数组),然后我们按方法顺序从上到下去执行,会充分了解到solidity的动态长度数组有哪些特性。
首先我们调用获取长度函数(getDynamicArrayLength),得到了结果3.
接着我们调用设置字节数组数据的函数(setDynamicArrayData),对数组的设置三个元素数据,
然后我们调用获取字节数组数据的函数(getDynamicArrayData),得到内容’0xa7bfdd’。
接下来,我们再运行设置数组长度的函数(setDynamicArrayLength5),把长度由原先的3设置为5.
那么问题来了,设置为5后就多来了两个数组元素,那么他们的内容是什么呢?我们设置后再运行获取数组数据的函数(getDynamicArrayData),会发现输出的内容是’0xaabfdd0000’,至此,我们得出了一个结论,当我们增加一个动态字节数组的长度时,新增加的长度元素内容,是用00来表示一个元素的。

四.字符串

字符串是啥就不说了。
但是有一点需要注意,在solidity中,如果你想取字符串中的某个字符,需要先将该字符串转为字节数组对象,然后再去根据字节数组索引去取才行。

pragma solidity ^0.4.16;

contract stringTest {
    string stringStr = "hello!world";
    
    function getStringLength() view returns(uint){
     return bytes(stringStr).length;   
    }

   //获取字符串内容
    function getStringAll() view returns(string){
        return stringStr;
    }
    
   /* 获取指定字符串 ×
   	//会报错,字符串不能直接以取索引元素的形式返回,要么只能返回全部,要么就先转为字节。
    function getStringByIndex(uint index) view returns(string){
        return stringStr[index];
    }
    */    

	//获取指定字符串内容 √
	//  要获取字符串的指定索引元素,需要先将其转为字节才行。
    function getStringByIndex(uint index) view returns(bytes1){
        return bytes(stringStr)[index];
    }
}

五.固定长度字节数组转动态长度字节数组

在solidity中,没有办法能够直接将固定长度字节数组转为动态字节数组,所以只能通过for循环来实现。

小知识:memory(值引用)关键字

我们for循环时要对动态长度字节数组的变量进行赋值,这需要我们使用‘memory’关键字将其手动声明为值引用类型才可以。
而在java中是不需要这么做的,但在solidity中,我们要用到值引用类型变量时,必须要加上’memory’才行。
与’memory’关键字相关的,则是’storage’关键字,代表指针。
参考:https://www.jianshu.com/p/874558b62572

代码如下:

pragma solidity ^0.4.16;

contract bytesFixedArrayOnBytesDynamicArray {
    bytes10 bytesVariable = 0x69206c6f766520796f75;
    
    function getBytesVariable() public view returns(bytes10){
        return bytesVariable;
    }
    
    // 将固定长度的字节数组,转换为动态长度字节数组对象。
    function getBytesForDynamicArray() public view returns(bytes){
        
     //bytes dynamicArrayBytes = new bytes(bytesVariable.length);//因为缺少 memory关键字将其定义为引用类型,因此编译失败。
        
     // memory是solidity中的关键字,意思是将改变量声明为引用类型。
     bytes memory dynamicArrayBytes = new bytes(bytesVariable.length);//编译成功

     for(uint i = 0; i < bytesVariable.length;i++){
         dynamicArrayBytes[i] = bytesVariable[i];
     }
    return dynamicArrayBytes;
    }
    
}

六.动态长度字节数组转String

动态长度字节数组转字符串相对来讲很简单,
string(动态字节数组对象)
即可。

pragma solidity ^0.4.16;

contract bytesDynamicArrayForString {
    bytes dynamicBytesArray = new bytes(3);
    function dataOfStringType() public view returns(string) {
        dynamicBytesArray[0] = 0x1d;
        dynamicBytesArray[1] = 0xcd;
        dynamicBytesArray[2] = 0x7d;
        return string(dynamicBytesArray);
    }
}

七.固定长度字节数组转String

在此前,固定长度字节数组转动态数组的话,是不能直接转的,只能通过for循环来实现,同样的,固定长度字节数组转string也是需要通过for循环来实现。
我们需要通过for循环,将固定长度字节数组转为动态字节数组,然后再将动态字节数组直接转为string对象。

pragma solidity ^0.4.16;

contract bytesFixedArrayForString {
    
    bytes10 public fixedBytesArray = 0x69206c6f766520796f75;
    
    
    function getStringOfFixedBytesArray() public view returns(string){
        
        bytes memory bytesDynamicArray = new bytes(fixedBytesArray.length);
        
        for(uint i = 0;i < bytesDynamicArray.length;i++){
            
            bytesDynamicArray[i] = fixedBytesArray[i];
            
        }
        string memory str = string(bytesDynamicArray);
        return str;
        
        //return string(bytesDynamicArray); 这样也可以。
    }
}

二.普通数组

普通数组,就是非字节类型的数组,用法和java大致相似,不多叙述了。

一.固定长度数组

定义格式:数据类型[长度] 变量名;
例如:uint[5] arrayA;

pragma solidity ^0.4.16;

contract ordinaryArray {
    
    uint[5] arrayA = [12,2,86,21,3];//具有指定数据的固定数组成员变量。
    
    uint[5] arrayB;//不具有指定数据的固定数组成员变量。
    
    uint[5] arrayC;//不具有指定数据的固定数组成员变量。
    
    //直接输出打印所有元素
    function getArrayA() public view returns(uint[5]){
        return arrayA;// 结果:12,2,86,21,3
        
    }
    
    //为所有下标索引赋值并打印
    function getArrayB() public view returns(uint[5]){
        arrayB[0] = 1;
        arrayB[1] = 3;
        arrayB[2] = 5;
        arrayB[3] = 7;
        arrayB[4] = 9;
        return arrayB;//结果:1,3,5,7,9
    }
    
    //仅为前两个下标索引赋值并打印
    function getArrayC() public view returns(uint[5]){
        arrayC[0] = 6;
        arrayC[1] = 8;
        return arrayC;//结果:6,8,0,0,0
        
    }
}

二.动态长度数组

定义格式:数据类型[] 变量名;
例如:uint[] arrayB;

pragma solidity ^0.4.16;

contract dynamicArray {
    uint[] arrayA = [1,3,5,7,9];
    uint[] arrayB;
    
    //对原先长度为5的动态数组,将其长度设为8,并赋值和打印输出。
    function setLengthAndGetDataOfArrayA() public view returns(uint[]){
        arrayA.length = 8;
        arrayA[5] = 212;
        arrayA[6] = 712;
        arrayA[7] = 612;
        return arrayA;//结果:1,3,5,7,9,212,712,612
    }
    
    //将原先未定义长度的动态数组,对其长度设为2,并赋值和打印输出。
    function setLengthAndGetDateOfArrayB() public view returns(uint[]){
        arrayB.length = 2;
        arrayB[0] = 6;
        arrayB[1] = 2;
        arrayB.push(1212);// push方法,将一个新元素添加到末端。
        return arrayB;// 结果:6,2,1212
    }
}

三.固定二维数组

格式:数据类型[二维数组长度][一维数组长度] 变量名;
例如:uint[2][5] uintDimensionArray;

pragma solidity ^0.4.16;

contract ordinaryDimensionArray {
    
    uint[2][5] uintDimensionArray = [[11,22],[22,11],[33,44],[44,33],[55,77]];
        
	//获取二维数组长度
   function getUintDimensionArrayLength() public view returns(uint){
        return uintDimensionArray.length;
    }
    
    //打印输出二维数组整个数据
    function getUintDimensionArrayAll() public view returns(uint[2][5]){
        return uintDimensionArray;
    }
    
    //打印输出二维数组中的某个元素
    function getUintDimensionArrayByIndex() public view returns(uint[2]){
        return uintDimensionArray[3];// [44,33]
    
    }
    
    //对二维数组中的某个元素赋值,并打印输出。
    function setAndGetUintDimensionArrayByIndex() public view returns(uint[2]){
        uintDimensionArray[1] = [222,111];
        
        return uintDimensionArray[1];// [222,111]
    }
    
}

四.动态二维数组

格式:数据类型[][] 变量名;
例如:string[][] strArray;

动态二维数组和固定二维数组不同的是,动态二维数组不支持直接获取所有元素内容只能获取单个索引的元素内容

pragma solidity ^0.4.16;

contract dynamicDimensionArray {
    
    string[][] strArray = [["hello","world!"],["hi","solidity!"]]; // 动态二维数组 字符串类型
    
    uint[][] uintArray = [[11,22],[33,44]];//动态二维数组 int类型
    
        /* 动态二维数组不支持直接获取所有元素。
   function getUintArrayAll() public view returns(uint[][]){
        return uintArray;// 不可以直接获取动态二维数组所有元素,编译报错:  UnimplementedFeatureError: Nested dynamic arrays not implemented here.
    }*/
    
    //获取单个指定字符串类型动态二维数组的元素。
    function getStrArrayByIndex() public view returns(string){
        return strArray[0][1];//输出结果: world
    }

	//向字符串类型动态二维数组添加元素,并打印。
    function addElementAndGetStrArrayByIndex() public view returns(string){
        strArray.push(["你好","同学"]);
        return strArray[2][0];//输出结果:你好
    }
    
	//获取int类型二维动态数组的长度
    function getUintArrayLength() public view returns(uint){
        return uintArray.length;
    }
    
    //获取单个指定int类型动态二维数组的元素。
    function getUintArrayByIndex() public view returns(uint[]){
        return uintArray[0];//输出结果: [11,22]
    }
    
    //向int类型动态二维数组添加元素,并打印。
    function addElementAndGetUintArrayByIndex() public view returns(uint[]){
        uintArray.push([1233,3214]);
        return uintArray[2];//输出结果: [1233,3214]
    }
    
}

三.数组字面(直接创建并返回)

说到‘数组字面’可能你不理解这是什么,但如果用java代码来表示,你应该就明白了。
比如,在java中,我直接 return new String {"你好"}; 代表着直接返回一个动态数组对象给调用处。
那么solidity中的‘数组字面’概念就是指这个,此处我以普通固定长度数组来做例子。

注意点:
不过需要注意的,返回值处,对于所要返回类型的位数(如uint等类型的话,则可分为16位或256位等),要和其真实返回的位数相匹配,不然会编译出错。
对于这种问题,有两个办法解决:
1.在returns的括号内,对类型的位数进行定义,例如:
function getUintFor8bit() public view returns(uint16[3])
2.retruns不再指定位数,在return处,对其位数进行强转,例如:
return [uint(257),2,3];

pragma solidity ^0.4.16;

contract returnArray {
    
   	//最大数值为251,可以用8位,也就是uint8位数
    function getUintFor8bit() public view returns(uint8[3]){
        return [251,32,3];
    }
    
    //最大数值达到了256,使用uint8位数会编译出错,必须用uint16位数
    /*function getUintFor256bit() public view returns(uint8[3]){
        return [256,32,3];
    }*/
    function getUintFor256bit() public view returns(uint16[3]){
        return [256,32,3];
    }
    
    //直接进行位数强转
    function getUintForTypeReturn() public view returns(uint[3]){//因为直接对元素值的位数进行强转,所以此处不指定类型位数。
        return [uint(257),2,3];// 通过‘uint(元素值)’的形式进行位数强转。
    }
    
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值