基数変換プログラムとは
n進数で表された文字列を、別の指定されたn進数に変換して文字列として出力するプログラム。
例えば、"0123456789abcdef"の16進数で表された「a0」を2進数である"01"で表すと、「1111011」となる。
必要な機能
基数変換プログラムに必要な機能は、大きく分けると以下の2つである。
・n進数の文字を10進数に変換する(str to int)
・10進数の数字をn進数に変換する(int to str)
プログラムの全体像
#include <unistd.h>
#include <stdlib.h>
long ft_base_check(char *base);
int ft_len(char *str);
int ft_len_num(long nbr);
int ft_len_digits(long num, char *base);
char *ft_from_deci(int nbr, char *base);
int ft_strcmp(char *s1, char *s2);
int ft_strlen_base(char *str, char *base);
long ft_str_len(char *str, char *base);
void ft_to_deci(char *str, char *base, long *ans, long tmp);
int ft_atoi_base(char *str, char *base);
int ft_to_decimal(char *nbr, char *base_from);
long ft_base_check(char *base)
{
long i;
long j;
long count;
count = 0;
while (base[count])
{
if (base[count] == '+' || base[count] == '-'
|| base[count] == ' ' || (base[count] >= 9 && base[count] <= 13))
return (-1);
count++;
}
i = 0;
while (i < count - 1)
{
j = i + 1;
while (j < count)
{
if (base[i] == base[j])
return (-1);
j++;
}
i++;
}
return (count);
}
int ft_len(char *str)
{
int count;
count = 0;
while (str[count])
{
if (str[count] == '+' || str[count] == '-' )
return (-1);
count++;
}
return (count);
}
int ft_len_num(long nbr)
{
int count = 0;
if (nbr == 0)
return (1);
while (nbr != 0)
{
nbr /= 10;
count++;
}
return (count);
}
int ft_len_digits(long num, char *base)
{
int len;
len = 0;
while (num >= ft_len(base))
{
num /= ft_len(base);
len++;
}
return (len);
}
char *ft_from_deci(int nbr, char *base)
{
long num;
int len;
int index;
char *result;
num = nbr;
if (num < 0)
num = -num;
len = 1 + ft_len_digits(num, base);
if (nbr < 0)
len++;
result = (char *)malloc(sizeof(char) * (len + 1));
if (result == NULL)
return NULL;
index = len - 1;
while (num >= ft_len(base))
{
result[index--] = base[num % ft_len(base)];
num /= ft_len(base);
}
result[index] = base[num];
if (nbr < 0)
result[0] = '-';
result[len] = '\0';
return result;
}
int ft_strcmp(char *s1, char *s2)
{
while (*s1 != '\0' || *s2 != '\0')
{
if (*s1 != *s2)
{
return (*s1 - *s2);
}
s1++;
s2++;
}
return (0);
}
int ft_strlen_base(char *str, char *base)
{
int i = 0;
int j = 0;
int count = 0;
while (str[i] != '\0' && count == i)
{
j = 0;
while (base[j] != '\0')
{
if (base[j] == str[i])
{
count++;
break;
}
j++;
}
i++;
}
return (count);
}
long ft_str_len(char *str, char *base)
{
long count;
long j;
count = 0;
while (str[count])
{
j = 0;
while (base[j])
{
if (str[count] == base[j])
{
count++;
break ;
}
j++;
}
if (!base[j])
break ;
}
return (count);
}
void ft_to_deci(char *str, char *base, long *ans, long tmp)
{
long i;
long j;
long str_len;
i = 0;
while (str[i])
{
j = 0;
str_len = ft_str_len(str, base);
while (base[j])
{
if (str[i] == base[j])
{
tmp = j;
while (--str_len > i)
tmp *= ft_base_check(base);
*ans += tmp;
i++;
break ;
}
j++;
}
if (!base[j])
return ;
}
}
int ft_atoi_base(char *str, char *base)
{
long i;
long sign;
long ans;
char *tmp;
i = 0;
ans = 0;
sign = 1;
if (ft_base_check(base) == -1)
return (0);
while ((str[i] >= 9 && str[i] <= 13) || str[i] == ' ')
i++;
while (str[i] == '+' || str[i] == '-')
{
if (str[i] == '-')
sign *= -1;
i++;
}
tmp = str + i;
ft_to_deci(tmp, base, &ans, 0);
return ((int)ans * sign);
}
int ft_to_decimal(char *nbr, char *base_from)
{
int i;
int j;
int sign;
char *str;
sign = 1;
i = 0;
while ((nbr[i] >= 9 && nbr[i] <= 13) || nbr[i] == ' ')
i++;
while (nbr[i] == '+' || nbr[i] == '-')
{
if (nbr[i] == '-')
sign *= -1;
i++;
}
nbr += i;
str = (char *)malloc(sizeof(char) * (ft_strlen_base(nbr, base_from) + 1));
j = -1;
while (++j < ft_strlen_base(nbr, base_from))
str[j] = nbr[j];
return (sign * ft_atoi_base(str, base_from));
}
char *ft_convert_base(char *nbr, char *base_from, char *base_to)
{
char *result;
int num;
if (!nbr || ft_base_check(base_from) < 2 || ft_base_check(base_to) < 2)
return (0);
num = ft_to_decimal(nbr, base_from);
result = ft_from_deci(num, base_to);
return (result);
}
それぞれの関数の説明
strcmp
与えられた文字列を比較する。全く同じ時だけ0を返し、そのほかの場合は辞書順での差分を返す。
int ft_strcmp(char *s1, char *s2)
{
while (*s1 != '\0' || *s2 != '\0')
{
if (*s1 != *s2)
{
return (*s1 - *s2);
}
s1++;
s2++;
}
return (0);
}
strlen_base
ベースシステムとして与えられた文字列に当てはまる文字列が何文字分あるかをカウント。(前から何文字分が数を表している文字列かを調べている)
int ft_strlen_base(char *str, char *base)
{
int i = 0;
int j = 0;
int count = 0;
while (str[i] != '\0' && count == i)
{
j = 0;
while (base[j] != '\0')
{
if (base[j] == str[i])
{
count++;
break;
}
j++;
}
i++;
}
return (count);
}
str_len
上のstrlen_base関数と全く同じ。作成した後に気づいた。笑
long ft_str_len(char *str, char *base)
{
long count;
long j;
count = 0;
while (str[count])
{
j = 0;
while (base[j])
{
if (str[count] == base[j])
{
count++;
break ;
}
j++;
}
if (!base[j])
break ;
}
return (count);
}
to_dici
この関数は指定のベースシステムで表された文字列を、10進数に変換する関数。
ft_base_check関数でベースシステムが正しく機能するか調べている。
void ft_to_deci(char *str, char *base, long *ans, long tmp)
{
long i;
long j;
long str_len;
i = 0;
while (str[i])
{
j = 0;
str_len = ft_str_len(str, base);
while (base[j])
{
if (str[i] == base[j])
{
tmp = j;
while (--str_len > i)
tmp *= ft_base_check(base);
*ans += tmp;
i++;
break ;
}
j++;
}
if (!base[j])
return ;
}
}
atoi_base
この関数は指定のベースシステムで表された文字列を、10進数に変換する関数。
文字列に含まれる最初の数字を探して、to_deci関数に10進数への変換はお任せする。
int ft_atoi_base(char *str, char *base)
{
long i;
long sign;
long ans;
char *tmp;
i = 0;
ans = 0;
sign = 1;
if (ft_base_check(base) == -1)
return (0);
while ((str[i] >= 9 && str[i] <= 13) || str[i] == ' ')
i++;
while (str[i] == '+' || str[i] == '-')
{
if (str[i] == '-')
sign *= -1;
i++;
}
tmp = str + i;
ft_to_deci(tmp, base, &ans, 0);
return ((int)ans * sign);
}
to_decimal
int ft_to_decimal(char *nbr, char *base_from)
{
int i;
int j;
int sign;
char *str;
sign = 1;
i = 0;
while ((nbr[i] >= 9 && nbr[i] <= 13) || nbr[i] == ' ')
i++;
while (nbr[i] == '+' || nbr[i] == '-')
{
if (nbr[i] == '-')
sign *= -1;
i++;
}
nbr += i;
str = (char *)malloc(sizeof(char) * (ft_strlen_base(nbr, base_from) + 1));
j = -1;
while (++j < ft_strlen_base(nbr, base_from))
str[j] = nbr[j];
return (sign * ft_atoi_base(str, base_from));
}
convert_base
全ての基幹。
char *ft_convert_base(char *nbr, char *base_from, char *base_to)
{
char *result;
int num;
if (!nbr || ft_base_check(base_from) < 2 || ft_base_check(base_to) < 2)
return (0);
num = ft_to_decimal(nbr, base_from);
result = ft_from_deci(num, base_to);
return (result);
}
ft_base_check
ベースシステムが正しく機能するか判断する。
具体的に以下の場合を省く。
- 1文字以下の時
- "+"や"-"が含まれている時
- 重複文字がある場合
long ft_base_check(char *base)
{
long i;
long j;
long count;
count = 0;
while (base[count])
{
if (base[count] == '+' || base[count] == '-'
|| base[count] == ' ' || (base[count] >= 9 && base[count] <= 13))
return (-1);
count++;
}
i = 0;
while (i < count - 1)
{
j = i + 1;
while (j < count)
{
if (base[i] == base[j])
return (-1);
j++;
}
i++;
}
return (count);
}
len
シンプルに文字列の長さを返す。
int ft_len(char *str)
{
int count;
count = 0;
while (str[count])
{
if (str[count] == '+' || str[count] == '-' )
return (-1);
count++;
}
return (count);
}
len_num
数字が何桁か返す。
int ft_len_num(long nbr)
{
int count = 0;
if (nbr == 0)
return (1);
while (nbr != 0)
{
nbr /= 10;
count++;
}
return (count);
}
len_digits
10進数の数字を指定のベースシステムで表記したら何桁になるかを調べる関数。
int ft_len_digits(long num, char *base)
{
int len;
len = 0;
while (num >= ft_len(base))
{
num /= ft_len(base);
len++;
}
return (len);
}
from_deci
10進数から出力先のベースシステムへ変換する関数。
char *ft_from_deci(int nbr, char *base)
{
long num;
int len;
int index;
char *result;
num = nbr;
if (num < 0)
num = -num;
len = 1 + ft_len_digits(num, base);
if (nbr < 0)
len++;
result = (char *)malloc(sizeof(char) * (len + 1));
if (result == NULL)
return NULL;
index = len - 1;
while (num >= ft_len(base))
{
result[index--] = base[num % ft_len(base)];
num /= ft_len(base);
}
result[index] = base[num];
if (nbr < 0)
result[0] = '-';
result[len] = '\0';
return result;
}
まとめ
今回は基数変換プログラムをmalloc関数とwrite関数のみで実装してみた。競技プログラミングなどでライブラリとして作成しておきたくなったら、もう少し簡潔にまとめるつもりです。
コメント
いいね以上の気持ちはコメントで