69

の没落について誰かと素敵な会話をしましたstd::stoi。率直に言えば、内部的に使用しstd::strtol、エラーが報告された場合にスローします。彼らによると、ただし、std::strtolの入力に対してエラーを報告すべきではなく、がスロー"abcxyz"されません。stoistd::invalid_argument

まず、これらのケースの動作について GCC でテストされた 2 つのプログラムを次に示し
ます

どちらも で成功、"123"で失敗を示し"abc"ます。


私はより多くの情報を引き出すために標準を調べました:

§ 21.5

Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that  
no conversion could be performed. Throws out_of_range if the converted value is  
outside the range of representable values for the return type.

これは、に依存する動作を要約したものstrtolです。今はどうstrtolですか?C11ドラフトでこれを見つけました:

§7.22.1.4

If the subject sequence is empty or does not have the expected form, no  
conversion is performed; the value of nptr is stored in the object  
pointed to by endptr, provided that endptr is not a null pointer.

を渡す状況を考えると"abc"、C 標準nptrでは、文字列の先頭を指す は、渡されたポインタである に格納されると規定されendptrています。これは、テストと一致しているようです。また、次のように 0 を返す必要があります。

§7.22.1.4

If no conversion could be performed, zero is returned.

stoi以前のリファレンスでは、変換が実行されないため、0 を返さなければならないと言われていました。これらの条件は、 throwingの C++11 標準に準拠するようになりましたstd::invalid_argument


この結果は私にとって重要です。stoi文字列から int への変換の他の方法のより良い代替手段として推奨したり、期待どおりに機能したかのように自分で使用したりしたくないからです。テキストを無効な変換としてキャッチします。

このすべての後、私はどこかで間違っていましたか?この例外がスローされたという良い証拠があるように思えます。私の証明は有効ですか、またはstd::stoi与えられたときにその例外をスローすることが保証されていません"abc"か?

4

1 に答える 1

85

std::stoi入力でエラーをスローします"abcxyz"か?

はい。

あなたの混乱は、オーバーフローを除いてエラーstrtolを報告しないという事実から来るかもしれないと思います。変換が実行されなかったことを報告できますが、これはC標準ではエラー状態とは呼ばれません。

strtolは3つのC標準すべてで同様に定義されており、退屈な詳細は割愛しますが、基本的には、実際の数に対応する入力文字列の部分文字列である「サブジェクトシーケンス」を定義します。次の4つの条件は同等です。

  • サブジェクトシーケンスは期待される形式を持っています(平易な英語で:それは数字です)
  • サブジェクトシーケンスは空ではありません
  • 変換が発生しました
  • *endptr != nptrendptr(これは、がnull以外の場合にのみ意味があります)

オーバーフローが発生した場合でも、変換は発生したと言われます。

ここで、には"abcxyz"数値が含まれていないため、文字列のサブジェクトシーケンスは"abcxyz"空である必要があり、変換を実行できないことは明らかです。次のC90/C99 / C11プログラムは、実験的にそれを確認します。

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *nptr = "abcxyz", *endptr[1];
    strtol(nptr, endptr, 0);
    if (*endptr == nptr)
        printf("No conversion could be performed.\n");
    return 0;
}

これは、オプションの基本引数なしで入力が与えられた場合、の適合実装はスローするstd::stoi 必要があることを意味します。invalid_argument"abcxyz"


std::stoiこれは、満足のいくエラーチェックがあることを意味しますか?

いいえ。文字列の最初の非数字文字から始まるすべての文字を黙って削除するため、後で完全なチェックをstd::stoi実行するよりも寛大であると彼女が言ったとき、あなたが話していた人は正しいです。errno == 0 && end != start && *end=='\0'std::strtolstd::stoi

実際、私の頭から離れて、ネイティブ変換がJavascriptのように動作する唯一の言語は、Javascriptです。それでも、16進数の特殊なケースを回避するために、std::stoi基数10を強制する必要があります。parseInt(n, 10)

input      |  std::atoi       std::stoi      Javascript      full check 
===========+=============================================================
hello      |  0               error          error(NaN)      error      
0xygen     |  0               0              error(NaN)      error      
0x42       |  0               0              66              error      
42x0       |  42              42             42              error      
42         |  42              42             42              42         
-----------+-------------------------------------------------------------
languages  |  Perl, Ruby,     Javascript     Javascript      C#, Java,  
           |  PHP, C...       (base 10)                      Python...  

注:空白と冗長な+記号の処理には、言語間でも違いがあります。


さて、完全なエラーチェックが必要です。何を使用すればよいですか?

私はこれを行う組み込み関数をboost::lexical_cast<int>知りませんが、あなたが望むことをします。int()Pythonの関数とは異なり、周囲の空白を拒否するため、特に厳密です。無効な文字とオーバーフローにより、同じ例外が発生することに注意してくださいboost::bad_lexical_cast

#include <boost/lexical_cast.hpp>

int main() {
    std::string s = "42";
    try {
        int n = boost::lexical_cast<int>(s);
        std::cout << "n = " << n << std::endl;
    } catch (boost::bad_lexical_cast) {
        std::cout << "conversion failed" << std::endl;
    }
}
于 2012-07-22T11:16:50.440 に答える