S-JIS[2009-02-01] 変更履歴
htlexタスクを使ってテキストを操作(検索・置換)するサンプルです。
テキストというのはタグとタグの間にある文字列のことです。
コメント<!-- 〜 -->は含まれません。
「ひしだま」という文言が含まれているテキストを検索する例です。
<?xml version="1.0" encoding="Shift_JIS"?> <project name="HtHtmlLexer sample" default="find" basedir="."> <typedef resource="htlex.typedef.properties" classpath="htlexer.jar" /> <property name="html" location="C:\temp\lexer\from" /> <property name="todir" location="C:\temp\lexer\to" /> <target name="find"> <htlex> <fileset dir="${html}"> <include name="**/*.html" /> </fileset> <htconv encoding="MS932" logMatch="info"> <ctext text="ひしだま" textMatch="find" /> </htconv> </htlex> </target> </project>
Buildfile: HtLexerSample\bin\build.xml
find:
〜
match: C:\temp\lexer\from\index.html
8 ひしだまのホームページ(Hishidama's HomePage)
21 ひしだま's ホームページ
24 ひしだまの
55 ひしだまの携帯ページ
〜
BUILD SUCCESSFUL
Total time: 15 seconds
logMatchを指定しているので、対象のテキストを見つけた際に、そのファイル名と行番号およびタグの内容を出力します。
textMatchにfindを指定しないと、完全に一致するテキストだけが表示されます。
textMatchにfindを指定した場合は正規表現による検索となるので、複数の文字を指定できます。
(HTMLエスケープされていない)「<」と「>」を検索する例です。
<target name="find2">
<htlex>
<fileset dir="${html}">
<include name="**/*.html" />
</fileset>
<htconv encoding="MS932" logMatch="info">
<ctext text="[\<\>]" textMatch="find" />
</htconv>
</htlex>
</target>
正規表現では、角括弧[〜]でくくると、その中の個々の文字にマッチします。
ただ、「<」や「>」といった記号は、「\」によってエスケープする必要があるようです。
詳細はjava.util.regex.PatternのJavadocを参照して下さい。
text属性の代わりにtext要素を使うことも出来ます。
ついでにCDATAセクションも使えば、「<」や「>」を直接build.xml上に記述できます。
<target name="find3">
<htlex>
<fileset dir="${html}">
<include name="**/*.html" />
</fileset>
<htconv encoding="MS932" logMatch="info">
<ctext textMatch="find">
<text><![CDATA[[\<\>]]]></text>
</ctext>
</htconv>
</htlex>
</target>
「<」を「<」に、「>」を「>」に置換する例です。
<target name="replace"> <htlex todir="${todir}"> <fileset dir="${html}"> <include name="**/*.html" /> </fileset> <htconv encoding="MS932" logConvert="info" logWriteFile="warn"> <ctext text="\<" textMatch="find" newText="&lt;" newTextOperation="replaceAll" /> <ctext text="\>" textMatch="find" newText="&gt;" newTextOperation="replaceAll" /> </htconv> </htlex> </target>
単純な置換や正規表現では実現できない変換を行いたい場合は、データタイプを拡張して独自のロジックを実装することが出来ます。
以下、ピリオド区切りの年月・日付(yyyy.Mやyyyy.M.d
)をハイフン区切り(yyyy-MMやyyyy-MM-dd
)に変更する例です。
(KANZAKIさんの日付の表記に関するノートによると、YYYY-MM-DDがW3Cの提唱する標準らしいので)
<typedef resource="htlex.typedef.properties" classpath="htlexer.jar;../classes" loaderref="htlex" /> <typedef name="ctext.ymd" classname="sample.ant.types.DateText" loaderref="htlex" />
↑まず、独自タスク「ctext.ymd
」を定義します。
htlexer.jarのクラスパスに独自クラスのパスも加え、またloaderrefも同じ値を指定します。
<target name="text_change-line ymd">
<htlex todir="${todir}">
<fileset dir="${html}">
<include name="**/*.html" />
</fileset>
<htconv encoding="MS932" logConvert="info" logWriteFile="warn">
<ctext.ymd text="\." textMatch="find" minYear="1997" maxYear="2009">
<newtext>original</newtext>
</ctext.ymd>
</htconv>
</htlex>
</target>
htconvの中に、独自タスクを指定します。
デフォルトのctextの仕様で、変換を行う場合はnewTextの指定が必須なので、適当な文字列を渡しておきます。
また、検索対象のマッチング条件も必要なので、今回は「ピリオド区切りの日付」が対象なのでピリオドが含まれる文字列を条件としています。
minYear・maxYearは、独自タスクで使用する属性です。
package sample.ant.types; import jp.hishidama.ant.types.htlex.TextType; public class DateText extends TextType { protected int minYear = 1980, maxYear = 9999; public void setMinYear(int y) { minYear = y; } public void setMaxYear(int y) { maxYear = y; }
独自クラスでは、ctextタグのクラスであるTextTypeを継承します。
そして、convert(String, String)メソッドをオーバーライドします。このメソッドが、文字列変換で実際に呼ばれるメソッドです。
private String date; @Override protected String convert(String text, String newText) { StringBuilder sb = new StringBuilder(text.length() + 8); for (int i = 0; i < text.length();) { int len = dateLen(text, i); if (len <= 0) { sb.append(text.charAt(i++)); } else { if (date != null) { sb.append(date); i += len; } else { for (int j = 0; j < len; j++) { sb.append(text.charAt(i++)); } } } } return sb.toString(); }
protected int dateLen(String s, int pos) { date = null; int period = 0; int[] ppos = new int[2]; // 数字とピリオドだけの塊を抽出する int end = pos; for (; end < s.length(); end++) { char c = s.charAt(end); if (c == '.') { if (period < ppos.length) { ppos[period] = end; } period++; } else if (c < '0' || '9' < c) { break; } } int len = end - pos; if (len <= 0) { return len; } int y, m, d; switch (period) { case 1: // y.m y = subinteger(s, pos, ppos[0]); m = subinteger(s, ppos[0] + 1, end); d = -1; break; case 2: // y.m.d y = subinteger(s, pos, ppos[0]); m = subinteger(s, ppos[0] + 1, ppos[1]); d = subinteger(s, ppos[1] + 1, end); break; default: return len; } // 範囲外の日付だったら変換しない if (y < minYear || y > maxYear) { return len; } if (m < 1 || m > 12) { return len; } switch (period) { case 1: // y.m date = String.format("%04d-%02d", y, m); break; case 2: // y.m.d if (d < 1 || d > 31) { return len; } date = String.format("%04d-%02d-%02d", y, m, d); break; } return len; }
protected int subinteger(String s, int start, int end) { if (start >= end) { return -1; } if (end - start > 4) { return -1; } return Integer.parseInt(s.substring(start, end)); } }