Преобразовать длинное целое число (десятичное) в строку с основанием 36 (обратная функция strtol в C) - PullRequest
4 голосов
/ 15 января 2020

Я могу использовать функцию strtol для преобразования значения на основе base36 (сохраненного в виде строки) в long int:

long int val = strtol("ABCZX123", 0, 36);

Существует ли стандартная функция, которая позволяет инвертировать это? То есть, чтобы преобразовать переменную long int val в строку base36, чтобы снова получить "ABCZX123"?

Ответы [ 3 ]

4 голосов
/ 15 января 2020

Для этого нет стандартной функции. Вам нужно написать свой собственный.

Пример использования: 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;
}
1 голос
/ 16 января 2020

Одним из отсутствующих атрибутов этого «Преобразовать длинное целое число в строку base 36» является string management.

Приведенное ниже значение страдает от потенциального переполнения буфера, когда destination слишком мало .

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

(Предполагается, что 32-битная длина) Рассмотрим переполнение ниже, поскольку результирующая строка должна быть "-10000000000000000000000000000000", для кодировки которой требуется 34 байта.

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

Альтернатива передаст размер буфера и затем предоставит некоторую сигнализацию об ошибках, когда буфер слишком мал.

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

Начиная с C99, вместо кода может использовать составной литерал для предоставления необходимого пространства - без вызова кода, который пытается вычислить необходимый размер или явно выделить буфер.

Возвращенный указатель строки из TO_BASE(long x, int base) действителен до конца блока.

#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));
}
1 голос
/ 15 января 2020

Вот еще один вариант, не требующий исходного массива символов, но менее переносимый, поскольку не все кодировки имеют непрерывные символы алфавита c, например, EBCDI C. Тест ЗДЕСЬ

#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; 
}
...