画面にこう出れば正解
どの言語で書いても、同じ入力なら画面に出る文字は1文字も違わない、というのが正解の決め方です(例は Python で動かしたときのコマンドです)。
ハイフンあり
$ python3 zipcode.py 123-4567
OK: 123-4567ハイフンなし
$ python3 zipcode.py 1234567
OK: 123-4567桁が足りない
$ python3 zipcode.py 123-456
NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)全角の数字
$ python3 zipcode.py 123-4567
NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)全角の数字(ハイフンなし)
$ python3 zipcode.py 1234567
NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)文字が混ざる
$ python3 zipcode.py 12a-4567
NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)前後に空白
$ python3 zipcode.py "123-4567 "
NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)10言語のプログラム
どれもそのまま保存して動かせます。毎朝、上の正解の例をすべて実際に動かして確かめています。
zipcode.php8.4.26 で 7 / 7 件の出力が正解と一致
<?php
// 郵便番号の形(数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
$s = $argv[1];
// 半角の数字だけを受け付けるように、\d ではなく [0-9] と書く。\z は「本当の終わり」($ は末尾の改行の前にも合う)
if (preg_match('/\A[0-9]{3}-?[0-9]{4}\z/', $s)) {
$digits = str_replace('-', '', $s);
echo "OK: " . substr($digits, 0, 3) . "-" . substr($digits, 3) . "\n";
} else {
echo "NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)\n";
}
動かし方(ファイルを保存したフォルダで) ・ PHP を入れる
$ php zipcode.php 123-4567
zipcode.py3.14.7 で 7 / 7 件の出力が正解と一致
# 郵便番号の形(数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
import re
import sys
s = sys.argv[1]
# Python の \d は全角の「1」にも合うので、[0-9] と書く。fullmatch は文字列全体が合うかを調べる
if re.fullmatch(r"[0-9]{3}-?[0-9]{4}", s):
digits = s.replace("-", "")
print(f"OK: {digits[:3]}-{digits[3:]}")
else:
print("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)")
動かし方(ファイルを保存したフォルダで) ・ Python を入れる
$ python3 zipcode.py 123-4567
zipcode.go1.27.2 で 7 / 7 件の出力が正解と一致
// 郵便番号の形(数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
package main
import (
"fmt"
"os"
"regexp"
"strings"
)
func main() {
s := os.Args[1]
// 半角の数字だけを受け付けることがはっきり分かるように、\d ではなく [0-9] と書く
if regexp.MustCompile(`^[0-9]{3}-?[0-9]{4}$`).MatchString(s) {
digits := strings.ReplaceAll(s, "-", "")
fmt.Printf("OK: %s-%s\n", digits[:3], digits[3:])
} else {
fmt.Println("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)")
}
}
動かし方(ファイルを保存したフォルダで) ・ Go を入れる
$ go run zipcode.go 123-4567
zipcode.js24.15.0 で 7 / 7 件の出力が正解と一致
// 郵便番号の形(数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
const s = process.argv[2];
// 半角の数字だけを受け付けることがはっきり分かるように、\d ではなく [0-9] と書く
if (/^[0-9]{3}-?[0-9]{4}$/.test(s)) {
const digits = s.replace("-", "");
console.log(`OK: ${digits.slice(0, 3)}-${digits.slice(3)}`);
} else {
console.log("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)");
}
動かし方(ファイルを保存したフォルダで) ・ JavaScript を入れる
$ node zipcode.js 123-4567
Zipcode.java21.0.12 で 7 / 7 件の出力が正解と一致
// 受け取った文字列が郵便番号の形(半角の数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
public class Zipcode {
public static void main(String[] args) {
String s = args[0];
// \d ではなく [0-9] と書いて、半角の数字だけを受け付けることをはっきりさせる
if (s.matches("[0-9]{3}-?[0-9]{4}")) {
String digits = s.replace("-", "");
System.out.println("OK: " + digits.substring(0, 3) + "-" + digits.substring(3));
} else {
System.out.println("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)");
}
}
}
動かし方(ファイルを保存したフォルダで) ・ Java を入れる
$ java Zipcode.java 123-4567
zipcode.cs10.0.402 で 7 / 7 件の出力が正解と一致
// 受け取った文字列が郵便番号の形(半角の数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
using System.Text.RegularExpressions;
string s = args[0];
// C# の \d は全角の「1」にも合うので、[0-9] と書いて半角の数字だけを受け付ける。
// $ は最後の改行の前にも合うので、文字列の終わりだけに合う \z を使う
if (Regex.IsMatch(s, @"^[0-9]{3}-?[0-9]{4}\z"))
{
string digits = s.Replace("-", "");
Console.WriteLine($"OK: {digits.Substring(0, 3)}-{digits.Substring(3)}");
}
else
{
Console.WriteLine("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)");
}
動かし方(ファイルを保存したフォルダで) ・ C# を入れる
$ dotnet run zipcode.cs -- 123-4567
zipcode.rs1.99.0 で 7 / 7 件の出力が正解と一致
// 受け取った文字列が郵便番号の形(半角の数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
fn main() {
let args: Vec<String> = std::env::args().collect();
let s: Vec<char> = args[1].chars().collect();
// 4文字目のハイフンは有っても無くてもよい。有れば取り除いて、数字7つだけにする
let digits: Vec<char> = if s.len() == 8 && s[3] == '-' {
[&s[..3], &s[4..]].concat()
} else {
s.clone()
};
// is_numeric() は全角の「1」にも true を返すので、半角の 0〜9 だけに合う is_ascii_digit() を使う
if digits.len() == 7 && digits.iter().all(|c| c.is_ascii_digit()) {
let a: String = digits[..3].iter().collect();
let b: String = digits[3..].iter().collect();
println!("OK: {a}-{b}");
} else {
println!("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)");
}
}
動かし方(ファイルを保存したフォルダで) ・ Rust を入れる
$ rustc zipcode.rs && ./zipcode 123-4567
zipcode.rb4.0.7 で 7 / 7 件の出力が正解と一致
# 郵便番号の形(数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
s = ARGV[0]
# 半角の数字だけを受け付けるように [0-9] と書く。Ruby の ^ と $ は「行」の頭と終わりなので、文字列全体は \A と \z で囲む
if s.match?(/\A[0-9]{3}-?[0-9]{4}\z/)
digits = s.delete("-")
puts "OK: #{digits[0, 3]}-#{digits[3..]}"
else
puts "NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)"
end
動かし方(ファイルを保存したフォルダで) ・ Ruby を入れる
$ ruby zipcode.rb 123-4567
zipcode.kt2.4.21 で 7 / 7 件の出力が正解と一致
// 受け取った文字列が郵便番号の形(半角の数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
fun main(args: Array<String>) {
val s = args[0]
// \d ではなく [0-9] と書いて、半角の数字だけを受け付けることをはっきりさせる
if (s.matches(Regex("[0-9]{3}-?[0-9]{4}"))) {
val digits = s.replace("-", "")
println("OK: ${digits.substring(0, 3)}-${digits.substring(3)}")
} else {
println("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)")
}
}
動かし方(ファイルを保存したフォルダで) ・ Kotlin を入れる
$ kotlinc zipcode.kt -include-runtime -d zipcode.jar && java -jar zipcode.jar 123-4567
zipcode.swift6.3.3 で 7 / 7 件の出力が正解と一致
// 受け取った文字列が郵便番号の形(半角の数字3桁・ハイフン・4桁、またはハイフンなし7桁)かを確かめる
let s = CommandLine.arguments[1]
// Swift の \d は全角の「1」にも合うので、[0-9] と書いて半角の数字だけを受け付ける
let pattern = try! Regex("[0-9]{3}-?[0-9]{4}")
if s.wholeMatch(of: pattern) != nil {
let digits = s.replacing("-", with: "")
print("OK: \(digits.prefix(3))-\(digits.dropFirst(3))")
} else {
print("NG: 郵便番号は半角の数字7桁で入力してください(例 123-4567)")
}
動かし方(ファイルを保存したフォルダで) ・ Swift を入れる
$ swift zipcode.swift 123-4567
言語でつまずきやすいところ
初心者がよく書く形(各言語で最初に習う書き方をそのまま使ったもの)を、同じ正解表で動かした結果です。言語を出した答えごとにまとめ、赤い字はその言語が実際に出した間違った行です。
| 入力 | 初心者がよく書く形の答え(10言語) |
|---|---|
ハイフンあり123-4567 | ✓10言語すべて正解 |
ハイフンなし1234567 | ✓10言語すべて正解 |
桁が足りない123-456 | ✓10言語すべて正解 |
全角の数字123-4567 | ✓10言語すべて正解 |
全角の数字(ハイフンなし)1234567 |
|
文字が混ざる12a-4567 | ✓10言語すべて正解 |
前後に空白"123-4567 " | ✓10言語すべて正解 |
この回でやること
- 文字列が決まった形に合うかを、正規表現で確かめる
- 合っていれば形をそろえて出し、合っていなければ理由を出す
解説
正規表現
^[0-9]{3}-?[0-9]{4}$ は「先頭から、0〜9 の数字3つ、ハイフンは有っても無くてもよい、数字4つで終わる」という意味です。問い合わせフォームの入力チェックでよく使います。
\d は全角の数字にも合うことがある
数字を表す \d は、言語によって合う文字が違います。
| 言語 | \d が全角の「1」に合うか |
|---|---|
| Python・C#・Swift | 合う(あらゆる言語の数字に合う) |
| JavaScript・Java・Kotlin・Go・Ruby・PHP | 合わない(半角の 0〜9 だけ) |
Rust には標準の正規表現がありません。1文字ずつ確かめるときに is_numeric() を使うと全角の数字も通るので、is_ascii_digit() を使います。
全角の数字を通してしまうと、そのあとの処理(数としての計算や、データベースへの保存)で困ることがあります。この回では \d ではなく [0-9] と書いて、半角だけを受け付けています。
前後の空白
コピーして貼り付けた値には、最後に空白が入ることがあります。この回では空白も「形が違う」として NG にしています。実際のフォームでは、確かめる前に空白を取り除く(trim)のが親切です。
練習
- 全角の数字を半角に直してから確かめるようにしてみましょう
- 電話番号(例 03-1234-5678)を確かめるプログラムを書いてみましょう