如何在Solidity中将uint转换为字符串?

42
在 Solidity 中,我是否可以将我的整型转换为字符串?
例子:
pragma solidity ^0.4.4;

contract someContract {

    uint i;

    function test() pure returns (string) {

      return "Here and Now is Happiness!";

    }

    function love() pure returns(string) {

        i = i +1;

        return "I love " + functionname(i) + " persons" ;
    }



}

什么是函数名?谢谢!

8个回答

91

1
这应该是被接受的答案。 - EdwardG
同意,这应该被接受。 - James
3
完美的答案。另外,要连接字符串,+ 是无法使用的,但是可以使用 return string(abi.encodePacked("I love ", Strings.toString(i), " persons")); 来实现。 - Christophe Vidal
现在你可以使用 string.concat(s1, s2) 了 @ChristopheVidal - Viktor Stefanov

40

关于 Solidity 0.8.0 的更新:

https://github.com/provable-things/ethereum-api/blob/master/provableAPI_0.6.sol 中的 uint2str() 函数已经过时,不再可用。但是这里有更新后的代码,使用 solidity 0.8.0: (上一个版本中存在溢出错误,但 solidity <0.8.0 忽略了该错误,因为它并不影响答案,但现在会引发错误) byte 也已更改为 bytes1,+、-、* 等操作与 SafeMath 库中的操作方式相同。

function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
            return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len;
        while (_i != 0) {
            k = k-1;
            uint8 temp = (48 + uint8(_i - _i / 10 * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }

2
这个版本适用于Solidity 0.8.1!请点赞。 :) - Senju
你能修改一下,加上小数参数吗?例如,如果 uint 是 20000,小数位是 3,则结果应该是 20。 - pandichef
感谢更新。 - 0xD1x0n

8
这里的两篇文章都提供了回复:

https://ethereum.stackexchange.com/questions/10811/solidity-concatenate-uint-into-a-string

https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int

function uintToString(uint v) constant returns (string str) {
        uint maxlength = 100;
        bytes memory reversed = new bytes(maxlength);
        uint i = 0;
        while (v != 0) {
            uint remainder = v % 10;
            v = v / 10;
            reversed[i++] = byte(48 + remainder);
        }
        bytes memory s = new bytes(i + 1);
        for (uint j = 0; j <= i; j++) {
            s[j] = reversed[i - j];
        }
        str = string(s);
    }

问候


2
我测试时发现这个变量有缺陷,Oraclize的解决方案 https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol 可能更好:function uint2str(uint i) internal pure returns (string){ if (i == 0) return "0"; uint j = i; uint length; while (j != 0){ length++; j /= 10; } bytes memory bstr = new bytes(length); uint k = length - 1; while (i != 0){ bstr[k--] = byte(48 + i % 10); i /= 10; } return string(bstr); } - Dmitriy Vinokurov
1
是的,我同意答案中的版本对我来说不起作用,但在这里Dmitriy指出的https://github.com/provable-things/ethereum-api/blob/master/oraclizeAPI_0.5.sol#L1045提供的那个可以工作。 - Diiaablo
另外,solc 0.7.0 不支持“byte(48 + remainder)”。无法将 uint256 显式转换为 bytes1。 - Buzz Moschetti

5

Concrete_Buddhas的回答在Solidity 0.8.0中不可用。这是修订版:

 function uint2str(
  uint256 _i
)
  internal
  pure
  returns (string memory str)
{
  if (_i == 0)
  {
    return "0";
  }
  uint256 j = _i;
  uint256 length;
  while (j != 0)
  {
    length++;
    j /= 10;
  }
  bytes memory bstr = new bytes(length);
  uint256 k = length;
  j = _i;
  while (j != 0)
  {
    bstr[--k] = bytes1(uint8(48 + j % 10));
    j /= 10;
  }
  str = string(bstr);
}

2
如果您的 uint 是从最初将字符串编码为 uint 而来的
string(abi.encode(myUint))

1

评论区提供的可证明的事情代码对我有用,但我的代码检查器发出了一个警告,即:"uintToStr": 避免将值赋给函数参数。[security/no-assign-params]。下面稍微更改了原始代码以纠正这个问题(重新分配参数_i到另一个名为number的变量):

    /// @notice converts number to string
    /// @dev source: https://github.com/provable-things/ethereum-api/blob/master/oraclizeAPI_0.5.sol#L1045
    /// @param _i integer to convert
    /// @return _uintAsString
    function uintToStr(uint _i) internal pure returns (string memory _uintAsString) {
        uint number = _i;
        if (number == 0) {
            return "0";
        }
        uint j = number;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len - 1;
        while (number != 0) {
            bstr[k--] = byte(uint8(48 + number % 10));
            number /= 10;
        }
        return string(bstr);
    }

0
如果您需要选择性地转换为科学计数法,例如为了更紧凑的数字表示,这里是一个修改后的版本以实现此目的:
function uintToString(uint v, bool scientific) public pure returns (string memory str) {

    if (v == 0) {
        return "0";
    }

    uint maxlength = 100;
    bytes memory reversed = new bytes(maxlength);
    uint i = 0;
    
    while (v != 0) {
        uint remainder = v % 10;
        v = v / 10;
        reversed[i++] = byte(uint8(48 + remainder));
    }

    uint zeros = 0;
    if (scientific) {
        for (uint k = 0; k < i; k++) {
            if (reversed[k] == '0') {
                zeros++;
            } else {
                break;
            }
        }
    }

    uint len = i - (zeros > 2 ? zeros : 0);
    bytes memory s = new bytes(len);
    for (uint j = 0; j < len; j++) {
        s[j] = reversed[i - j - 1];
    }

    str = string(s);

    if (scientific && zeros > 2) {
        str = string(abi.encodePacked(s, "e", uintToString(zeros, false)));
    }
}

一些单元测试:
function testUintToString() public {

    Assert.equal(Utils.uintToString(0, true), '0', '0');
    Assert.equal(Utils.uintToString(1, true), '1', '1');
    Assert.equal(Utils.uintToString(123, true), '123', '123');
    Assert.equal(Utils.uintToString(107680546035, true), '107680546035', '107680546035');
    Assert.equal(Utils.uintToString(1e9, true), '1e9', '1e9');
    Assert.equal(Utils.uintToString(1 ether, true), '1e18', '1 ether');
    Assert.equal(Utils.uintToString(550e8, true), '55e9', '55e9');
}

上面的代码片段与Solidity 0.6.0兼容。


0

虽然被接受的答案看起来是正确的,但在处理大数字时效率相当低下。以下是我会如何处理:

function itoa32 (uint x) private pure returns (uint y) {
    unchecked {
        require (x < 1e32);
        y = 0x3030303030303030303030303030303030303030303030303030303030303030;
        y += x % 10; x /= 10;
        y += x % 10 << 8; x /= 10;
        y += x % 10 << 16; x /= 10;
        y += x % 10 << 24; x /= 10;
        y += x % 10 << 32; x /= 10;
        y += x % 10 << 40; x /= 10;
        y += x % 10 << 48; x /= 10;
        y += x % 10 << 56; x /= 10;
        y += x % 10 << 64; x /= 10;
        y += x % 10 << 72; x /= 10;
        y += x % 10 << 80; x /= 10;
        y += x % 10 << 88; x /= 10;
        y += x % 10 << 96; x /= 10;
        y += x % 10 << 104; x /= 10;
        y += x % 10 << 112; x /= 10;
        y += x % 10 << 120; x /= 10;
        y += x % 10 << 128; x /= 10;
        y += x % 10 << 136; x /= 10;
        y += x % 10 << 144; x /= 10;
        y += x % 10 << 152; x /= 10;
        y += x % 10 << 160; x /= 10;
        y += x % 10 << 168; x /= 10;
        y += x % 10 << 176; x /= 10;
        y += x % 10 << 184; x /= 10;
        y += x % 10 << 192; x /= 10;
        y += x % 10 << 200; x /= 10;
        y += x % 10 << 208; x /= 10;
        y += x % 10 << 216; x /= 10;
        y += x % 10 << 224; x /= 10;
        y += x % 10 << 232; x /= 10;
        y += x % 10 << 240; x /= 10;
        y += x % 10 << 248;
    }
}

function itoa (uint x) internal pure returns (string memory s) {
    unchecked {
        if (x == 0) return "0";
        else {
            uint c1 = itoa32 (x % 1e32);
            x /= 1e32;
            if (x == 0) s = string (abi.encode (c1));
            else {
                uint c2 = itoa32 (x % 1e32);
                x /= 1e32;
                if (x == 0) {
                    s = string (abi.encode (c2, c1));
                    c1 = c2;
                } else {
                    uint c3 = itoa32 (x);
                    s = string (abi.encode (c3, c2, c1));
                    c1 = c3;
                }
            }
            uint z = 0;
            if (c1 >> 128 == 0x30303030303030303030303030303030) { c1 <<= 128; z += 16; }
            if (c1 >> 192 == 0x3030303030303030) { c1 <<= 64; z += 8; }
            if (c1 >> 224 == 0x30303030) { c1 <<= 32; z += 4; }
            if (c1 >> 240 == 0x3030) { c1 <<= 16; z += 2; }
            if (c1 >> 248 == 0x30) { z += 1; }
            assembly {
                let l := mload (s)
                s := add (s, z)
                mstore (s, sub (l, z))
            }
        }
    }
}

解释

itoa32函数将小于10^32的数字转换为32位数字,如果需要,则用零填充。

itoa 函数调用 itoa32 最多三次来转换任意的256位数,然后连接结果并删除前导零。它使用二分查找来确定要删除的前导零的确切数量,并从原始字符串中原地删除前导零。


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