Skip to content

Instantly share code, notes, and snippets.

@nomunomu0504
Created November 15, 2018 07:14
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nomunomu0504/fa9f21e089b0f4052f80ac7ef0e48a59 to your computer and use it in GitHub Desktop.
Save nomunomu0504/fa9f21e089b0f4052f80ac7ef0e48a59 to your computer and use it in GitHub Desktop.
C言語のprintf/scanfを自作して再現してみる
//
// 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;
}
//
// 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 // 配列初期化時のサイズ指定
//
// 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
//
// 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
//
// 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