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));
}
}