Created
November 15, 2018 07:14
-
-
Save nomunomu0504/fa9f21e089b0f4052f80ac7ef0e48a59 to your computer and use it in GitHub Desktop.
C言語のprintf/scanfを自作して再現してみる
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// main.cpp | |
// Created by NomuraHiroki on 2016/11/06. | |
// | |
#include "main.h" | |
int main (void) { | |
double d; | |
int i; | |
og_scanf("%d, %lf", &i, &d); | |
og_printf("You int is %d", i); | |
og_printf("You double is %lf ", d); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// main.h | |
// Created by NomuraHiroki on 2016/11/06. | |
// | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <math.h> | |
#include "og_printf.h" | |
#include "og_scanf.h" | |
#include "og_function.h" | |
#define STDIN 0 // 標準入力のファイルデスクリプタ番号 | |
#define STDOUT 1 // 標準入力のファイルデスクリプタ番号 | |
#define BLOCK 100 // 配列初期化時のサイズ指定 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// og_function.h | |
// Created by NomuraHiroki on 2016/11/06. | |
// | |
#ifndef OG_FUNCTION_OG_FUNCTION_H | |
#define OG_FUNCTION_OG_FUNCTION_H | |
#include "main.h" | |
/* 最期に入る改行を取り除く関数 */ | |
void trim(char *str) { | |
char *p; | |
// 改行のあるポインタを代入 | |
p = strchr(str, '\n'); | |
if(p != NULL) { // 改行が見つかったなら | |
// ヌル文字に変更する | |
*p = '\0'; | |
} | |
} | |
// atoiの自作 | |
int my_atoi( char* str ) { | |
int res = 0; | |
for (int i = 0; str[i] != '\0' && '0' <= str[i] && str[i] <= '9'; i++) { | |
res = res * 10 + ( str[i] - '0' ); | |
} | |
return res; | |
} | |
#endif // OG_FUNCTION_OG_FUNCTION_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// og_printf.h | |
// Created by NomuraHiroki on 2016/11/06. | |
// | |
#ifndef OG_PRINTF_OG_PRINTF_H | |
#define OG_PRINTF_OG_PRINTF_H | |
#include "main.h" | |
void og_printf(const char *str1, ...) { | |
// 作業用にstr1をコピー | |
char *str = new char[BLOCK]; | |
strcpy(str, str1); | |
// strから%を見つけた? | |
bool parsent_flag = false; | |
// 可変引数 | |
va_list args; | |
va_start(args , str); | |
// sscanf実行後のデータ保管 | |
char *result = NULL; | |
// resultのtemp領域 | |
char *res_temp = NULL; | |
// 取り出し開始位置 | |
int before_int = 0; | |
int i = 0; | |
/* | |
* 入力された文字列から「%」を見つけ出し、その後に書かれている文字から | |
* 表示する型を決定し、それに合わせて可変長引数から表示する変数を取ってくる。 | |
*/ | |
// 1文字ずつ読み出す | |
for (i = 0; i < strlen(str); i++) { | |
// 取り出した文字が'%'ならば | |
if (str[i] == '%') { | |
parsent_flag = true; | |
// 変数代入format以外の文字列の保存 | |
char *task = new char[i - before_int]; | |
for (int k = before_int; k < i; k++) { | |
task[k - before_int] = str[ k ]; | |
} | |
// 取り出し開始位置を指定 | |
before_int = i; | |
/* | |
* 必ず新たに、char/char*変数を作るときに、最後に'\0'を代入しておく | |
* そうじゃないと、write()や変数代入時などにエラーになる。 | |
*/ | |
// その次の文字を取り出し確認する | |
switch ( str[i + 1] ) { | |
// int型 | |
case 'd': { | |
// 変数の取得 | |
int value = va_arg(args, int); | |
// 数値の桁数の取得 | |
int numlen = (int) log10((double) value); | |
// 表示用文字列の定義 | |
char *strint = new char[numlen + 2]; | |
// 文末にヌル文字を代入 | |
strint[numlen + 2] = '\0'; | |
// 一桁ずつ文字列化 | |
for (int j = 0; j < numlen + 1; j++) { | |
int temp = value % 10; | |
// int型の数字をchar型の数字に変換し、char配列に代入 | |
strint[numlen - j] = (char) (temp + 48); | |
value /= 10; | |
} | |
// 桁数の取得 | |
size_t length = strlen(strint); | |
// 現在格納されてる文字列 + これから格納する文字列で配列を確保 | |
char *temp = new char[strlen(task) + length]; | |
strcat(temp, task); | |
strcat(temp, strint); | |
// temp領域の作成 | |
char *stack = new char[100]; | |
// ヌル文字の代入 | |
stack[0] = '\0'; | |
// strからtask + format分を切り取る | |
strncpy(stack, str + strlen(task) + 2 , strlen(str) - strlen(task) - 2); | |
// 残りのstr分を元に戻す | |
strcpy(str, stack); | |
// forのcountを0に戻す | |
i = 0; | |
// 文字列開始位置も0に戻す | |
before_int = 0; | |
// temp領域の削除 | |
delete[] stack; | |
if ( result == NULL ) | |
{ | |
// 完成形char配列へ代入 | |
result = strdup(temp); | |
}else { | |
res_temp = new char[ strlen(result) + length + strlen(task) + 1 ]; | |
// 前回の結果, 中間文字列, 今回の結果をそれぞれ連結 | |
strcat(res_temp, result); | |
strcat(res_temp, task); | |
strcat(res_temp, strint); | |
delete[] result; | |
// 最終配列に代入 | |
result = strdup(res_temp); | |
// temp領域を削除 | |
delete[] res_temp; | |
} | |
/* | |
* write(STDOUT, &strint, (size_t)(strlen(strint))); でうまく動作しなかった。 | |
* - そもそもwrite()の第2引数は「char*」型なので「&strint」は「char**」型になってしまって、動作しない。 | |
* なので、「strint」を引数にすることで「char*」になりうまく動作した。 | |
* | |
*/ | |
break; | |
} | |
/* | |
* char, char*型を1つの関数で | |
*/ | |
case 's': | |
case 'c': { | |
// 一文字表示ならば | |
if ( str[i + 1] == 'c' ) { | |
/* | |
* 可変長変数から変数を取得 | |
* charはintに格上げする | |
*/ | |
char value = va_arg(args, int); | |
/* | |
* 一文字表示用配列の用意 | |
* 配列の最後には必ずヌル文字を入れる | |
*/ | |
char *res = new char[2]; | |
// 可変長変数からの文字列の一文字目を代入 | |
res[0] = value; | |
res[1] = '\0'; | |
// 桁数の取得 | |
size_t length = strlen(res); | |
// 現在格納されてる文字列 + これから格納する文字列で配列を確保 | |
char *temp = new char[strlen(task) + length]; | |
strcat(temp, task); | |
strcat(temp, res); | |
// temp領域の作成 | |
char *stack = new char[100]; | |
// ヌル文字の代入 | |
stack[0] = '\0'; | |
// strからtask + format分を切り取る | |
strncpy(stack, str + strlen(task) + 2 , strlen(str) - strlen(task) - 2); | |
// 残りのstr分を元に戻す | |
strcpy(str, stack); | |
// forのcountを0に戻す | |
i = 0; | |
// 文字列開始位置も0に戻す | |
before_int = 0; | |
// temp領域の削除 | |
delete[] stack; | |
if ( result == NULL ) { | |
// 完成形char配列へ代入 | |
result = strdup(temp); | |
}else { | |
res_temp = new char[ strlen(result) + length + strlen(task) + 1 ]; | |
// 前回の結果, 中間文字列, 今回の結果をそれぞれ連結 | |
strcat(res_temp, result); | |
strcat(res_temp, task); | |
strcat(res_temp, res); | |
delete[] result; | |
// 最終配列に代入 | |
result = strdup(res_temp); | |
// temp領域を削除 | |
delete[] res_temp; | |
} | |
}else { // 文字列表示ならば | |
// 可変長変数から変数を取得 | |
char *value = va_arg(args, char*); | |
// 桁数の取得 | |
size_t length = strlen(value); | |
// 現在格納されてる文字列 + これから格納する文字列で配列を確保 | |
char *temp = new char[strlen(task) + length]; | |
strcat(temp, task); | |
strcat(temp, value); | |
// temp領域の作成 | |
char *stack = new char[100]; | |
// ヌル文字の代入 | |
stack[0] = '\0'; | |
// strからtask + format分を切り取る | |
strncpy(stack, str + strlen(task) + 2 , strlen(str) - strlen(task) - 1); | |
// 残りのstr分を元に戻す | |
strcpy(str, stack); | |
// forのcountを0に戻す | |
i = 0; | |
// 文字列開始位置も0に戻す | |
before_int = 0; | |
// temp領域の削除 | |
delete[] stack; | |
if ( result == NULL ) { | |
// 完成形char配列へ代入 | |
result = strdup(temp); | |
}else { | |
res_temp = new char[ strlen(result) + length + strlen(task) + 1 ]; | |
// 前回の結果, 中間文字列, 今回の結果をそれぞれ連結 | |
strcat(res_temp, result); | |
strcat(res_temp, task); | |
strcat(res_temp, value); | |
delete[] result; | |
// 最終配列に代入 | |
result = strdup(res_temp); | |
// temp領域を削除 | |
delete[] res_temp; | |
} | |
} | |
break; | |
} | |
/* | |
* double, float型 | |
* double -> char* の型変換の方法を模索中(解決) | |
* - sprintf() -> なぜか BAD_ACCESS | |
* - ただ単に、代入先を初期化してなかったからだった... | |
*/ | |
case 'l': | |
case 'f': | |
if ( str[i + 2] == 'f' || str[i + 1] == 'f' ) { | |
// 文字列代入用変数の定義 | |
char *value = new char[100]; | |
// 可変長変数からdouble型で値を取得 | |
double res = va_arg(args, double); | |
// とりあえずtemp変数へ代入 | |
sprintf(value, "%lf", res); | |
// 桁数の取得 | |
size_t length = strlen(value); | |
// 現在格納されてる文字列 + これから格納する文字列で配列を確保 | |
char *temp = new char[strlen(task) + length]; | |
strcat(temp, task); | |
strcat(temp, value); | |
// temp領域の作成 | |
char *stack = new char[100]; | |
// ヌル文字の代入 | |
stack[0] = '\0'; | |
// strからtask + format分を切り取る | |
strncpy(stack, str + strlen(task) + 3 , strlen(str) - strlen(task) - 2); | |
// 残りのstr分を元に戻す | |
strcpy(str, stack); | |
// forのcountを0に戻す | |
i = 0; | |
// 文字列開始位置も0に戻す | |
before_int = 0; | |
// temp領域の削除 | |
delete[] stack; | |
if ( result == NULL ) { | |
// 完成形char配列へ代入 | |
result = strdup(temp); | |
}else { | |
res_temp = new char[ strlen(result) + length + strlen(task) + 1 ]; | |
// 前回の結果, 中間文字列, 今回の結果をそれぞれ連結 | |
strcat(res_temp, result); | |
strcat(res_temp, task); | |
strcat(res_temp, value); | |
delete[] result; | |
// 最終配列に代入 | |
result = strdup(res_temp); | |
// temp領域を削除 | |
delete[] res_temp; | |
} | |
// temp変数領域を削除 | |
delete[] value; | |
} | |
break; | |
// 上記以外(数字とか) | |
default: { | |
if ( str[i + 1] == '.' ) { | |
if ('0' <= str[i + 2] && str[i + 2] <= '9') { | |
switch (str[i + 3]) { | |
/* | |
* double, float型 | |
* double -> char* の型変換の方法を模索中(解決) | |
* - sprintf() -> なぜか BAD_ACCESS | |
* - ただ単に、代入先を初期化してなかったからだった... | |
*/ | |
case 'l': | |
case 'f': | |
if (str[i + 3] == 'f' || str[i + 4] == 'f') { | |
// 文字列代入用変数の定義 | |
char *value = new char[100]; | |
// 可変長変数からdouble型で値を取得 | |
double res = va_arg(args, double); | |
// 出力フォーマット文字列を作成 | |
char *format = new char[6]; | |
// 末端にヌル文字を代入 | |
format[5] = '\0'; | |
// 小数点以下の表示桁数を指定するための文字列を作成 | |
char *single_char = new char[2]; | |
single_char[0] = str[i + 2]; | |
// 末端にヌル文字を代入 | |
single_char[1] = '\0'; | |
// int型の数字をchar型の数字に変換し、char配列に代入 | |
strcat(format, "%."); | |
// 表示文字数を末尾に連結 | |
strcat(format, single_char); | |
// double型で表示する | |
strcat(format, "lf"); | |
// とりあえずtemp変数へ代入 | |
sprintf(value, format, res); | |
// 桁数の取得 | |
size_t length = strlen(value); | |
// 現在格納されてる文字列 + これから格納する文字列で配列を確保 | |
char *temp = new char[strlen(task) + length]; | |
strcat(temp, task); | |
strcat(temp, value); | |
// temp領域の作成 | |
char *stack = new char[100]; | |
// ヌル文字の代入 | |
stack[0] = '\0'; | |
// strからtask + format分を切り取る | |
strncpy(stack, str + strlen(task) + 2, strlen(str) - strlen(task) - 2); | |
// 残りのstr分を元に戻す | |
strcpy(str, stack); | |
// forのcountを0に戻す | |
i = 0; | |
// 文字列開始位置も0に戻す | |
before_int = 0; | |
// temp領域の削除 | |
delete[] stack; | |
if (result == NULL) { | |
// 完成形char配列へ代入 | |
result = strdup(temp); | |
} else { | |
res_temp = new char[strlen(result) + length + strlen(task) + 1]; | |
// 前回の結果, 中間文字列, 今回の結果をそれぞれ連結 | |
strcat(res_temp, result); | |
strcat(res_temp, task); | |
strcat(res_temp, value); | |
delete[] result; | |
// 最終配列に代入 | |
result = strdup(res_temp); | |
// temp領域を削除 | |
delete[] res_temp; | |
} | |
// temp変数領域を削除 | |
delete[] value; | |
} | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
// 表示桁数の取得 | |
else if ( '0' <= str[i + 1] && str[i + 1] <= '9' ) { | |
// char型の数字をint型の数字に変換 | |
int num_range = str[i + 1] - 48; | |
switch ( str[i + 2] ) { | |
case 'd': { | |
// 変数の取得 | |
int value = va_arg(args, int); | |
// 表示する桁数分の配列を確保 | |
char *res = new char[num_range + 1]; | |
res[num_range + 1] = '\0'; | |
// 数値の桁数の取得 | |
int numlen = (int) log10((double) value); | |
// 表示する桁数分の文字列作成 | |
for (int j = 0; j < num_range; j++) { | |
if ( value == 0 ) break; | |
// n桁目以降の値を取得する | |
int temp = value % (int) pow(10, numlen - j); | |
// n桁目の値を取得する | |
int i_res = (value - temp) / (int) pow(10, numlen - j); | |
// n桁目の値を代入する | |
res[j] = (char) (i_res + 48); | |
// n桁目の代入を終了したらその分を引く | |
value -= (int) pow(10, numlen - j) * i_res; | |
} | |
// 桁数の取得 | |
size_t length = strlen(res); | |
// 現在格納されてる文字列 + これから格納する文字列で配列を確保 | |
char *temp = new char[strlen(task) + length]; | |
strcat(temp, task); | |
// temp領域の作成 | |
char *stack = new char[100]; | |
// ヌル文字の代入 | |
stack[0] = '\0'; | |
// strからtask + format分を切り取る | |
strncpy(stack, str + strlen(task) , strlen(str) - strlen(task)); | |
// 残りのstr分を元に戻す | |
strcpy(str, stack); | |
// forのcountを0に戻す | |
i = 0; | |
// 文字列開始位置も0に戻す | |
before_int = 0; | |
// temp領域の削除 | |
delete[] stack; | |
if ( result == NULL ) { | |
// 完成形char配列へ代入 | |
result = strdup(temp); | |
}else { | |
res_temp = new char[ strlen(result) + length + strlen(task) + 1 ]; | |
// 前回の結果, 中間文字列, 今回の結果をそれぞれ連結 | |
strcat(res_temp, result); | |
strcat(res_temp, task); | |
strcat(res_temp, res); | |
delete[] result; | |
// 最終配列に代入 | |
result = strdup(res_temp); | |
// temp領域を削除 | |
delete[] res_temp; | |
} | |
} | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
} | |
} | |
// 代入文がない場合そのまま表示 | |
if ( result == NULL && !parsent_flag ) { | |
result = strdup(str); | |
} | |
strcat(result, "\n"); | |
write(STDOUT, result, (size_t) strlen(result)); | |
// 可変変数のポインタをNULLに | |
va_end(args); | |
} | |
#endif //OG_PRINTF_OG_PRINTF_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// og_scanf.h | |
// Created by NomuraHiroki on 2016/11/06. | |
// | |
#ifndef OG_SCANF_OG_SCANF_H | |
#define OG_SCANF_OG_SCANF_H | |
#include "main.h" | |
void og_scanf(const char *str, ...) { | |
// 可変引数 | |
va_list args; | |
va_start(args , str); | |
/* | |
* 区切り文字「群」を指定 | |
* [, ] - カンマ、空白で分割する | |
*/ | |
const char *format = ", "; | |
// 一度分割したかどうか | |
bool cut_flag = false; | |
for( int i = 0; i < strlen(str); i++ ) { | |
if ( str[i] == '%' ) { | |
switch (str[i + 1]) { | |
case 'd': { | |
// 可変長変数から変数を取得 | |
int *res = va_arg(args, int*); | |
if (!cut_flag) | |
{ | |
cut_flag = true; | |
// 一時的に入力された文字を格納 | |
char *temp = new char[BLOCK]; | |
read(STDIN, temp, BLOCK); | |
// 文末の改行を削除 | |
trim(temp); | |
char *val = strtok(temp, format); | |
*res = my_atoi(val); | |
} | |
else | |
{ | |
char *val = strtok(NULL, format); | |
*res = my_atoi(val); | |
} | |
break; | |
} | |
case 'l': | |
case 'f': { | |
if ( str[i + 1] == 'f' || str[i + 2] == 'f' ) { | |
// 可変長変数から変数を取得 | |
double *res = va_arg(args, double*); | |
if (!cut_flag) | |
{ | |
cut_flag = true; | |
// 一時的に入力された文字を格納 | |
char *temp = new char[BLOCK]; | |
read(STDIN, temp, BLOCK); | |
// 文末の改行を削除 | |
trim(temp); | |
char *val = strtok(temp, format); | |
*res = atof(val); | |
} | |
else | |
{ | |
char *val = strtok(NULL, format); | |
*res = atof(val); | |
} | |
break; | |
} | |
} | |
case 's': { | |
// 可変長変数から変数を取得 | |
char *res = va_arg(args, char*); | |
if (!cut_flag) | |
{ | |
cut_flag = true; | |
// 一時的に入力された文字を格納 | |
char *temp = new char[BLOCK]; | |
read(STDIN, temp, BLOCK); | |
// 文末の改行を削除 | |
trim(temp); | |
// 先頭にヌル文字代入 | |
res[0] = '\0'; | |
// 入力された文字をヌル文字前に連結 | |
char *value = strtok(temp, format); | |
strcat(res, value); | |
} | |
else | |
{ | |
char *value = strtok(NULL, format); | |
strcat(res, value); | |
} | |
break; | |
} | |
case 'c': { | |
// 可変長変数から変数を取得 | |
char *value = va_arg(args, char*); | |
if (!cut_flag) | |
{ | |
cut_flag = true; | |
// 一時的に入力された文字を格納 | |
char *temp = new char[BLOCK]; | |
read(STDIN, temp, BLOCK); | |
// 文末の改行を削除 | |
trim(temp); | |
char *res = strtok(temp, format); | |
*value = res[0]; | |
// 入力された文字をヌル文字前に連結 | |
} | |
else | |
{ | |
char *res = strtok(NULL, format); | |
*value = res[0]; | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
} | |
} | |
va_end(args); | |
} | |
#endif //OG_SCANF_OG_SCANF_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment