SubwayTooter-Android-App/app/src/main/java/jp/juggler/util/AsciiPattern.kt

90 lines
3.6 KiB
Kotlin

package jp.juggler.util
import java.util.regex.Pattern
/*
java.util.regex.Pattern は Oracle JVM と Android で大きく異なる。
Androidの正規表現エンジンはICUベースで、
AndroidのAPIリファレンスで UNICODE_CHARACTER_CLASS を見ると
"This flag has no effect on Android, unicode character classes are always used." と書いてある。
\w \d \s などの文字クラスの扱いに違いがある。
JVMでUNICODE_CHARACTER_CLASSフラグなしの場合はこう
\s 空白文字: [\t\n\x0B\f\r]
\d 数字: [0-9]
\w 単語構成文字: [a-zA-Z_0-9]
JVMでUNICODE_CHARACTER_CLASSフラグありの場合はこう
\s 空白文字: \p{IsWhite_Space}
\d 数字: \p{IsDigit}
\w 単語構成文字: [\p{Alpha}\p{gc=Mn}\p{gc=Me}\p{gc=Mc}\p{Digit}\p{gc=Pc}\p{IsJoin_Control}]
ICUの場合はこう
http://userguide.icu-project.org/strings/regexp
\s Match a white space character. White space is defined as [\t\n\f\r\p{Z}].
\w Match a word character. Word characters are [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\u200c\u200d].
\d Match any character with the Unicode General Category of Nd (Number, Decimal Digit.)
とりあえず \d \D と \w と \W は凄く困るので 正規表現を書き換えてなんとかしたい。
なおJVMもICUも [A-Z[a-z]] と書くと [A-Za-z]と同じ事になる。
よって [^\w.-] を [^[A-Za-z0-9].-] に変換しても問題ない。
困るのは\W や \D の方だが、STのコードを見た感じ\Wは使っていないし、¥Dを文字クラスの中で使っていることもなかった
*/
fun String.asciiPattern(flags : Int = 0) : Pattern =
Pattern.compile(this.asciiPatternString(), flags)
fun String.asciiPatternString() : String {
val dst = StringBuilder()
dst.ensureCapacity(this.length)
var escaped = false
var insideSet = 0
var lastOpen =0
for( i in this.indices){
val c= this[i]
when {
escaped -> {
escaped = false
when(c) {
'w' -> dst.append(if(insideSet>0) "A-Za-z0-9_" else "[A-Za-z0-9_]")
'd' -> dst.append(if(insideSet>0) "0-9" else "[0-9]")
// W,Dは文字セット内では対応できないのでそのまま通す
'W' -> if(insideSet>0) dst.append('\\').append(c) else dst.append("[^A-Za-z0-9_]")
'D' -> if(insideSet>0) dst.append('\\').append(c) else dst.append("[^0-9]")
else -> dst.append('\\').append(c)
}
}
c == '\\' -> escaped = true
else -> {
dst.append(c)
if(c == '[') {
insideSet++
lastOpen=i
} else if(c == ']' && insideSet >0 && i > lastOpen+1) {
insideSet--
// [] のようなカラの文字クラスは正規表現のエラーになる。
// つまり文字クラスは最低でも1文字を含むので、
// []] のような記述は ] のみを示す文字クラスになる。
// [ ]] のような記述は 空白に続いて文字']'が出現する入力にマッチする。
// 1文字あけた次からは閉じ括弧として扱われて、
// 続いて開いてない時に登場した ] は普通の文字として扱われる。
// [[ABC][DEF]]は [ABCDEF]と同じ。入れ子になっている
// JVMのPatternでは[A-Z&&[^D-F]]のような記述は「A-ZのうちD-F以外」と解釈される
// ICUには [\p{Letter}&&\p{script=cyrillic}] や [\p{Letter}--\p{script=latin}]
// JVMと同じような記述ができるかどうかは分からない。
}
}
}
}
if(escaped) dst.append('\\')
return dst.toString()
}