入力から空白を削りたいといった要件がたまにあるが、空白と一言で言っても半角スペース・全角スペースだけでなく、ユニコードには様々な空白文字がある。調べた範囲では29種類あった。これらの空白文字を正規表現で一網打尽する方法はないか検証してみた。検証のために用いたコードは、この記事の最後に載せてある。
検証結果
検証結果は下の表にまとめた。各行が空白文字に、各列が正規表現になっている。その文字がその正規表現でマッチしたものには「o」を付けた。
| code | name | \p{javaWhitespace} |
\p{javaSpaceChar} |
\p{Zs} |
\s |
\p{Blank} |
見た目 |
|---|---|---|---|---|---|---|---|
| \u0009 | HORIZONTAL TABULATION | o | o | o |
foo\u0009bar
|
||
| \u000A | LINE FEED | o | o |
foo\u000Abar
|
|||
| \u000B | VERTICAL TABULATION | o | o |
foo\u000Bbar
|
|||
| \u000C | FORM FEED | o | o |
foo\u000Cbar
|
|||
| \u000D | CARRIAGE RETURN | o | o |
foo\u000Dbar
|
|||
| \u001C | FILE SEPARATOR | o |
foo\u001Cbar
|
||||
| \u001D | GROUP SEPARATOR | o |
foo\u001Dbar
|
||||
| \u001E | RECORD SEPARATOR | o |
foo\u001Ebar
|
||||
| \u001F | UNIT SEPARATOR | o |
foo\u001Fbar
|
||||
| \u0020 | SPACE | o | o | o | o | o |
foo bar
|
| \u00A0 | NO-BREAK SPACE | o | o |
foo bar
|
|||
| \u1680 | OGHAM SPACE MARK | o | o | o |
foo bar
|
||
| \u180E | MONGOLIAN VOWEL SEPARATOR | o | o | o |
foobar
|
||
| \u2000 | EN QUAD | o | o | o |
foo bar
|
||
| \u2001 | EM QUAD | o | o | o |
foo bar
|
||
| \u2002 | EN SPACE | o | o | o |
foo bar
|
||
| \u2003 | EM SPACE | o | o | o |
foo bar
|
||
| \u2004 | THREE-PER-EM SPACE | o | o | o |
foo bar
|
||
| \u2005 | FOUR-PER-EM SPACE | o | o | o |
foo bar
|
||
| \u2006 | SIX-PER-EM SPACE | o | o | o |
foo bar
|
||
| \u2007 | FIGURE SPACE | o | o |
foo bar
|
|||
| \u2008 | PUNCTUATION SPACE | o | o | o |
foo bar
|
||
| \u2009 | THIN SPACE | o | o | o |
foo bar
|
||
| \u200A | HAIR SPACE | o | o | o |
foo bar
|
||
| \u200B | ZERO WIDTH SPACE | foo``bar |
|||||
| \u202F | NARROW NO-BREAK SPACE | o | o |
foo bar
|
|||
| \u205F | MEDIUM MATHEMATICAL SPACE | o | o | o |
foo bar
|
||
| \u3000 | IDEOGRAPHIC SPACE | o | o | o |
foo bar
|
||
| \uFEFF | ZERO WIDTH NO-BREAK SPACE | foo``bar |
- 全角スペースと言われるものはこの表では
\u3000になる。 - \u001F 以下の文字はMarkdownで表示しきれないものがあるので見た目には反映していない。
\p{javaWhitespace} は java.lang.Character の isWhitespace と同じものらしいが、一番多く空白文字にマッチした。それでも、マッチしない文字もあったので、\p{javaWhitespace}とユニコードエスケープを組み合わせた正規表現を作る必要がありそう。
全角スペースと半角スペースとタブくらいにマッチすればいい要件なら、\p{javaSpaceChar} や \p{Zs} で十分。
検証用コード
import org.scalatest._
class CompilationSpec extends FeatureSpec with GivenWhenThen with BeforeAndAfter with ParallelTestExecution with Matchers {
feature("Playground") {
scenario("test \\p{javaSpaceChar}") {
def isS(char: String) = """\s""".r.findFirstIn(char).nonEmpty
def isZs(char: String) = """\p{Zs}""".r.findFirstIn(char).nonEmpty
def isJavaSpaceChar(char: String) = """\p{javaSpaceChar}""".r.findFirstIn(char).nonEmpty
def isJavaWhitespace(char: String) = """\p{javaWhitespace}""".r.findFirstIn(char).nonEmpty
def isBlank(char: String) = """\p{Blank}""".r.findFirstIn(char).nonEmpty
implicit class MyBoolean(bool: Boolean) {
def o: String = {
bool match {
case true => "o"
case false => ""
}
}
}
val unicodeSpaces = Seq(
Seq("\u0009", "HORIZONTAL TABULATION"),
Seq("\u000A", "LINE FEED"),
Seq("\u000B", "VERTICAL TABULATION"),
Seq("\u000C", "FORM FEED"),
Seq("\u000D", "CARRIAGE RETURN"),
Seq("\u001C", "FILE SEPARATOR"),
Seq("\u001D", "GROUP SEPARATOR"),
Seq("\u001E", "RECORD SEPARATOR"),
Seq("\u001F", "UNIT SEPARATOR"),
Seq("\u0020", "SPACE"),
Seq("\u00A0", "NO-BREAK SPACE"),
Seq("\u1680", "OGHAM SPACE MARK"),
Seq("\u180E", "MONGOLIAN VOWEL SEPARATOR"),
Seq("\u2000", "EN QUAD"),
Seq("\u2001", "EM QUAD"),
Seq("\u2002", "EN SPACE"),
Seq("\u2003", "EM SPACE"),
Seq("\u2004", "THREE-PER-EM SPACE"),
Seq("\u2005", "FOUR-PER-EM SPACE"),
Seq("\u2006", "SIX-PER-EM SPACE"),
Seq("\u2007", "FIGURE SPACE"),
Seq("\u2008", "PUNCTUATION SPACE"),
Seq("\u2009", "THIN SPACE"),
Seq("\u200A", "HAIR SPACE"),
Seq("\u200B", "ZERO WIDTH SPACE"),
Seq("\u202F", "NARROW NO-BREAK SPACE"),
Seq("\u205F", "MEDIUM MATHEMATICAL SPACE"),
Seq("\u3000", "IDEOGRAPHIC SPACE"), // 全角スペース
Seq("\uFEFF", "ZERO WIDTH NO-BREAK SPACE")
)
unicodeSpaces.foreach {
case Seq(space, name) =>
val code = space.charAt(0).toInt.formatted("\\u%04X")
println(List(code, name, isJavaWhitespace(space).o, isJavaSpaceChar(space).o, isZs(space).o, isS(space).o, isBlank(space).o, "`foo`" + ({ space.charAt(0).toInt match { case a if a <= 31 => code; case a => space; } }) + "`bar`").mkString("| ", " | ", " |"))
}
}
}
}