Convert long integer(decimal) to base 36 string (strtol inverted function in C)

前端 未结 3 2114
执笔经年
执笔经年 2020-12-20 22:11

I can use the strtol function for turning a base36 based value (saved as a string) into a long int:

long int val = strtol(\"ABCZX12         


        
相关标签:
3条回答
  • 2020-12-20 22:34

    One of the missing attributes of this "Convert long integer to base 36 string" is string management.

    The below suffers from a potential buffer overflow when destination is too small.

    char *long_to_string(char *destination, long num, int base);
    

    (Assuming 32-bit long) Consider the overflow of below as the resultant string should be "-10000000000000000000000000000000", which needs 34 bytes to encode the string.

    char buffer[33];                     // Too small
    long_to_string(buffer, LONG_MIN, 2); // Oops! 
    

    An alternative would pass in the buffer size and then provide some sort of error signaling when the buffer is too small.

    char* longtostr(char *dest, size_t size, long a, int base)
    

    Since C99, code instead could use a compound literal to provide the needed space - without calling code trying to compute the needed size nor explicitly allocate the buffer.

    The returned string pointer from TO_BASE(long x, int base) is valid until the end of the block.

    #include <assert.h>
    #include <limits.h>
    #define TO_BASE_N (sizeof(long)*CHAR_BIT + 2)
    
    //                               v. compound literal .v
    #define TO_BASE(x, b) my_to_base((char [TO_BASE_N]){""}, (x), (b))
    
    char *my_to_base(char *buf, long a, int base) {
      assert(base >= 2 && base <= 36);
      long i = a < 0 ? a : -a;  // use the negative side - this handle _MIN, _MAX nicely
      char *s = &buf[TO_BASE_N - 1];
      *s = '\0';
      do {
        s--;
        *s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[-(i % base)];
        i /= base;
      } while (i);
    
      if (a < 0) {
        s--;
        *s = '-';
      }
    
      // Could add memmove here to move the used buffer to the beginning
    
      return s;
    }
    
    #include <limits.h>
    #include <stdio.h>
    int main(void) {
      long ip1 = 0x01020304;
      long ip2 = 0x05060708;
      long ip3 = LONG_MIN;
      printf("%s %s\n", TO_BASE(ip1, 16), TO_BASE(ip2, 16), TO_BASE(ip3, 16));
      printf("%s %s\n", TO_BASE(ip1, 2), TO_BASE(ip2, 2), TO_BASE(ip3, 2));
      puts(TO_BASE(ip1, 8));
      puts(TO_BASE(ip1, 36));
      puts(TO_BASE(ip3, 10));
    }
    
    0 讨论(0)
  • 2020-12-20 22:35

    There's no standard function for this. You'll need to write your own one.

    Usage example: https://godbolt.org/z/MhRcNA

    const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    
    char *reverse(char *str)
    {
        char *end = str;
        char *start = str;
    
        if(!str || !*str) return str;
        while(*(end + 1)) end++;
        while(end > start)
        {
            int ch = *end;
            *end-- = *start;
            *start++ = ch;
        }
        return str;
    }
    
    char *tostring(char *buff, long long num, int base)
    {
        int sign = num < 0;
        char *savedbuff = buff;
    
        if(base < 2 || base >= sizeof(digits)) return NULL;
        if(buff)
        {
            do
            {   
                *buff++ = digits[abs(num % base)];
                num /= base;
            }while(num);
            if(sign)
            {
                *buff++ = '-';
            }
            *buff = 0;
            reverse(savedbuff);
        }
        return savedbuff;
    }
    
    0 讨论(0)
  • 2020-12-20 22:48

    Here is another option with no need for source array of charaters, but less portable since not all character encodings have contiguous alphabetic characters, for example EBCDIC. Test HERE

    #include <stdio.h> 
    #include <string.h> 
    #include <stdlib.h>
    #include <stdbool.h>
    #include <limits.h>
    
    char get_chars(long long value) 
    { 
        if (value >= 0 && value <= 9) 
            return value + '0'; 
        else
            return value - 10 + 'A'; 
    } 
    
    void reverse_string(char *str) 
    { 
        int len = strlen(str); 
    
        for (int i = 0; i < len/2; i++) 
        { 
            char temp = str[i]; 
            str[i] = str[len - i - 1]; 
            str[len - i - 1] = temp; 
        } 
    } 
    
    char* convert_to_base(char *res, int base, long long input) 
    { 
        bool flag = 0;
        int index = 0;   
        if(input < 0){  
           input = llabs(input);
           flag = 1;
        }
        else if(input == 0){
           res[index++] = '0';
           res[index] = '\0';
           return res;
        }      
           while(input > 0)
           {          
              res[index++] = get_chars(input % base); 
              input /= base; 
        } 
        if(flag){
            res[index++] = '-';
        }       
        res[index] = '\0';   
        reverse_string(res); 
        return res; 
    } 
    
    int main() {  
        long long input = 0;
        printf("** Integer to Base-36 **\n ");
        printf("Enter a valid number: ");
        scanf("%lld", &input); 
        if(input >= LLONG_MAX && input <= LLONG_MIN){
          printf("Invalid number");  
          return 0; 
        }
    
        int base = 36; 
        char res[100]; 
        printf("%lld -> %s\n", input, convert_to_base(res, base, input));
    
        return 0; 
    }
    
    0 讨论(0)
提交回复
热议问题