2008/08/18

Hpricotでテキスト表示させると「 」が「?」になる

対処方法

require 'hpricot'
require 'hpricot/xchar'

Hpricot::XChar::PREDEFINED_U.merge!({" " => 32})←①
or
Hpricot::XChar::PREDEFINED_U.update({" " => 32})←②

doc = Hpricot('

before after

')
puts doc.inner_text

①または②を記載する。
「 」へのエンコード対応表がないので原因。
投稿を公開

JavaでのHTMLパース

protected void regist(String path) throws Exception {

try {

log.info("regist file:" + path);


String url = getURLFromPath(path);
if (StringUtils.isBlank(url)) {
log.warn("path format invalid:" + path);
return;
}

// URLコンテンツテーブル検索
Map req = new HashMap();
req.put("url", url);
List resList = urlContentSQL.select(req);

// 複数セーブポイント
// urlContentSQL.getSqlMapClient().startTransaction()

String uriFileName = path;
if (uriFileName.indexOf(':') == -1)
uriFileName = "file:" + uriFileName;

Source source = new Source(new InputStreamReader(new FileInputStream(uriFileName), "JISAutoDetect"));

String title = getTitle(source);
String txt = source.getTextExtractor().setIncludeAttributes(true).toString();
String fullTxt = txt;
String orgTxt = source.toString();

req.put("title", title);
req.put("fullTxt", fullTxt);
req.put("orgTxt", orgTxt);
// modified_datetime // デフォルトで設定される
req.put("modifiedUsrId", usrId);

String contentId = null;
// URLコンテンツテーブル新規作成
if (resList == null || resList.size() == 0) {

log.info("insert url_content");

// created_datetime デフォルトで設定される

contentId = MsgDBUtils.getUUID();
req.put("createdUsrId", usrId);
req.put("id", contentId);
urlContentSQL.insert(req);

}
// URLコンテンツテーブル更新
else {

log.info("update url_content");

contentId = (String)resList.get(0).get("id");
urlContentSQL.update(req);

}

// コンテンツタグ対応を検索
String seedUrlId = getSeedUrlIdFromPath(path);
List
resSeedUrlList = getSeedUrlIdData(seedUrlId);
if (resSeedUrlList == null || resSeedUrlList.size() == 0) {
log.error("seed_url.seed_url_id invalid:" + seedUrlId);
return;
// throw new Exception("seed_url.seed_url_id invalid:" + seedUrlId);
}

for (Map resSeedUrl : resSeedUrlList) {

Map reqContentTagMap = new HashMap();
List
resContentTagMapList = contentTagMapSQL.select(reqContentTagMap);

// コンテンツタグ対応新規作成
if (resContentTagMapList == null || resContentTagMapList.size() == 0) {

log.info("insert content_tag_map");

reqContentTagMap.put("id", MsgDBUtils.getUUID());
contentTagMapSQL.insert(reqContentTagMap);

}

}

}
catch (Exception e) {
log.error("regist error", e);
throw e;

}


}

JavaでのHTMLパース

protected void regist(String path) throws Exception {

try {

log.info("regist file:" + path);


String url = getURLFromPath(path);
if (StringUtils.isBlank(url)) {
log.warn("path format invalid:" + path);
return;
}

// URLコンテンツテーブル検索
Map req = new HashMap();
req.put("url", url);
List resList = urlContentSQL.select(req);

// 複数セーブポイント
// urlContentSQL.getSqlMapClient().startTransaction()

String uriFileName = path;
if (uriFileName.indexOf(':') == -1)
uriFileName = "file:" + uriFileName;

Source source = new Source(new InputStreamReader(new FileInputStream(uriFileName), "JISAutoDetect"));

String title = getTitle(source);
String txt = source.getTextExtractor().setIncludeAttributes(true).toString();
String fullTxt = txt;
String orgTxt = source.toString();

req.put("title", title);
req.put("fullTxt", fullTxt);
req.put("orgTxt", orgTxt);
// modified_datetime // デフォルトで設定される
req.put("modifiedUsrId", usrId);

String contentId = null;
// URLコンテンツテーブル新規作成
if (resList == null || resList.size() == 0) {

log.info("insert url_content");

// created_datetime デフォルトで設定される

contentId = MsgDBUtils.getUUID();
req.put("createdUsrId", usrId);
req.put("id", contentId);
urlContentSQL.insert(req);

}
// URLコンテンツテーブル更新
else {

log.info("update url_content");

contentId = (String)resList.get(0).get("id");
urlContentSQL.update(req);

}

// コンテンツタグ対応を検索
String seedUrlId = getSeedUrlIdFromPath(path);
List
resSeedUrlList = getSeedUrlIdData(seedUrlId);
if (resSeedUrlList == null || resSeedUrlList.size() == 0) {
log.error("seed_url.seed_url_id invalid:" + seedUrlId);
return;
// throw new Exception("seed_url.seed_url_id invalid:" + seedUrlId);
}

for (Map resSeedUrl : resSeedUrlList) {

Map reqContentTagMap = new HashMap();
List
resContentTagMapList = contentTagMapSQL.select(reqContentTagMap);

// コンテンツタグ対応新規作成
if (resContentTagMapList == null || resContentTagMapList.size() == 0) {

log.info("insert content_tag_map");

reqContentTagMap.put("id", MsgDBUtils.getUUID());
contentTagMapSQL.insert(reqContentTagMap);

}

}

}
catch (Exception e) {
log.error("regist error", e);
throw e;

}


}

2008/08/14

rubyのコード推測精度が低いことへの対処

NKFのコード推測メソッドを利用する。
str.kconv(Kconv::UTF8, NKF.guess(str))
str.toutf8だとうまくいかないことが多い。

rubyのエンコードde

2008/08/13

JRUBYでCAYENNEをつかう

registerNewObjectの部分でうまく動作しない。
ひとひねり必要そうだ。

JRUBY

配列内の項目を特定の値に入れ替える

itemList = ["aaaa", "bbbb", "cccc"]

itemList.collect! {|item|

if item == "bbbb" then
'dddd'
else
item
end
}

2008/06/04

VBSでUNICDEの全角文字数を判断する関数


' UNICODEでも全角半角の文字数を判断するメソッド
Function LenByte(ByVal s)

Dim c, i, k

c = 0

For i = 0 To Len(s) - 1

k = Mid(s, i + 1, 1)

If (Asc(k) And &HFF00) = 0 Then

c = c + 1

Else

c = c + 2

End If

Next

LenByte = c

End Function

2008/05/27

cayenne+pgpoolではまりどころ

以前、pgpoolをcayenneで利用する為について記載した。
しかし、pgpoolやjndiで再接続機能を利用する為には更に工夫が必要だ。


以下のようにTransactionクラスを継承したクラスを作成する。


import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;

import org.apache.cayenne.CayenneException;
import org.apache.cayenne.access.Transaction;

public class NoTransaction extends Transaction {

@Override
public void begin() {
}

@Override
public void commit() throws IllegalStateException, SQLException, CayenneException {
close();
}

@Override
public void rollback() throws IllegalStateException, SQLException, CayenneException {
close();
}

protected void close() {

if (connections == null || connections.isEmpty()) {
return;
}

Iterator it = connections.values().iterator();
while (it.hasNext()) {
try {

((Connection) it.next()).close();
}
catch (Throwable th) {
}
}

}

}


そしてSQLを実行する部分をトランザクションしないよう以下のようにする。


Transaction tx = new NoTransaction();
try {

Transaction.bindThreadTransaction(tx);

Expression expression = ExpressionFactory.matchExp(~);
SelectQuery query = new SelectQuery(Ent.class, expression);
List entList = getDataContext().performQuery(query);
if (CollectionUtils.isEmpty(entList))
return null;

return entList;

}
finally {
try {
tx.commit();
} catch (Exception e) {

}
}

2008/03/31

rubyのgem自身アップグレード

gem update --system

Tomcat5.5のログ設定

Tomcat5.5ではデフォルトのログ出力機能はlog4jからJDKのjava.util.loggingを利用するように変更された。
log4jのほうが出力フォーマットの設定等柔軟に変更できるのでlog4jにログ出力を統一しようと思ったら以下のことをやらなければならない。
①${tomcat.home}/common/libにcommons-logginとlog4jのライブラリをおく。

②${tomcat.home}/common/classesにlog4j.xmlをおく

2008/03/19

JavaのMessageFormat.formatメソッドについて

変換対象の文字列に「{」や「'」が含まれている場合は
以下のように「'{'」や「''」とする。

String s = "function aaa() '{' alert(''{0}''); '}' ";

結構はまりどころ。

Prototypeでのブラウザ判別


if (Prototype.Browser.IE6)
alert('You are using IE6');
else if (Prototype.Browser.IE)
alert('You are using IE7');
else
alert('Not IE');

2008/03/13

cayenneでPKを指定してデータを取得する時のはまりどころ

cayenneではDataObjectUtils.objectForPKによってPKを指定してデータを取得できるが
このメソッドはデフォルトでキャッシュしてしまう。なので以下のメソッドを利用しましょう。

ObjectIdQuery query = new ObjectIdQuery(new ObjectId(AAAA.class.getSimpleName(), AAAA.ID_PK_COLUMN, id), false, ObjectIdQuery.CACHE_REFRESH);
(AAAA)DataObjectUtils.objectForQuery(dataContext, query);

ここでポイントはCACHE_REFRESH を指定して常に新しい情報を取得していること。
これを指定しないとだめ
pgpoolでロードバランサ機能を利用する場合、トランザクション内のSELECTでないことが条件の一つにあげられるがCayenneではデフォルトでトランザクションを開始してしまう。

そこでトランザクションを開始させない方法は以下

try {
// トランザクションは実行しない
Transaction.bindThreadTransaction(Transaction.noTransaction());

// なんらかのクエリを発行
}
finally {
// 元にもどす
Transaction.bindThreadTransaction(null);
}

2008/01/15

postgresのはまりどころ:配列にNULLがふくまれていた場合

配列の値

Array['aaaa', 'bbbb', null]

という値をJDBCで取得する場合にgetArray()で取得された結果は
Stringの配列となります。

但し、nullの値も'null'に文字列化されセットされます。
これバグだよなー。

2007/12/11

ibatisのはまりどころ:JDBCタイプのARRAYを利用するその2

ibatisで配列を参照できるようになったと思ったら更新ではまった。

ARRAYタイプの更新は当然JDBC実装に依存する。
僕が利用していたDBはPostgres。

JDBC規約?によればsetObjectを利用すべしとのこと。
でもJDBCソースみてみるとsetObject内には配列時の対応はないし、setArrayを利用するにもArrayはIFの為、オブジェクトの生成しずらい。
Postgresは配列対応なのになんでそういう実装がないの?
Webで検索してもよい資料はでてこない。

なので以下のような対応をしました。
一応、これでできることはできるんだけどパフォーマンスがでないだろうなー。

本気でやるならJDBCを拡張するかしたほうがよいと思うがなんでみんな対応しないんだろう?

// -- 以下コード
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;


import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;
import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;

public class ArrayTypeHandlerCallback implements TypeHandlerCallback {



public Object getResult(ResultGetter resultGetter) throws SQLException {
Array array = resultGetter.getArray();
if (array == null)
return null;
else
return Arrays.asList((Object[])array.getArray());
}

public void setParameter(ParameterSetter parameterSetter, Object obj)
throws SQLException {

if(obj == null){
parameterSetter.setNull(Types.ARRAY);
} else {

List lst = (List)obj;
parameterSetter.setArray(createArray(parameterSetter, lst));
}

}

public Object valueOf(String str) {
// 特につかっていないので今回は実装しない
return "";
}

public Array createArray(ParameterSetter parameterSetter, List list) throws SQLException {

Array array = null;
PreparedStatement ps = null;
try {

String arrayPhrase = toArrayPhrase(list);
if (arrayPhrase == null)
return null;

ps = parameterSetter.getPreparedStatement();
Connection cn = ps.getConnection();
String sql = "SELECT " + arrayPhrase;
ps = cn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
rs.next();
array = rs.getArray(1);
}
finally {

// 初回のPreparedStatementはIbatisによって制御されるのでcloseはしない
if (ps != null)
ps.close();
}

return array;

}

public String toArrayPhrase(List list) {

if (list == null || list.isEmpty())
return null;

int cnt = 0;
int max = list.size();
String value = null;
StringBuffer sb = new StringBuffer();
sb.append("ARRAY[");
for(Object obj : list) {

cnt++;

value = decorateSQLValue(obj);
if (cnt != max)
sb.append(value + ",");
else
sb.append(value);

}
sb.append("]");

return sb.toString();
}

public String decorateSQLValue(Object obj) {

if (obj == null)
return null;

if (obj instanceof String) {
return "'" + obj.toString() + "'";
}
else if (obj instanceof Integer) {
return obj.toString();
}
else {
return "'" + obj.toString() + "'";
}

}

}

2007/11/28

DBのはまりどころ

よく実行するSQLのパフォーマンスを向上させるために
javaのpreparedstatementにてSQLを実行する。

昨今のフレームワークもSQLはプリコンパイルした形で実行することが多い。
SQLインジェクション対策にもなっている。

これは一見、よさそうに見える。
但し、以下の点に注意
パラメータ数が多いもの。
この場合、SQLをプリコンパイルするのにかなりの時間を要するので逆に
パフォーマンス低下を発生させかねない。
簡単な確認しか行っていないがPostgres8.2.5ではだいたい100以上のパラメータ
をつけると急激にパフォーマンス低下が発生した。
みなさんどうですか?

2007/11/07

全文検索時に利用できるパラメータトークンロジック

Pattern pattern = Pattern.compile("(((?<=\\s| |\\A)\".+?\"(?=\\s| |\\Z))|((?<=\\s| |\\A)[^\\s| ]+?(?=\\s| |\\Z)))");
Matcher matcher = pattern.matcher("\"aaaa  \" bbbb  \" cccc\" dddd");
StringBuffer sb = new StringBuffer();
while (matcher.find()) {

System.out.println("発見0:[" + matcher.group(0) + "]");

}
matcher.appendTail(sb);
System.out.println(sb.toString());

または

// Pattern pattern = Pattern.compile("(((?<=\\s| |\t|\\A)\".+?\"(?=\\s| |\t|\\Z))|((\\s|\t| )+))");
// Pattern pattern2 = Pattern.compile("((?<=\\s| |\t|\\A)\".+?\"(?=\\s| |\t|\\Z))");
// Matcher matcher = pattern.matcher("\"aaaa  \" bbbb   \" cccc\" dddd");
// Matcher matcher2;
// StringBuffer sb = new StringBuffer();
// while (matcher.find()) {
//
// System.out.print("発見:[" + matcher.group(0) + "]");
// matcher2 = pattern2.matcher(matcher.group(0).toString());
// // 「"」でかこまれた文字列
// if (matcher2.find()) {
// System.out.println("0:[" + matcher.group(0) + "]");
// matcher.appendReplacement(sb, " +" + matcher.group(0));
// }
// // 単純な空白文字列
// else {
// System.out.println("1:[" + matcher.group(0) + "]");
// matcher.appendReplacement(sb, " +");
// }
// }
// matcher.appendTail(sb);
// System.out.println(sb.toString());