結論:Androidで半角/全角区別する正規表現は[0-9]
や[a-zA-Z0-9_]
を明示的に使用しよう
端的に言うと\d
を使用しても全角数字もマッチ、\w
は全角英数字もマッチするよ、というお話
起こったこと
先日、業務で「インプットからの文字列に、半角数字以外が含まれている場合はバリデーションエラーを表示する」というごくごく普通の要件を満たす正規表現を書いていました。
KMMでロジックを記載しテストコードもバッチリ通っているのに、いざAndroidで試そうと全角数字を入力してところバリデーションエラーが出ない!!という状況に至りました。
もしかして、Kotlin/Native → Kotlin/JVMでの不具合か?と思って
Kolint onlineで試したところ
fun main() {
println("全角 1 is ${isError("1")}")
println("半角 1 is ${isError("1")}")
}
fun isError(value: String): String {
val regex = Regex("""\D+""")
if (value.contains(regex)) return "Error"
return "OK"
}
// Kotlin
// 全角 1 is Error
// 半角 1 is OK
// Android
// 全角 1 is OK
// 半角 1 is OK
嘘やん、、、
なんでAndroidだけ違うの、、、
ちなみにiOSではKotlinと同じく想定通りに動きました。
解決
fun main() {
println("全角 1 is ${isError("1")}")
println("半角 1 is ${isError("1")}")
}
fun isError(value: String): String {
val regex = Regex("""[^0-9]+""")
if (value.contains(regex)) return "Error"
return "OK"
}
// Kotlinも Androidも
// 全角 1 is Error
// 半角 1 is OK
冒頭にも書いたように明示的に文字クラスを使うとAndroidでも想定通りの結果になりました。
原因
結局のところあまり深く理解していないのですが
Unicodeの文字判定に依存するらしいです(小並感)
人に教えてもらうまで結構詰まってました。
メタ文字があるのに文字クラスを使用するとAndroid studioでめちゃくちゃ波線がでるけど、KDocに残してLintはsuppressしましょう!
余談
もともとInt型に変換できればええやろ!という実装がなされていたのですが、それだとそもそもKotlinで全角/半角の区別がないようです。
fun main() {
println("全角 1 is ${isError("1")}")
println("半角 1 is ${isError("1")}")
}
fun isError(value: String): String {
if (value.toIntOrNull() == null) return "Error"
return "OK"
}
// 全角 1 is OK
// 半角 1 is OK