stox系(C++11)が使えない時の文字列→数値変換

# はじめに
stox系(C++11)が使えない環境の場合にatoi/atofの強化版を自作してみた記事になります

# 文字列→数値変換の基本
atoi/atofでは数値以外の文字列が入った場合、エラーにならず0を返すという仕様になっています

下記のように記述して出力してみました

```C++
void OutputLog( const std::string& str )
{
	try
	{
		std::cout << str << "をatoiで変換：" << atoi( str.c_str() ) << std::endl;
	}
	catch( std::exception e )
	{
		std::cout << str << "の変換に失敗！！" << std::endl;
	}
	try
	{
		std::cout << str << "をatofで変換：" << atof( str.c_str() ) << std::endl;
	}
	catch( std::exception e )
	{
		std::cout << str << "の変換に失敗！！" << std::endl;
	}

	std::cout << std::endl;
}

int main()
{
	OutputLog( "" );
	OutputLog( "0aaa" );
	OutputLog( "00aaa" );
	OutputLog( "-00aaa" );
	OutputLog( "-0aaa" );
	OutputLog( "009" );
	OutputLog( "    12345" );
	OutputLog( "　　12345" );    // 全角スペース
	OutputLog( " \t  \n \r 12345" );
	OutputLog( " \t  \n  \r 0" );
	OutputLog( "-.32f" );
	OutputLog( "あああああ" );
	OutputLog( "-あああああ" );
	OutputLog( "01弐参" );
	OutputLog( "零壱23" );
	return 0;
}
```

出力結果↓
```
をatoiで変換：0
をatofで変換：0

0aaaをatoiで変換：0
0aaaをatofで変換：0

00aaaをatoiで変換：0
00aaaをatofで変換：0

-00aaaをatoiで変換：0
-00aaaをatofで変換：-0

-0aaaをatoiで変換：0
-0aaaをatofで変換：-0

009をatoiで変換：9
009をatofで変換：9

    12345をatoiで変換：12345
    12345をatofで変換：12345

　　12345をatoiで変換：0
　　12345をatofで変換：0


 12345をatoiで変換：12345

 12345をatofで変換：12345


 0をatoiで変換：0

 0をatofで変換：0

-.32fをatoiで変換：0
-.32fをatofで変換：-0.32

あああああをatoiで変換：0
あああああをatofで変換：0

-あああああをatoiで変換：0
-あああああをatofで変換：0

01弐参をatoiで変換：1
01弐参をatofで変換：1

零壱23をatoiで変換：0
零壱23をatofで変換：0
```

エラーにはならず、0が返ってきてしまいます
これをエラーにする判定処理を入れて作ってみました

# 判定処理を作ってみた

```C++
// 文字列の置換
void ReplaceStart( std::string& inoutStr, const std::string& oldStr, const std::string& newStr )
{
	size_t str_pos = 0;
	std::string::size_type  pos( GetFirstPos( inoutStr, oldStr ) );

	while( str_pos + oldStr.length() < inoutStr.length() && pos == str_pos )
	{
		inoutStr.replace( pos, oldStr.length(), newStr );
		pos = GetFirstPos( &inoutStr[ pos + newStr.length() ], oldStr );

		str_pos += newStr.length();
	}
}


// 文字列の前の空白や改行を除去する
void TrimStart( std::string& inoutStr )
{
	while( inoutStr.front() == ' ' || 
		   inoutStr.front() == '\n' || 
		   inoutStr.front() == '\r' ||
		   inoutStr.front() == '\t' )
	{
		ReplaceStart( inoutStr, " ", "" );
		ReplaceStart( inoutStr, "\n", "" );
		ReplaceStart( inoutStr, "\r", "" );
		ReplaceStart( inoutStr, "\t", "" );
	}
}

// 文字列を数値に変換する
template<typename T> 
T ConvertToNum( const std::string& numStr ) // 文字の先頭にあるスペースを削除して数値を取得する
{
	std::string bufStr = numStr;
	double checkNum = atof( numStr.c_str() );

	T convertNum = ( T ) checkNum;
	TrimStart( bufStr );

	if( checkNum == 0 && bufStr.front() != '0' && ( bufStr[ 0 ] == '-' && bufStr[ 1 ] == '0' ) == false )
	{
		throw std::invalid_argument( "この文字列は変換できません" );
	}

	return convertNum;}
```

GetFirstPosは下記で記述してるので今回は割愛

https://qiita.com/f_tsucchy/items/a67b96c07ef638bbe831

最初に空白がある可能性があるので取り除いた後にatofで変換し、変換した値が0の場合に本当に0なのかチェックをしています

先程のチェックコードに作った関数を入れて確認してみます

```C++
void OutputLog( const std::string& str )
{
	try
	{
		std::cout << str << "をatoiで変換：" << atoi( str.c_str() ) << std::endl;
	}
	catch( std::exception e )
	{
		std::cout << str << "の変換に失敗！！" << std::endl;
	}
	try
	{
		std::cout << str << "をatofで変換：" << atof( str.c_str() ) << std::endl;
	}
	catch( std::exception e )
	{
		std::cout << str << "の変換に失敗！！" << std::endl;
	}

	std::cout << std::endl;

	try
	{
		int num = ConvertToNum<int>( str );
		std::cout << str << "をintに変換：" << num << std::endl;
	}
	catch( std::exception e )
	{
		std::cout << str << "の変換に失敗！！" << std::endl;
	}
	try
	{
		float num = ConvertToNum<float>( str );
		std::cout << str << "をfloatに変換：" << num << std::endl;
	}
	catch( std::exception e )
	{
		std::cout << str << "の変換に失敗！！" << std::endl;
	}

	try
	{
		float num = ConvertToNum<double>( str );
		std::cout << str << "をdoubleに変換：" << num << std::endl;
	}
	catch( std::exception e )
	{
		std::cout << str << "の変換に失敗！！" << std::endl;
	}

	std::cout << std::endl;
}
```

結果↓
```
をatoiで変換：0
をatofで変換：0

の変換に失敗！！
の変換に失敗！！
の変換に失敗！！

0aaaをatoiで変換：0
0aaaをatofで変換：0

0aaaをintに変換：0
0aaaをfloatに変換：0
0aaaをdoubleに変換：0

00aaaをatoiで変換：0
00aaaをatofで変換：0

00aaaをintに変換：0
00aaaをfloatに変換：0
00aaaをdoubleに変換：0

-00aaaをatoiで変換：0
-00aaaをatofで変換：-0

-00aaaをintに変換：0
-00aaaをfloatに変換：-0
-00aaaをdoubleに変換：-0

-0aaaをatoiで変換：0
-0aaaをatofで変換：-0

-0aaaをintに変換：0
-0aaaをfloatに変換：-0
-0aaaをdoubleに変換：-0

009をatoiで変換：9
009をatofで変換：9

009をintに変換：9
009をfloatに変換：9
009をdoubleに変換：9

    12345をatoiで変換：12345
    12345をatofで変換：12345

    12345をintに変換：12345
    12345をfloatに変換：12345
    12345をdoubleに変換：12345

　　12345をatoiで変換：0
　　12345をatofで変換：0

　　12345の変換に失敗！！
　　12345の変換に失敗！！
　　12345の変換に失敗！！


 12345をatoiで変換：12345

 12345をatofで変換：12345


 12345をintに変換：12345

 12345をfloatに変換：12345

 12345をdoubleに変換：12345


 0をatoiで変換：0

 0をatofで変換：0


 0をintに変換：0

 0をfloatに変換：0

 0をdoubleに変換：0

-.32fをatoiで変換：0
-.32fをatofで変換：-0.32

-.32fをintに変換：0
-.32fをfloatに変換：-0.32
-.32fをdoubleに変換：-0.32

あああああをatoiで変換：0
あああああをatofで変換：0

あああああの変換に失敗！！
あああああの変換に失敗！！
あああああの変換に失敗！！

-あああああをatoiで変換：0
-あああああをatofで変換：0

-あああああの変換に失敗！！
-あああああの変換に失敗！！
-あああああの変換に失敗！！

01弐参をatoiで変換：1
01弐参をatofで変換：1

01弐参をintに変換：1
01弐参をfloatに変換：1
01弐参をdoubleに変換：1

零壱23をatoiで変換：0
零壱23をatofで変換：0

零壱23の変換に失敗！！
零壱23の変換に失敗！！
零壱23の変換に失敗！！
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2969992/d67bf52a-7ecc-b98c-3443-5e17e3f79ce3.png)
↑は全角が前にある場合の変換なので失敗にしています