S-JIS[2009-02-01] 変更履歴

htlexタスク テキスト操作サンプル

htlexタスクを使ってテキストを操作(検索・置換)するサンプルです。

テキストというのはタグとタグの間にある文字列のことです。
コメント<!-- 〜 -->は含まれません。


テキストで検索する例

「ひしだま」という文言が含まれているテキストを検索する例です。

build.xml:

<?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="[\&lt;\&gt;]" 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>

テキストを変更する例

「<」を「&lt;」に、「>」を「&gt;」に置換する例です。

	<target name="replace">
		<htlex todir="${todir}">
			<fileset dir="${html}">
				<include name="**/*.html" />
			</fileset>
			<htconv encoding="MS932" logConvert="info" logWriteFile="warn">
				<ctext text="\&lt;" textMatch="find" newText="&amp;lt;" newTextOperation="replaceAll" />
				<ctext text="\&gt;" textMatch="find" newText="&amp;gt;" newTextOperation="replaceAll" />
			</htconv>
		</htlex>
	</target>

オリジナルの変換を行う例

単純な置換や正規表現では実現できない変換を行いたい場合は、データタイプを拡張して独自のロジックを実装することが出来ます。

以下、ピリオド区切りの年月・日付(yyyy.Mやyyyy.M.d)をハイフン区切り(yyyy-MMやyyyy-MM-dd)に変更する例です。
(KANZAKIさんの日付の表記に関するノートによると、YYYY-MM-DDがW3Cの提唱する標準らしいので)

build.xml:

	<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の指定が必須なので、適当な文字列を渡しておきます。
また、検索対象のマッチング条件も必要なので、今回は「ピリオド区切りの日付」が対象なのでピリオドが含まれる文字列を条件としています。
minYearmaxYearは、独自タスクで使用する属性です。

sample/ant/types/DateText.java:

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

htlexタスクへ戻る / 自作ソフトへ戻る / 技術メモへ行く
メールの送信先:ひしだま