Menu

C言語で文字列を数値に:atoi、strtol、そして数値を文字列にする方法

C言語でテキストと数値を相互に変換する:atoi がエラーを報告できない理由、endptr と errno を使った strtol・strtod の正しい使い方、snprintf で数値を文字列にする方法、そして '0' を使った1桁の技。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

テキストはユーザー、ファイル、コマンドラインから文字として届きます。"42" は3バイト - '4''2''\0' - であり、変換するまでその中に数の 42 はありません。Cには2世代の変換関数があります。短くて古い、エラーを報告できないものと、長くて報告できるものです。このページは両方を示し、実際のプログラムが後者を使う理由を説明します。

古い一族:atoi、atol、atof

名前のとおりに読めます。ASCII から int へ、long へ、double へ。引数1つ、結果1つです。

先頭の空白は飛ばされ、任意の符号が読まれ、数字が消費され、その後のすべては無視されます。入力が不正になるまでは便利です。

3つとも 0 を表示します。戻りコードもフラグもなく、「うまくいったか?」と尋ねる手段もありません - そして int をあふれさせる値は報告されるエラーではなく未定義動作です。おもちゃのプログラムの中にハードコードしたリテラルなら我慢できます。人やファイルが与えうるものに対しては、静かなバグの製造機です。

正しい道具:strtol

strtol(string to long)は同じ変換に、何が起きたかを報告する2つの経路を備えたものです。

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

endptr には関数が消費しなかった最初の文字が設定されます。base は基数で、10 を使うか、リテラル自身の接頭辞に判断させるなら 0 を使います(16進なら 0x、8進なら先頭の 0)。

endptr を読むことで atoi には答えられない問いに答えられます。

  • end == input何も変換されなかったという意味です - テキストが数で始まっていませんでした。
  • *end != '\0' は後ろに内容が残っていたという意味です。それがエラーかどうかはあなたの判断です。"42 apples" を解析しているなら問題ありませんし、その行全体が数であるべきだったなら問題です。

オーバーフローは別途 errno を通じて報告されます。完全で正しい書き方は、一度書いて使い回す価値があります。

3つの細部がこれを正しくしています。呼び出し前の errno = 0 が重要なのは、ライブラリは errno設定するだけで決してクリアしないからです。以前の呼び出しの古い値が、今回の失敗として読まれてしまいます。strtollong を返し、それは int より広いかもしれないので、INT_MININT_MAX に対する範囲検査は ERANGE とは別に必要です。そして成功をステータスとして返し、値はポインタ経由で渡すのが、Cの関数が「値か失敗のどちらか」を報告する方法です - 戻り値は両方の役目を果たせないのです。

strtoul は符号なしの対応物で、strtollstrtoulllong long を扱います。

その他の基数

基数 16 は16進を読み、基数 0 は接頭辞を調べて自分で判断します。

基数 0 はある特定の場合に罠になります。"012" のようなゼロ詰めの十進が8進として読まれ、10 として出てくるのです。入力が十進なら明示的に 10 と書きましょう。

浮動小数点:strtod

strtoddouble に対する同じ形で、atofatoi と同じ欠点を持っています。

strtod は科学表記("1.5e3")、16進の浮動小数点、そして "inf""nan" という語も受け付けます。小数点の区切り文字はロケールの影響を受けるので、他所で書かれたファイルを解析する人を驚かせます - 既定の "C" ロケールでは区切り文字は常にドットです。

数値からテキストへ:snprintf

逆方向には snprintf が標準の答えです。渡したサイズを超えて書くことはなく、必ず終端し、完全な結果に必要だった長さを返します。

戻り値が切り詰めの検査になります。バッファサイズ以上なら、テキストは収まりませんでした。

手を出すべきでないものが2つ。itoa は標準Cの一部ではないので、それを使うコードはどこでもコンパイルできるわけではありません。そして sprintf はサイズ制限のない snprintf であり、文句も言わずバッファの末尾を越えて書き込みます。

1文字と '0'

数字の文字 '0' から '9' は連続していることが保証されているので、単なる引き算で数字の文字とその値を相互に変換できます。

このように振る舞うのは数字だけです - 英字は連続していることを要求されていないので、c - 'a' はアルファベットの計算に対して可搬ではありません。信用する前に isdigit で引き算を守りましょう。

unsigned char へのキャストが必要な理由は文字列と同じです。<ctype.h> の関数は負の引数に対して未定義であり、素の char は符号付きでありうるからです。

どの関数を使うか

  • ユーザー、ファイル、argv からのテキスト → 完全な検査を伴う strtolstrtod
  • 自分で書いて管理しているリテラル → atoi でも許容範囲です(特筆すべきことはありませんが)。
  • 数値をバッファへ → snprintf、そして戻り値を確認すること。
  • 1文字の数字 → isdigit のあとで '0' を引く。
  • 決して使わない → getssprintfitoa

よくある質問

C言語で文字列を int に変換するには?

正解は strtol(text, &end, 10) です。値を返し、end に消費しなかった最初の文字を設定し、オーバーフローを errno で報告します。atoi(text) は短いのですが、"0" にも "hello" にも 0 を返し、その2つを区別する手段がありません。

C言語の atoi の何が問題なのですか?

失敗を報告できないことです。atoi("abc")0 を返しますが、これは本物のゼロと区別がつきませんし、オーバーフローは報告されるエラーではなく未定義動作です。テキストがユーザー、ファイル、コマンドライン引数から来たときは必ず strtol を使いましょう。

C言語で int を文字列に変換するには?

snprintf(buf, sizeof buf, "%d", n) です。標準的で、バッファサイズで制限され、完全な結果に必要だった文字数を返します - したがって戻り値が sizeof buf 以上ならテキストが切り詰められたと分かります。itoa は標準Cではありません。

C言語で文字 '7' を数値の 7 にするには?

'0' を引きます:int digit = c - '0';。数字の文字はどのC言語の文字集合でも連続しているので、この引き算で 0 から 9 が得られます。それ以外の文字では意味のない計算になるので、先に isdigit((unsigned char)c) で守りましょう。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める