Number/String conversion

Rambings, rants, insight, wisdom, and idotic thoughts, depending on the time.

Moderator: punt

Post Reply
punt
VIP
Posts: 244
Joined: Wed Mar 24, 2004 7:46 pm
Has thanked: 0
Been thanked: 9 times

Number/String conversion

Post by punt »

Another common need in c++ is converting strings to their binary number and back. There are several methods available, but many of these have varying calling sequences, and some don't have options to add the radix indicator, pad the number with zeros, etc. In addition, many of these will use their auto radix determination to if a number leads with a 0, it is an octal format.

Ideally there would be available a common, consistent way to transfer strings back to integers (to include bool). C++17 makes it pretty simple to do. This uses the std::from_chars, std::to_chars in c++17. It allows one to specify if a radix should be prepended to the number (if converting to string format, and allows one to specify the minimum characters the string should take (and prepend zeros if less). It support hex, decimal, octal, and binary. Bool are converted to true/false.

I have also included the other string methods just for a complete picture:
#ifndef StringUtility_hpp
#define StringUtility_hpp
#include <iostream>

#include <string>
#include <type_traits>
#include <array>
#include <vector>
#include <charconv>
#include <utility>

using namespace std::string_literals ;

namespace strutil {
    extern const std::string whitespace ;
    constexpr auto max_number_size = 10000;
    //=====================================================================
    // Trim utilities
    //=====================================================================
   
    //========================================================================
    std::string ltrim(const std::string &value) ;
    //========================================================================
    std::string rtrim(const std::string &value) ;
    //========================================================================
    std::string trim(const std::string &value);
    //========================================================================
    std::string simplify(const std::string &value);
   
    //=====================================================================
    // Case utilities
    //=====================================================================
   
    //========================================================================
    std::string upper(const std::string &value) ;
    //========================================================================
    std::string lower(const std::string &value) ;
   
    //=====================================================================
    // String manipulation (remove remaining based on separator, split,parse)
    //=====================================================================
    //========================================================================
    std::string strip(const std::string &value , const std::string &sep="//",bool pack = true);
    //========================================================================
    std::pair<std::string,std::string> split(const std::string &value , const std::string &sep=",");
    //========================================================================
    std::vector<std::string> parse(const std::string& value,
                         const std::string& sep = ",");

    //=====================================================================
    // Number to/from String utilities
    //=====================================================================

    //========================================================================
    //Handle bool conversion to string
    template <typename T>
    typename std::enable_if< std::is_same_v<T,bool>,std::string>::type
    numtostr(T value, int base = 10, bool base_indicator = false,
           int min_char=0){
        if (value) {
            return "true"s;
        }
        else {
            return "false"s;
        }
       
    }
   
    //========================================================================
    //Handle integer conversion to string
    template <typename T>
    typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T,bool>,std::string>::type
    numtostr(T value, int base = 10, bool base_indicator = false,
           int min_char=0){
        std::array<char,max_number_size> str ;
        if (base == 0) {
            base = 10 ;
        }
        if (auto [pc,ec] = std::to_chars(str.data(), str.data()+str.size(), value,base) ;
            ec == std::errc()){
            // Ok was successful, now make the string ;
            auto rvalue = std::string(str.data(),pc) ;
            // Check if we need to pad with zeros
            if (rvalue.size()< min_char) {
                auto head = std::string(min_char - rvalue.size(),'0') ;
                rvalue = head+rvalue ;
            }
            // do we append the base?
            if (base_indicator) {
                switch (base) {
                    case 10:
                        rvalue = "0d"s + rvalue ;
                        break;
                    case 16:
                        rvalue = "0x"s + rvalue ;
                        break;
                    case 2:
                        rvalue = "0b"s + rvalue ;
                        break;
                    case 8:
                        rvalue = "0o"s + rvalue ;
                        break;
                       
                    default:
                        break;
                }
            }
            return rvalue ;
        }
        else {
            // Failed to convert
            return "0"s;
        }
    }
   
    //========================================================================
    //Handle string conversion to integer
    template <typename T>
    typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T,bool>,void>::type
    strtonum(const std::string &input, T &rvalue, int base = 10){
        auto temp = trim(lower(input));
        if (temp == "true"s){
            rvalue = static_cast<T>(1) ;
            return;
        }
        else if (temp == "false"s){
            rvalue = static_cast<T>(0) ;
            return ;
        }
        // we are now to "true" Integers
        int embedded_base = 0 ;
        if (temp.find("0x"s) == 0){
            embedded_base = 16;
            temp = temp.substr(2);
        }
        else if (temp.find("0d"s) == 0){
            embedded_base = 10 ;
            temp = temp.substr(2);
        }
        else if (temp.find("0o"s) == 0){
            embedded_base = 8 ;
            temp = temp.substr(2);
        }
        else if (temp.find("0b"s) == 0){

            embedded_base = 2 ;
            temp = temp.substr(2);
        }
       
        if (embedded_base != 0) {
            base = embedded_base;
           
        }
        if (base == 0){
            base = 10 ;
        }
       
       
        if (auto [msg,ec] = std::from_chars(temp.data(), temp.data()+temp.size(), rvalue, base);
            ec == std::errc()){
           
            return  ;
        }
        else if (ec == std::errc::invalid_argument)
        {
            //std::cout << "That isn't a number.\n";
            rvalue = 0;
            return  ;
        }
        else if (ec == std::errc::result_out_of_range)
        {
            //std::cout << "This number is larger than an supplied value.\n";
            rvalue = 0;
            return  ;
        }
        else {
            // Unknown error
            rvalue = 0;
            return ;
        }
    }

    //========================================================================
    //Handle string conversion to bool
    template <typename T>
    typename std::enable_if<std::is_integral_v<T> && std::is_same_v<T,bool>,void>::type
    strtonum(const std::string &input, T &rvalue, int base = 10){
        auto temp = trim(lower(input));
        if (temp == "true"s){
            rvalue = true ;
            return;
        }
        else if (temp == "false"s) {
            rvalue =  false ;
            return;
        }
        else {
            long long dummy = 0 ;
            strtonum(input,dummy,base) ;
            rvalue =  static_cast<bool>(dummy) ;
            return;
        }
    }

    //=======================================================================
    // Make some convience functions from strtonum to return a value (allows
    // user to use auto value = func(), versus having to define value ahead

    // Char methods
    unsigned char strtouc(const std::string &input,int base = 10){
        unsigned char value ;
        strtonum(input,value,base) ;
        return value ;
    }
    char strtoc(const std::string &input,int base = 10){
        char value ;
        strtonum(input,value,base) ;
        return value ;
    }

    // Short methods
    unsigned short strtous(const std::string &input,int base = 10){
        unsigned short value ;
        strtonum(input,value,base) ;
        return value ;
    }
    short strtos(const std::string &input,int base = 10){
        short value ;
        strtonum(input,value,base) ;
        return value ;
    }

    // int methods
    unsigned int strtoui(const std::string &input,int base = 10){
        unsigned int value ;
        strtonum(input,value,base) ;
        return value ;
    }
    int strtoi(const std::string &input,int base = 10){
        int value ;
        strtonum(input,value,base) ;
        return value ;
    }

    // long methods
    unsigned long strtoul(const std::string &input,int base = 10){
        unsigned long value ;
        strtonum(input,value,base) ;
        return value ;
    }
    long strtol(const std::string &input,int base = 10){
        long value ;
        strtonum(input,value,base) ;
        return value ;
    }

    // Long Long methods
    unsigned long long strtoull(const std::string &input,int base = 10){
        unsigned long long value ;
        strtonum(input,value,base) ;
        return value ;
    }
    long long strtoll(const std::string &input,int base = 10){
        long long value ;
        strtonum(input,value,base) ;
        return value ;
    }
   
    // Bool
    bool strtob(const std::string &input,int base = 10){
        bool value ;
        strtonum(input,value,base) ;
        return value ;
    }

}

#endif /* StringUtility_hpp */
and for the implementation:
 #include "StringUtility.hpp"

#include <algorithm>
#include <cctype>
#include <charconv>

namespace strutil {
    //=====================================================================
    // Define the constant "whitespace"
    // Whitespace is space,tab,vertical tab,feed,newline,carriage return
    const std::string whitespace = " \t\v\f\n\r";

    //=====================================================================
    // Trim utilities
    //=====================================================================
    //=====================================================================
    std::string rtrim(const std::string &value) {
        auto loc = value.find_last_not_of(whitespace) + 1;
        return value.substr(0,loc);
    }

    //=====================================================================
    std::string ltrim(const std::string &value) {
        auto loc = value.find_first_not_of(whitespace) ;
        if (loc == std::string::npos){
            return std::string();
        }
        return value.substr(loc);

    }
   
    //=====================================================================
    std::string trim(const std::string &value){
        return ltrim(rtrim(value));
    }

    //=====================================================================
    std::string simplify(const std::string &value) {
        auto temp = trim(value) ;
        auto append = false ;
        std::string rvalue ;
        for (auto &ch : temp){
            if (!std::isspace(static_cast<int>(ch))){
                append = true ;
                rvalue += ch;
            }
            else {
                if (append) {
                    rvalue += ' ';
                    append = false ;
                }
            }
        }
        return rvalue ;
    }
    //=====================================================================
    // Case utilities
    //=====================================================================
    //=====================================================================
    std::string upper(const std::string& value) {
        auto input = value ;
        std::transform(input.begin(), input.end(), input.begin(),
                   [](unsigned char c){ return std::toupper(c); } // correct
                   );
        return input;

    }
   
    //=====================================================================
    std::string lower(const std::string& value) {
        auto input = value ;
        std::transform(input.begin(), input.end(), input.begin(),
                   [](unsigned char c){ return std::tolower(c); } // correct
                   );
        return input;

    }
   
    //=====================================================================
    // String manipulation (remove remaining based on separator, split)
    //=====================================================================
    //=====================================================================
    std::string strip(const std::string &value , const std::string &sep,bool pack){
        auto loc = value.find(sep) ;
        auto rvalue =""s;
        if (loc == std::string::npos){
            rvalue = value;
        }
        else {
            rvalue = value.substr(0,loc) ;
        }
        if (pack) {
            rvalue = trim(rvalue) ;
        }
        return rvalue ;
    }
   
    //=====================================================================
    std::pair<std::string,std::string> split(const std::string &value , const std::string &sep){
       
        auto size = sep.size() ;
        auto first = value ;
        auto second = ""s ;
       
        auto loc = value.find(sep) ;
        if (loc != std::string::npos){
            first = rtrim(value.substr(0,loc)) ;
            loc = loc+size ;
            if (loc < value.size()){
                second = ltrim(value.substr(loc));
            }
        }
       
        return std::make_pair(first,second);
    }
/*
    //=====================================================================
    std::vector<std::string> parse(const std::string& value,const std::string& sep){
        std::vector<std::string> rvalue ;
        auto temp = value ;
        do {
            auto [first,second] = split(temp,",");
            rvalue.push_back(first);
            temp = second ;
        } while (!temp.empty());

       
        return rvalue;
    }

*/

    std::vector<std::string> parse(const std::string& value, const std::string& sep) {
        std::vector<std::string> rvalue ;
       
        std::vector<std::string::size_type> offsets;
        std::string::size_type loc = 0 ;
        do {
            loc = value.find(sep,loc) ;
            if (loc != std::string::npos) {
                offsets.push_back(loc) ;
                loc = loc+sep.size() ;
   
            }
        }while (loc < value.size()) ;
        loc = 0 ;
        for (auto index : offsets){
            rvalue.push_back(trim(value.substr(loc,index-loc)));
            loc = index + sep.size() ;
           
        }
        if (loc < value.size()){
            rvalue.push_back(trim(value.substr(loc)));
        }
       
        return rvalue ;
    }

}
These users thanked the author punt for the post:
Xuri
Post Reply