画面にこう出れば正解
どの言語で書いても、同じ入力なら画面に出る文字は1文字も違わない、というのが正解の決め方です(例は Python で動かしたときのコマンドです)。
基本
$ python3 parseint.py 42
42 の2倍は 84マイナス
$ python3 parseint.py -7
-7 の2倍は -14先頭の0
$ python3 parseint.py 007
7 の2倍は 14文字
$ python3 parseint.py abc
整数ではありません小数
$ python3 parseint.py 12.0
整数ではありません前に空白
$ python3 parseint.py " 12"
整数ではありません全角の数字
$ python3 parseint.py 12
整数ではありません空
$ python3 parseint.py ""
整数ではありませんプラス記号
$ python3 parseint.py +5
整数ではありません16桁の数
$ python3 parseint.py 9007199254740993
9007199254740993 の2倍は 18014398509481986範囲を少し超える
$ python3 parseint.py 1000000000000000001
大きすぎます20桁
$ python3 parseint.py 99999999999999999999
大きすぎますマイナス0
$ python3 parseint.py -0
0 の2倍は 0下線入り
$ python3 parseint.py 1_000
整数ではありません16進数の書き方
$ python3 parseint.py 0x1A
整数ではありません10言語のプログラム
どれもそのまま保存して動かせます。毎朝、上の正解の例をすべて実際に動かして確かめています。
parseint.php8.4.26 で 15 / 15 件の出力が正解と一致
<?php
// 受け取った文字列が整数なら2倍にして出す。整数でなければ、そう伝える
$s = $argv[1];
// is_numeric は前の空白・「+」・小数も数とみなすので、先に「-? と半角数字だけ」かを確かめる
if (!preg_match('/\A-?[0-9]+\z/', $s)) {
echo "整数ではありません\n";
exit;
}
// PHP の整数に入らない数は (int) で別の数に変わるので、先に桁数で「大きすぎる」かを決める(10の18乗は19桁)
$digits = ltrim(ltrim($s, '-'), '0');
if (strlen($digits) > 19 || (strlen($digits) === 19 && $digits !== '1000000000000000000')) {
echo "大きすぎます\n";
exit;
}
$n = (int) $s;
echo $n . " の2倍は " . ($n * 2) . "\n";
動かし方(ファイルを保存したフォルダで) ・ PHP を入れる
$ php parseint.php 42
parseint.py3.14.7 で 15 / 15 件の出力が正解と一致
# 受け取った文字列が整数なら2倍にして出す。整数でなければ、そう伝える
import re
import sys
s = sys.argv[1]
# int() は前後の空白・全角の数字・「+」・「_」も受け付けるので、先に「-? と半角数字だけ」かを確かめる
if not re.fullmatch(r"-?[0-9]+", s):
print("整数ではありません")
sys.exit()
n = int(s) # Python の整数には上限が無いので、20桁でもそのまま比べられる
if abs(n) > 10**18:
print("大きすぎます")
else:
print(f"{n} の2倍は {n * 2}")
動かし方(ファイルを保存したフォルダで) ・ Python を入れる
$ python3 parseint.py 42
parseint.go1.27.2 で 15 / 15 件の出力が正解と一致
// 受け取った文字列が整数なら2倍にして出す。整数でなければ、そう伝える
package main
import (
"fmt"
"os"
"regexp"
"strconv"
"strings"
)
func main() {
s := os.Args[1]
// strconv.Atoi は「+5」も受け付けるので、先に「-? と半角数字だけ」かを確かめる
if !regexp.MustCompile(`^-?[0-9]+$`).MatchString(s) {
fmt.Println("整数ではありません")
return
}
// int64 に入らない数は直すとエラーになるので、先に桁数で「大きすぎる」かを決める(10の18乗は19桁)
digits := strings.TrimLeft(strings.TrimPrefix(s, "-"), "0")
if len(digits) > 19 || (len(digits) == 19 && digits != "1000000000000000000") {
fmt.Println("大きすぎます")
return
}
n, _ := strconv.ParseInt(s, 10, 64) // ここまで来れば必ず int64 に入る
fmt.Printf("%d の2倍は %d\n", n, n*2)
}
動かし方(ファイルを保存したフォルダで) ・ Go を入れる
$ go run parseint.go 42
parseint.js24.15.0 で 15 / 15 件の出力が正解と一致
// 受け取った文字列が整数なら2倍にして出す。整数でなければ、そう伝える
const s = process.argv[2];
// Number() は空の文字列を 0 に、" 12" や "0x1A" も数にするので、先に「-? と半角数字だけ」かを確かめる
if (!/^-?[0-9]+$/.test(s)) {
console.log("整数ではありません");
process.exit();
}
// 普通の数(Number)は 2の53乗 を超えると1の位がずれるので、大きい整数を正しく持てる BigInt を使う
const n = BigInt(s);
const limit = 10n ** 18n;
if (n > limit || n < -limit) {
console.log("大きすぎます");
} else {
console.log(`${n} の2倍は ${n * 2n}`);
}
動かし方(ファイルを保存したフォルダで) ・ JavaScript を入れる
$ node parseint.js 42
ParseInt.java21.0.12 で 15 / 15 件の出力が正解と一致
// 受け取った文字列が整数なら2倍にして出す。整数でなければ「整数ではありません」と出す
import java.math.BigInteger;
public class ParseInt {
public static void main(String[] args) {
String s = args[0];
// Long.parseLong は「+5」や全角の「12」も受け付けるので、先に「-? と半角の数字だけ」かを確かめる
if (!s.matches("-?[0-9]+")) {
System.out.println("整数ではありません");
return;
}
// long に入りきらない桁数でも比べられるように、大きさに上限のない BigInteger で直す
BigInteger n = new BigInteger(s);
if (n.abs().compareTo(BigInteger.TEN.pow(18)) > 0) {
System.out.println("大きすぎます");
return;
}
System.out.println(n + " の2倍は " + n.multiply(BigInteger.TWO));
}
}
動かし方(ファイルを保存したフォルダで) ・ Java を入れる
$ java ParseInt.java 42
parseint.cs10.0.402 で 15 / 15 件の出力が正解と一致
// 受け取った文字列が整数なら2倍にして出す。整数でなければ「整数ではありません」と出す
using System.Globalization;
using System.Numerics;
using System.Text.RegularExpressions;
string s = args[0];
// long.TryParse は前後の空白や「+5」も受け付けるので、先に「-? と半角の数字だけ」かを確かめる
// ($ は最後の改行の前にも合うので、文字列の終わりは \z と書く)
if (!Regex.IsMatch(s, @"\A-?[0-9]+\z"))
{
Console.WriteLine("整数ではありません");
return;
}
// long に入りきらない桁数でも比べられるように、大きさに上限のない BigInteger で直す
BigInteger n = BigInteger.Parse(s, CultureInfo.InvariantCulture);
if (BigInteger.Abs(n) > BigInteger.Pow(10, 18))
{
Console.WriteLine("大きすぎます");
return;
}
Console.WriteLine($"{n} の2倍は {n * 2}");
動かし方(ファイルを保存したフォルダで) ・ C# を入れる
$ dotnet run parseint.cs -- 42
parseint.rs1.99.0 で 15 / 15 件の出力が正解と一致
// 受け取った文字列が整数なら2倍にして出す。整数でなければ「整数ではありません」と出す
fn main() {
let args: Vec<String> = std::env::args().collect();
let s = &args[1];
// parse は「+5」も受け付けるので、先に「-? と半角の数字だけ」かを確かめる
let digits = s.strip_prefix('-').unwrap_or(s);
if digits.is_empty() || !digits.chars().all(|c| c.is_ascii_digit()) {
println!("整数ではありません");
return;
}
// i64 に入りきらない桁数でも比べられるように、先頭の0を取った桁数で比べる(同じ桁数なら文字列の比較で大小がわかる)
let digits = digits.trim_start_matches('0');
if digits.len() > 19 || (digits.len() == 19 && digits > "1000000000000000000") {
println!("大きすぎます");
return;
}
let n: i64 = s.parse().unwrap();
println!("{} の2倍は {}", n, n * 2);
}
動かし方(ファイルを保存したフォルダで) ・ Rust を入れる
$ rustc parseint.rs && ./parseint 42
parseint.rb4.0.7 で 15 / 15 件の出力が正解と一致
# 受け取った文字列が整数なら2倍にして出す。整数でなければ、そう伝える
s = ARGV[0]
# Integer() は前後の空白・「+」・「_」・「0x」も受け付けるので、先に「-? と半角数字だけ」かを確かめる
unless s.match?(/\A-?[0-9]+\z/)
puts "整数ではありません"
exit
end
# Integer("010") は 8進数として 8 になるので、10進数と指定する。Ruby の整数には上限が無い
n = Integer(s, 10)
if n.abs > 10**18
puts "大きすぎます"
else
puts "#{n} の2倍は #{n * 2}"
end
動かし方(ファイルを保存したフォルダで) ・ Ruby を入れる
$ ruby parseint.rb 42
parseint.kt2.4.21 で 15 / 15 件の出力が正解と一致
// 受け取った文字列が整数なら2倍にして出す。整数でなければ「整数ではありません」と出す
import java.math.BigInteger
fun main(args: Array<String>) {
val s = args[0]
// toLongOrNull は「+5」や全角の「12」も受け付けるので、先に「-? と半角の数字だけ」かを確かめる
if (!Regex("-?[0-9]+").matches(s)) {
println("整数ではありません")
return
}
// Long に入りきらない桁数でも比べられるように、大きさに上限のない BigInteger で直す
val n = BigInteger(s)
if (n.abs() > BigInteger.TEN.pow(18)) {
println("大きすぎます")
return
}
println("$n の2倍は ${n * BigInteger.TWO}")
}
動かし方(ファイルを保存したフォルダで) ・ Kotlin を入れる
$ kotlinc parseint.kt -include-runtime -d parseint.jar && java -jar parseint.jar 42
parseint.swift6.3.3 で 15 / 15 件の出力が正解と一致
// 受け取った文字列が整数なら2倍にして出す。整数でなければ「整数ではありません」と出す
let s = CommandLine.arguments[1]
// Int() は「+5」も受け付けるので、先に「-? と半角の数字だけ」かを確かめる
let digits = s.hasPrefix("-") ? String(s.dropFirst()) : s
if digits.isEmpty || !digits.allSatisfy({ $0.isASCII && $0.isNumber }) {
print("整数ではありません")
} else {
// Int に入りきらない桁数でも比べられるように、先頭の0を取った桁数で比べる(同じ桁数なら文字列の比較で大小がわかる)
let trimmed = String(digits.drop(while: { $0 == "0" }))
if trimmed.count > 19 || (trimmed.count == 19 && trimmed > "1000000000000000000") {
print("大きすぎます")
} else {
let n = Int(s)!
print("\(n) の2倍は \(n * 2)")
}
}
動かし方(ファイルを保存したフォルダで) ・ Swift を入れる
$ swift parseint.swift 42
言語でつまずきやすいところ
初心者がよく書く形(各言語で最初に習う書き方をそのまま使ったもの)を、同じ正解表で動かした結果です。言語を出した答えごとにまとめ、赤い字はその言語が実際に出した間違った行です。
| 入力 | 初心者がよく書く形の答え(10言語) |
|---|---|
基本42 | ✓10言語すべて正解 |
マイナス-7 | ✓10言語すべて正解 |
先頭の0007 | ✓10言語すべて正解 |
文字abc | ✓10言語すべて正解 |
小数12.0 |
|
前に空白" 12" |
|
全角の数字12 |
|
空"" |
|
プラス記号+5 |
|
16桁の数9007199254740993 |
|
範囲を少し超える1000000000000000001 |
|
20桁99999999999999999999 |
|
マイナス0-0 | ✓10言語すべて正解 |
下線入り1_000 |
|
16進数の書き方0x1A |
|
この回でやること
- 文字列を整数に直す
- 整数として受け付けてよい形かを、自分で決めて確かめる
- 大きすぎる数を見分ける
この回の決まり
- 受け付けるのは、半角の数字だけ(先頭にマイナスが1つ付いてもよい)。前後の空白・全角の数字・小数点・プラス記号・下線(
_)・0xなどは「整数ではありません」 - 先頭の 0 は取って出す(
007は 7)。-0は 0 - 大きさが 10の18乗(100京)を超える数は「大きすぎます」
解説
「数に直す」関数は、決まりより広く受け付けることがある
どの言語にも、文字列を整数に直す関数(int()・parseInt・Integer.parseInt・strconv.Atoi など)があります。ただし、前後の空白、全角の数字、プラス記号、下線、0x の書き方などを「数」として受け付けるかどうかは、関数によって違います。空の文字列を 0 として扱う関数もあります。
各言語のいちばん普通の関数に、この回の入力を渡した結果です(「受け付けた」は、エラーにならずに数として返したもの)。
| 入力 | 受け付けて数にした言語・関数 |
|---|---|
+5 | 10言語すべて(5 として) |
12(前に空白) | Python int()・Ruby Integer()・C# long.TryParse・PHP is_numeric+(int)・JavaScript Number() |
12(全角) | Python int()・Java Long.parseLong・Kotlin toLongOrNull() |
12.0 | PHP・JavaScript(12 として) |
1_000 | Python・Ruby(1000 として) |
0x1A | JavaScript・Ruby(26 として) |
| 空の文字列 | JavaScript Number()(0 として) |
「+5」は、どの言語の関数も受け付けました。この回の決まりで「+5」を受け付けないなら、関数に任せずに自分で確かめるしかありません。
問い合わせフォームなど、人が入力した値を受け取るときは、関数に任せる前に「受け付ける形」を決めて、その形かを確かめます。この回では、第7回 と同じように、半角の数字だけかを1文字ずつ(または正規表現で)確かめてから数に直しています。
大きい数
整数の型には、入る大きさに上限があります(よく使う64ビットの整数で、およそ 922京)。20桁の「99999999999999999999」を渡すと、Go・Java・Kotlin・C#・Rust・Swift の関数は「直せない」というエラーを返しました。エラーの種類を見ずに「整数ではありません」と出すと、「大きすぎる」のか「形が違う」のかが区別できません。
JavaScript の普通の数(Number)は、9007199254740991(2の53乗−1)を超えると、1の位まで正しく表せなくなります。「9007199254740993」を Number() に渡すと 9007199254740992 になり、「1000000000000000001」は 1000000000000000000 になって「大きすぎます」の判定をすり抜けました。大きい整数は BigInt を使います。
練習
- 「1,000」のようにカンマの入った数も受け付けるようにしてみましょう
- 0 から 120 までの数だけを受け付ける「年齢の入力チェック」を書いてみましょう