DEV Community

Augusta Ehihebolo
Augusta Ehihebolo

Posted on • Updated on

Master String Manipulation in Solidity

Introduction

String manipulation in Solidity is not as straightforward as it is in most other programming languages, like JavaScript, Python, Java, etc.; with the entire notion/belief that a good background in JavaScript gives you an edge in Solidity, it sucks and becomes frustrating when you can't just use the (+) sign to concatenate strings in Solidity, like ("a"+"b") or do other simple string manipulations the way we are used to in most other programming languages.

What is string in programming?

String is a piece of text or an array/sequence/collection of alphanumeric characters enclosed within a single or double quote mark, such as 'Welcome!' or "number 3". It is one of the essential concepts in computer programming.

String is a data type used in most computer languages to store data values of ordered sequences of characters. It can be saved in a variable, entered by the user (input), or displayed on the screen (output).

Manipulation of string to deliver information or change its contents is usually possible and relatively simple. The following are some of the things we can do with string:

  • Check length of a string
  • Check character position in a string
  • Concatenate(join strings to form another word) strings
  • Compare if two strings are the same
  • Change letters in a string from uppercase to lowercase and verse versa
  • Reverse a string

String in solidity

String in Solidity has some similarities with bytes; both are dynamic array types, which means they can hold data of any size. Each element of a bytes variable is a single byte, whereas each element of a string variable is a string character. it does not support functions to:

  • Get string length
  • Concatenate strings
  • Convert from upper case to lower case
  • Join two strings
  • Reverse string
  • Compare two strings
  • To achieve any of the above mentioned, you have to do some sorts of manual manipulations.

In Solidity, string is an array of UTF-8 characters that can be converted to bytes easily, and this is the primary way of manipulating string. It's vital to note that UTF-8 characters don't always match bytes perfectly. Although the conversion will be accurate in either direction, there is no direct relationship between each byte index and the associated string index. It's also worth remembering that string literals are limited to readable ASCII characters.

Project setup

Go to Remix IDE, in your workspace create a new file called stringMan.sol.
Go to your workspace compiler, set the compiler to the latest local version or any version greater than or equal to 0.8.0, and enable the auto compiler so that your work compiles automatically on the go, this will allow you to spot an error easily, that's one great thing I really love about Remix IDE.
Now, open the stringMan.sol file and set the SPDX-Licence-Identifier: UNLICENSED and Pragma solidity ^0.8.0.0;. Awesome! Our environment is all set.

Find the length of a string

Now that we have successfully set up our project environment, let’s start getting our hands dirty!
In your stringMan.sol file, create a contract called StringManipulation, inside the StringManipulation contract, create a string called str1, then set str1 to the string literal "Congratulations".

// SPDX-License-Identifier: UNLICENSED;
pragma solidity ^0.8.0.0;

contract StringManipulation{
    string public str1 = "Congratulations";
}
Enter fullscreen mode Exit fullscreen mode

Moving on, let’s create a function that gets the value of our string variable str1, a function that gets the length of the string, and a function that sets the value of str1 to a new string.

First, declare a function called getStr that returns the string str1, then declare another function called getStrLength that returns the length of the string, and declare another function called setStr that sets the string “str1” to a new string of your choice.

// SPDX-License-Identifier: UNLICENSED;
pragma solidity ^0.8.0.0;

contract StringManipulation {
    //create string
    string public str1 = "Congratulations";

   //get string function
    function getStr() public view returns(string memory) {
        return str1;
    }

    //get string length function
    function getStrLength() public view returns(uint) {
        bytes memory strB = bytes(str1);
        return strB.length;
    }

    //set string function
    function setStr(string memory str) public {
        str1 = str;
    }
}
Enter fullscreen mode Exit fullscreen mode

In the above code, the first function i.e the get string function will return the value of the string variable “str1” which is congratulations, for the second function i.e the get string length function, since we cannot get the length of a string directly, we converted the string str1 to byte and return the length, and for the last function which is the set string function, we set the string variable str1 to a new string variable str which allows us to set the string str1 to any string of our choice.

Concatenate strings

Now that we know how to find our way around getting the length of a string, let’s move on to string concatenation.
In your StringManipulation contract, create another function called concateString, let it take 2 arguments str1 and str2, and return the concatenated string.

// SPDX-License-Identifier: UNLICENSED;
pragma solidity ^0.8.0.0;

contract StringManipulation{
    string public str1 = "Congratulations";

//get string function
    function getStr() public view returns(string memory) {
        return str1;
    }

    //set string function
    function setStr(string memory str) public {
        str1 = str;
    }

    //get string length function
    function getStrLength() public view returns(uint) {
        bytes memory strB = bytes(str1);
        return strB.length;
    }


    //concatenate strings function
    function concatString(
        string memory _str1, 
        string memory _str2
        ) public pure returns(string memory) {
    return string (abi.encodePacked(_str1,_str2));
    }
}
Enter fullscreen mode Exit fullscreen mode

In solidity, you cannot concatenate strings simply by using the plus sign like in JavaScript and some other languages, e.g. (str1 + str2), instead, you call the abi.encodedPacked() function and passed in the strings to be concatenated.

Compare strings

With string concatenation successfully out of the way, you should drink a glass of juice and take a deep breath. having done that, let’s keep on moving…
Head over to your StringManipulation contract again, create another function called compareStr, let it take two(2) string arguments str1 and str2 and return a boolean.

// SPDX-License-Identifier: UNLICENSED;
pragma solidity ^0.8.0.0;

contract StringManipulation{
    string public str1 = "Congratulations";

   //get string function
    function getStr() public view returns(string memory) {
        return str1;
    }

    //set string function
    function setStr(string memory str) public {
        str1 = str;
    }

    //get string length function
    function getStrLength() public view returns(uint) {
        bytes memory strB = bytes(str1);
        return strB.length;
    }

    //concatenate strings function
    function concatString(
        string memory _str1, 
        string memory _str2
        ) public pure returns(string memory) {
    return string (abi.encodePacked(_str1,_str2));
    }

    //compare strings function
    function compare(
        string memory _str1, 
        string memory _str2
        ) external pure returns(bool){
            return keccak256(abi.encodePacked(_str1)
            ) == keccak256(abi.encodePacked(_str2));
            }
}
Enter fullscreen mode Exit fullscreen mode

Just as in concatenation, to compare 2 strings in solidity, we called the keccak256 function and passed in the abi.encoded function which takes the argument, and then compares.

Reverse string

I am sure you are feeling like a boss already, you should be because it’s not easy getting here. Without any delay, let’s see how we can reverse a string.
Still, inside your StringManipulation smart contract, create a function called reverseStr that takes a string argument _str and returns a string(reversed of the string that was passed in)

// SPDX-License-Identifier: UNLICENSED;
pragma solidity ^0.8.0.0;

contract StringManipulation{
    string public str1 = "Congratulations";

   //get string function
    function getStr() public view returns(string memory) {
        return str1;
    }

    //set string function
    function setStr(string memory str) public {
        str1 = str;
    }

    //get string length function
    function getStrLength() public view returns(uint) {
        bytes memory strB = bytes(str1);
        return strB.length;
    }

    //concatenate strings function
    function concatString(
        string memory _str1, 
        string memory _str2
        ) public pure returns(string memory) {
    return string (abi.encodePacked(_str1,_str2));
    }

    //compare strings function
    function compareStr(
        string memory _str1, 
        string memory _str2
        ) external pure returns(bool){
            return keccak256(abi.encodePacked(_str1)
            ) == keccak256(abi.encodePacked(_str2));
            }

       //reverse string function
    function reverseStr(string memory _str) external pure returns(string memory){
        bytes memory baseByte = bytes(_str);
        string memory tempValue = new string(baseByte.length);
        bytes memory reverse = bytes(tempValue); 
        for(uint i = 0; i < baseByte.length; i++) {
             reverse[baseByte.length - i - 1] = baseByte[i];
             }
             return string(reverse);
             }
}
Enter fullscreen mode Exit fullscreen mode

Here, we converted the string to byte, get the instance of the string length, and simply looped through from the back.

Convert string to lower case

Moving further, let’s see how we can convert a string from uppercase to lowercase. In your StringManipulation contract, create a function called toLowerCase that takes a string argument _str and returns a string(the lowercase of the passed in string)

// SPDX-License-Identifier: UNLICENSED;
pragma solidity ^0.8.0.0;

contract StringManipulation{
    string public str1 = "Congratulations";

   //get string function
    function getStr() public view returns(string memory) {
        return str1;
    }

    //set string function
    function setStr(string memory str) public {
        str1 = str;
    }

    //get string length function
    function getStrLength() public view returns(uint) {
        bytes memory strB = bytes(str1);
        return strB.length;
    }

    //concatenate strings function
    function concatString(
        string memory _str1, 
        string memory _str2
        ) public pure returns(string memory) {
    return string (abi.encodePacked(_str1,_str2));
    }

    //compare strings function
    function compareStr(
        string memory _str1, 
        string memory _str2
        ) external pure returns(bool){
            return keccak256(abi.encodePacked(_str1)
            ) == keccak256(abi.encodePacked(_str2));
            }

       //reverse string function
    function reverseStr(string memory _str) external pure returns(string memory){
        bytes memory baseByte = bytes(_str);
        string memory tempValue = new string(baseByte.length);
        bytes memory reverse = bytes(tempValue); 
        for(uint i = 0; i < baseByte.length; i++) {
             reverse[baseByte.length - i - 1] = baseByte[i];
             }
             return string(reverse);
             } 

    //convert string to lowercase
    function toLowerCase(string memory _str) public pure returns (string memory) {
        bytes memory bString = bytes(_str);
        bytes memory bLowerCase = new bytes(bString.length);
        for (uint j = 0; j < bString.length; j++) {
            // Uppercase character...
            if ((uint8(bString[j]) >= 65) && (uint8(bString[j]) <= 90)) {
                // So we add 32 to make it lowercase
                bLowerCase[j] = bytes1(uint8(bString[j]) + 32);
            } else {
                bLowerCase[j] = bString[j];
            }
        }
        return string(bLowerCase);
    }
}
Enter fullscreen mode Exit fullscreen mode

For the toLowerCase function, we first converted the string to byte, then get the instance of the length; since the ASCII values of A - Z are from 65 - 90, and that of a - z, from 97 - 112, we looped through to check if each character is within the range of 65 - 90(A - Z), add 32 if found to be true, to convert it to a lower case else return it that way.

Test functions

If everything went well, your smart contract should be successfully compiled by now since it’s in auto compile. If you didn’t enable auto compile, now is the time to compile your smart contract, hit the compile button to compile it, and head over to deploy once compiled.

Now that you have successfully deployed your smart contract, let’s test all the functions to ensure they are working as they should:

Get string, set string and get string length functions:

Get string and get string length

Set string, get string and get string length

Concatenate strings function:

Compare strings function:

It returns true if the words are the same

it returns false if the two(2) words are different

Reverse string function:

toLowerCase function:

Conclusion:

It is not very efficient to manipulate strings in solidity, hence it is not recommended. Using strings in solidity is quite expensive since it consumes too much gas thereby costing too much gas fee; if you can, avoid string manipulation and attempt to replace it with byte instead.

Also, Stings in Solidity accepts the backslash character ( \ ) as an escape character and the backslash plus n characters (\ + n) (\n) as a new line character, just like JavaScript.

Reference

Solidity 0. 8. 13 Documentation
Etheruem/Solidity toLower() equivalent
String manipulation in Solidity

Get the complete code here

Let's connect:
Linkedin
Twitter

Top comments (0)