ログイン新規登録
Qiita Conference2024年4月17日水曜日から19日金曜日16時半から

Qiita最大規模のテックカンファレンス参加申し込み受付中!

基調講演ゲスト(敬称略)

牛尾剛、 市谷聡啓、 けんすう、 ゆる言語学ラジオ、 田中邦裕、小城久美子、 飯沼亜紀

0

【C】基数変換プログロムを自作してみた

投稿日

基数変換プログラムとは

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関数のみで実装してみた。競技プログラミングなどでライブラリとして作成しておきたくなったら、もう少し簡潔にまとめるつもりです。

新規登録して、もっと便利にQiitaを使ってみよう

  1. あなたにマッチした記事をお届けします
  2. 便利な情報をあとで効率的に読み返せます
  3. ダークテーマを利用できます
ログインすると使える機能について

コメント

この記事にコメントはありません。

いいね以上の気持ちはコメントで

Qiita Conference 2024 4月17日(水)~19(金)開催!

Qiita Conferenceは、Qiita最大規模のテックカンファレンスです!

基調講演ゲスト(敬称略)

牛尾剛、 市谷聡啓、 けんすう、 ゆる言語学ラジオ、 田中邦裕、小城久美子、 飯沼亜紀

0

新規登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる

ソーシャルアカウントでログイン・新規登録

メールアドレスでログイン・新規登録