倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Java と XML と空白と (5):XML の空白と Java

今回は、XML の空白を Java で扱う方法を幾つか。

  • 正規表現
  • 属性値の正規化
  • テキストの正規化

正規表現


XML での空白は ' ', '\t', '\r', '\n' の4文字なので、Java での正規表現java.util.regex.Pattern の JavaDoc 参照)で XML空白文字 (whitespace character) を表現すると

[ \t\r\n]

となります。 同様に空白 (whitespace) (空白文字の1つ以上の連なり)は

[ \t\r\n]+

となります。

属性値の正規化


属性値はパーサによって「正規化 (normalization)」という処理が行われます。 属性値の型が CDATA でない場合、この「正規化」の処理は以下のことを行います:

  • 先頭と後尾の空白を取り除く
  • 連続する空白文字を1つの空白で置き換える

★先頭と後尾の空白を取り除く★

「先頭と後尾の空白を取り除く」処理には String#trim() が適していそうですが、このメソッドで空白とみなす文字は「文字コードが \u0020 以下の文字」となっています。

幸いにも(というか、そうなるようにしてあるのでしょうが)、XML の属性値の文字列に String#trim() を適用すると、意図した通りに「先頭と後尾の空白を取り除く」ことができます

これは、「文字コードが \u0020 以下の文字」の文字の内、XML の空白文字 (' ', '\t', '\r', 'n') 以外の文字は禁止されている(使用できる文字として定義されていない)ためです。 例えば、文字コードが \u000b の文字が属性値の先頭にあった場合、「正規化」によってこの文字が除去されてはいけませんが、String#trim() では除去してしまいます。 しかし、こういった文字は属性値の中には現れないことになっています*1

★連続する空白文字を1つの空白で置き換える★

この処理は、String#replaceAll() メソッドによって実現できます。 上で定義した XML の空白を表す正規表現にも注意:

String attValue = ...;
attValue.replaceAll("[ \\t\\r\\n]+", " ");  // 正規表現にエスケープが必要

★属性の正規化★

結局のところ、「属性値の正規化」を行うには、以下の処理を行えば良いことになります(必ずしも実装がこうなっているわけではありませんが):

public String normalize(String attValue){
    return attValue.trim().replaceAll("[ \\t\\r\\n]+", " ");
}

テキストの正規化


テキスト中の空白の扱いは処理する側のアプリケーションに任せられています(@xml:space 属性がない場合)。 とはいっても、よく使われる処理は決まっているかと思います。

Nux JavaDoc API の nux.xom.pool.XOMUtil.Normalizer クラスに空白の取り扱い方を列挙してくれているので、そちらを紹介しておきましょう。 種類は5つ:

  • PRESERVE
  • STRIPE
  • REPLACE
  • TRIM
  • COLLAPSE

以下、次のサンプル XML 文書についての適用結果と共に処理方法を見ていきましょう:

<root>
  <text>
    If at first an idea does not sound absurd,
    then there is no hope for it.
        - ALBERT EINSTEIN
  </text>
</root>

★PRESERVE(保存する)★

Whitespace normalization returns the string unchanged; hence indicates no whitespace normalization should be performed at all; This is typically the default for applications.

何もしません。 @xml:space 属性が付いているのと同じです。 大抵の場合、これがアプリケーションのデフォルト処理になっています。

<root>
  <text>
    If at first an idea does not sound absurd,
    then there is no hope for it.
        - ALBERT EINSTEIN
  </text>
</root>

★STRIP(はぐ)★

Whitespace normalization removes strings that consist of whitespace-only (boundary whitespace), retaining other strings unchanged.

空白のみからなるテキストを取り除き、それ以外のテキストはそのままにします。

<root><text>
    If at first an idea does not sound absurd,
    then there is no hope for it.
        - ALBERT EINSTEIN
  </text></root>

★REPLACE(置換する)★

Whitespace normalization replaces each whitespace character in the string with a ' ' space character.

空白文字をスペース (' ') で置換します。

<root>   <text>     If at first an idea does not sound absurd,     then there is no hope for it.         - ALBERT EINSTEIN   </text> </root>

ここでは、改行文字をスペースに変換しています*2

★TRIM(切り取る)★

Whitespace normalization removes leading and trailing whitespaces, if present, ala String.trim().

先頭と後尾の空白を String#trim() メソッドを用いて取り除きます。

<root><text>If at first an idea does not sound absurd,
    then there is no hope for it.
        - ALBERT EINSTEIN</text></root>

★COLLAPSE(崩す)★

Whitespace normalization replaces each sequence of whitespace in the string by a single ' ' space character; Further, leading and trailing whitespaces are removed, if present, ala String.trim().

空白の連なりをスペース (' ') で置き換え、先頭と後尾の空白を String#trim() メソッドで取り除きます。 REPLACE & COLLAPSE。 値が単語の XML プロパティファイルまたは XHTML に使うことが多いと思います。

<root><text>If at first an idea does not sound absurd, then there is no hope for it. - ALBERT EINSTEIN</text></root>

*1:これは、HTML ではうまくいかない場合があります。 それは、HTML の空白文字の中に \u200B が含まれているためです。

*2:XML 文書では、OS の種類にかかわらず、改行は '\n' 1文字で表されます。