36

以下のプログラムは文字列を long に変換しますが、私の理解ではエラーも返されます。strtol文字列を long に正常に変換した場合、2 番目のパラメーターはstrtolNULL に等しくなるはずであるという事実に依存しています。以下のアプリケーションを 55 で実行すると、次のメッセージが表示されます。

./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55

strtol からエラーを正常に検出するにはどうすればよいですか? 私のアプリケーションでは、ゼロが有効な値です。

コード:

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

static long parseLong(const char * str);

int main(int argc, char ** argv)
{
    printf("%s as long is %ld\n", argv[1], parseLong(argv[1]));
    return 0;
 }

static long parseLong(const char * str)
{
    long _val = 0;
    char * temp;

    _val = strtol(str, &temp, 0);

    if(temp != '\0')
            printf("Could not convert %s to long and leftover string is: %s", str, temp);

    return _val;
}
4

5 に答える 5

67

アンダースコアで始まる名前は実装用に予約されていることに注意してください。コード内でそのような名前を使用しないことをお勧めします。したがって、_valちょうどあるべきですval

およびその関係者のエラー処理の完全な仕様strtol()は複雑で、最初に遭遇したときは驚くほど複雑です。あなたが完全に正しいことの 1 つは、関数を使用して呼び出すことstrtol()です。コードで「そのまま」使用することは、おそらく正しくありません。

質問は C と C++ の両方でタグ付けされているため、C2011 標準から引用します。C++ 標準の適切な表現を自分で見つけることができます。

ISO/IEC 9899:2011 §7.22.1.4 strtolstrtollstrtoulおよびstrtoull関数

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);

¶2 [...] まず、入力文字列を 3 つの部分に分解します。最初の、おそらく空の空白文字のシーケンス (isspace 関数で指定)、特定の基数で表される整数に似たサブジェクト シーケンスです。 base の値と、入力文字列の終端の null 文字を含む、認識されない 1 つ以上の文字の最終文字列。[...]

¶7 サブジェクト シーケンスが空であるか、期待される形式を持たない場合、変換は実行されません。の値は、nptrが指すオブジェクトに格納されます。endptrただし、endptrはヌル ポインタではありません。

戻り値

¶8 strtolstrtollstrtoul、およびstrtoull関数は、変換された値があればそれを返します。変換を実行できなかった場合は、0 が返されます。正しい値が表現可能な値の範囲外にある場合、LONG_MIN、LONG_MAX、LLONG_MIN、LLONG_MAX、ULONG_MAX、または ULLONG_MAX が返され (戻り値の型と値の符号があれば)、マクロ ERANGE の値は次のようになります。に格納されerrnoます。

標準 C ライブラリ関数はerrno0 に設定されないことに注意してください。したがって、信頼性を高めるには、 をerrno呼び出す前に 0 に設定する必要がありますstrtol()

したがって、parseLong()関数は次のようになります。

static long parseLong(const char *str)
{
    errno = 0;
    char *temp;
    long val = strtol(str, &temp, 0);

    if (temp == str || *temp != '\0' ||
        ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
        fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n",
                str, temp);
        // cerr << "Could not convert '" << str << "' to long and leftover string is '"
        //      << temp << "'\n";
    return val;
}

エラーの場合、返された内容に応じて、0 または LONG_MIN または LONG_MAX が返されることに注意してくださいstrtol()。呼び出しコードが変換が成功したかどうかを知る必要がある場合は、別の関数インターフェイスが必要です — 以下を参照してください。また、エラーはstderrではなくstdoutに出力する必要があり、エラー メッセージは改行で終了する必要があることに注意してください\n。そうでない場合、タイムリーに表示される保証はありません。

さて、ライブラリ コードではおそらく印刷は必要なく、呼び出し元のコードは変換が成功したか失敗したかを知りたがるかもしれないので、インターフェースも修正するかもしれません。その場合、おそらく関数を変更して、成功/失敗の表示を返すようにします。

bool parseLong(const char *str, long *val)
{
    char *temp;
    bool rc = true;
    errno = 0;
    *val = strtol(str, &temp, 0);

    if (temp == str || *temp != '\0' ||
        ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
        rc = false;

    return rc;
}

次のように使用できます。

if (parseLong(str, &value))
    …conversion successful…
else
    …handle error…

「末尾のジャンク」、「無効な数値文字列」、「値が大きすぎる」、「値が小さすぎる」(および「エラーなし」) を区別する必要がある場合enumは、ブール値の戻りコードの代わりに整数 or を使用します。末尾の空白は許可するが他の文字は許可しない場合、または先頭の空白を許可したくない場合は、関数でさらに作業を行う必要があります。このコードでは、8 進数、10 進数、および 16 進数を使用できます。厳密に 10 進数が必要な場合は、への呼び出しで 0 を 10 に変更する必要がありますstrtol()

関数が標準ライブラリの一部としてマスカレードする場合、それらは永続的に設定されるべきではないerrnoため0、コードをラップして保存する必要がありますerrno

int saved = errno;  // At the start, before errno = 0;

…rest of function…

if (errno == 0)     // Before the return
    errno = saved;
于 2013-01-05T21:30:33.243 に答える
23

あなたはほとんどそこにいます。tempそれ自体は null にはなりませんが、文字列全体が変換された場合は null 文字を指すため、逆参照する必要があります。

if (*temp != '\0')
于 2013-01-05T20:35:33.740 に答える
4

間接的なレベルがありません。ポインターが であるかどうかではなく、文字が終端であるかどうかを確認したい場合:NULNULL

if (*temp != '\0')

ところで、これはエラー チェックの良い方法ではありません。関数ファミリの適切なエラー チェック方法はstrto*、出力ポインタを文字列の末尾と比較することによって行われません。これは、ゼロの戻り値をチェックし、 の戻り値を取得することによって実行する必要がありますerrno

于 2013-01-05T20:36:10.260 に答える
1

チェックしているはずです

*temp != '\0'

これに従って、strotol を呼び出した後に errno の値を確認することもできるはずです。

RETURN VALUES
     The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result
     of the conversion, unless the value would underflow or overflow.  If no conver-
     sion could be performed, 0 is returned and the global variable errno is set to
     EINVAL (the last feature is not portable across all platforms).  If an overflow
     or underflow occurs, errno is set to ERANGE and the function return value is
     clamped according to the following table.


       Function       underflow     overflow
       strtol()       LONG_MIN      LONG_MAX
       strtoll()      LLONG_MIN     LLONG_MAX
       strtoimax()    INTMAX_MIN    INTMAX_MAX
       strtoq()       LLONG_MIN     LLONG_MAX
于 2013-01-05T20:41:16.900 に答える