C++で文字列(char *として与えられた)をintに解析する方法は何ですか?堅牢で明確なエラー処理はプラスです(ゼロを返す)。
ベストアンサー1
してはいけないこと
これが私の最初のアドバイスです。これには stringstream を使用しないでください。最初は使い方が簡単に思えるかもしれませんが、堅牢性と適切なエラー処理が必要な場合は、多くの追加作業が必要になることがわかります。
直感的にうまくいくと思われるアプローチは次のとおりです。
bool str2int (int &i, char const *s)
{
std::stringstream ss(s);
ss >> i;
if (ss.fail()) {
// not an integer
return false;
}
return true;
}
これには大きな問題があります。str2int(i, "1337h4x0r")
は問題なく を返しtrue
、i
値 を取得します。この問題を回避するには、変換後1337
に に文字が残らないようにします。stringstream
bool str2int (int &i, char const *s)
{
char c;
std::stringstream ss(s);
ss >> i;
if (ss.fail() || ss.get(c)) {
// not an integer
return false;
}
return true;
}
1 つの問題は解決しましたが、まだいくつか問題が残っています。
ss << std::hex
文字列内の数値が 10 進数でない場合はどうなるでしょうか。変換を試みる前に、ストリームを正しいモード (例 ) に設定することで、他の基数に対応しようとすることができます。しかし、これは呼び出し元が数値の基数を事前に知っておく必要があることを意味します。呼び出し元がそれをどのように知ることができるでしょうか。呼び出し元はまだ数値が何であるかを知りません。彼らはそれが数値であることさえ知りません。彼らがそれがどの基数であるかをどうやって知ることができると期待できるでしょうか。プログラムに入力されるすべての数値が 10 進数でなければならないことを義務付け、16 進数または 8 進数の入力を無効として拒否することもできます。しかし、これはあまり柔軟でも堅牢でもありません。この問題には簡単な解決策はありません。10 進数の変換は 8 進数 (先頭にゼロがある) では常に成功し、8 進数の変換は一部の 10 進数で成功する可能性があるため、単純に各基数に対して 1 回変換を試すことはできません。そのため、先頭にゼロがあるかどうかを確認する必要があります。しかし、待ってください。16 進数も先頭にゼロがある場合があります (0x...)。ため息。
上記の問題に対処できたとしても、さらに大きな問題が残ります。呼び出し側が不正な入力 (例: "123foo") と の範囲外の数値int
(例: 32 ビット の場合は "4000000000" int
) を区別する必要がある場合はどうすればよいのでしょうか。 では、この区別を行う方法がありません。わかるのは変換が成功したか失敗したかだけです。失敗した場合、失敗した理由をstringstream
知る方法はありません。おわかりのように、堅牢性と明確なエラー処理が必要な場合は、 にはまだまだ改善の余地があります。stringstream
これが私の 2 番目のアドバイスにつながります。これにはBoost を使用しないでくださいlexical_cast
。lexical_cast
ドキュメントに記載されている内容を検討してください。
変換に対してより高度な制御が必要な場合、std::stringstream と std::wstringstream はより適切なパスを提供します。ストリームベースでない変換が必要な場合、lexical_cast は適切なツールではなく、そのようなシナリオ向けに特別に設計されていません。
stringstream
何ですか?の制御レベルが低いことは既に説明しましたが、それでも「より高度な制御」が必要な場合はstringstream
の代わりに を使用する必要があると書かれています。また、は の単なるラッパーなので、 と同じ問題、つまり複数の基数のサポートが不十分で、エラー処理が不十分な問題があります。lexical_cast
lexical_cast
stringstream
stringstream
最善の解決策
幸いなことに、上記の問題はすべてすでに解決されています。C 標準ライブラリには、strtol
これらの問題がまったくないファミリが含まれています。
enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };
STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
char *end;
long l;
errno = 0;
l = strtol(s, &end, base);
if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
return OVERFLOW;
}
if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
return UNDERFLOW;
}
if (*s == '\0' || *end != '\0') {
return INCONVERTIBLE;
}
i = l;
return SUCCESS;
}
すべてのエラーケースを処理し、2 から 36 までの任意の基数をサポートするものとしては非常にシンプルです。 がbase
0 (デフォルト) の場合、任意の基数から変換を試みます。または、呼び出し側が 3 番目の引数を指定して、特定の基数に対してのみ変換を試行するように指定することもできます。これは堅牢で、最小限の労力ですべてのエラーを処理します。
(そして家族を好むstrtol
)その他の理由:
- はるかに優れた表示実行時パフォーマンス
- コンパイル時のオーバーヘッドが少なくなります(他のものはヘッダーから SLOC を 20 倍近く取得します)
- コードサイズが最小になる
他の方法を使用する正当な理由はまったくありません。