- PR -

jakartaOroのパターンマッチングについて

投稿者投稿内容
banboo
大ベテラン
会議室デビュー日: 2003/12/05
投稿数: 210
投稿日時: 2004-01-02 19:46
jakartaOroのパターンマッチングについて質問がございます。
行いたい事は、入力文字列があった時に、@を区切り文字として@の間の
文字列を取り出す
ということです。例えば

auto12.txtの内容

ABC@CD@DEF

で、上記のauto12.txtをプログラムへの入力ファイルとすると、
「@」の文字列部分がパターンマッチし、@を区切り文字として

ABC
CD
DEF

を出力するという事です(プログラムを下記に示す)。ここで、auto12.txt

A
B
C@C
D@D
E
F

に変更しても、同様の結果が得られる様にしたいのですが、いい方法が
思い浮かびません。一応、自分なりに解決方法を二つ考えてみました。

方法1.splitメソッドのオプションを使用する方法

http://www.jajakarta.org/oro/ja/api/org/apache/oro/text/perl/Perl5Util.html
の中で、Perl5Utilのsplitメソッドを参考にすると、
split
public void split(java.util.Collection results,
java.lang.String pattern,
java.lang.String input,
int limit)

[m]/パターン/[i][m][s][x]
頭のm は省略可能です。後ろのオプションは次のような意味を持ちます。
i アルファベットの大文字と小文字を区別しないでマッチします。
m 入力を複数行として扱います。
s 入力を1行として扱います。

と書かれてあります。reg.split(e, "/@/s", buf);
とすれば、入力ファイルの文字列を一行として扱えるかなと思ったのですが
思ったような結果が得られませんでした。文字ストリームへのアクセスは行単位が基本なので
readLine()メソッドを使用しておりますが、それを使用しない方法とか
あるのでしょうか?


方法2.改行部分を他の文字($)に置換して、改行を除去する。そして、
入力ファイルの文字列を一行にしてから、目的の処理を行う。その後
再度、$を改行に置換する。


方法1、2以外にも、いい方法が御座いましたら
ご教授お願い致します。


import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;

import org.apache.oro.text.perl.MalformedPerl5PatternException;
import org.apache.oro.text.perl.Perl5Util;

public class OroTest3 {

public static void main(String[] args) throws MalformedPerl5PatternException, IOException {

File file = new File("auto12.txt");

BufferedReader in = new BufferedReader(new FileReader(file));
String buf = null;
Perl5Util reg = new Perl5Util();

Vector e = new Vector();

while ((buf = in.readLine()) != null) {

reg.split(e, "/@/", buf);

for (int i = 0; i < e.size(); i++) {
String str = (String) e.get(i);
System.out.println(str);
}
}
}
}
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2004-01-03 07:26
方法は少なくとも三つありますね。

(1) BufferedReader#read(char[], int, int) を使う。
確保するメモリに過不足がないよう注意すること。
(ヒント: java.io.File クラス)

(2) BufferedReader#readLine() で 1 行ずつ読みながら、最後にマッチしたものを次の行に繰り越す。
この時 readLine() の仕様に注意する。
String の作りすぎにも注意。

(3) readLine() と繰り越し処理を使いながら、indexOf() と substring() で頑張る。
(正規表現が必要なほど複雑な処理でもないでしょう)
なお、indexOf() と substring() は String クラスだけのものではないこと、ここでわざと引数の数と型を示していないこと に注意する。
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2004-01-03 07:39
もう一つありました。

(1') BufferedReader#read(char[], int, int) を使うが、適切なサイズだけ読み込み、繰り越し処理を行う。

これと比べると (1) のヒントは頭が足りませんね。> 自分
banboo
大ベテラン
会議室デビュー日: 2003/12/05
投稿数: 210
投稿日時: 2004-01-03 15:15
Gio様、アドバイスを頂き有り難う御座います。
教えて頂いた方法に関して、今から考えてみます。

方法1について追記します。

方法1.splitメソッドのオプションを使用する方法

http://www.jajakarta.org/oro/ja/api/org/apache/oro/text/perl/Perl5Util.html
の中で、Perl5Utilのsplitメソッドを参考にすると、
split
public void split(java.util.Collection results,
java.lang.String pattern,
java.lang.String input,
int limit)

[m]/パターン/[i][m][s][x]
頭のm は省略可能です。後ろのオプションは次のような意味を持ちます。
i アルファベットの大文字と小文字を区別しないでマッチします。
m 入力を複数行として扱います。
s 入力を1行として扱います。

と書かれてあります。そこで、
reg.split(e, "/@/s", buf);
とし、入力を1行として扱う事にしました。すると

A
B
C@C
D@D
E
F
を入力ファイル、auto11.txt とした場合に、

A
A
B
A
B
C
C
A
B
C
C
D
D
A
B
C
C
D
D
E
A
B
C
C
D
D
E
F

と、予期しない結果が得られました。
プログラムで、どういう処理が行われて上記のような結果になるのか
理解できません。
「s 入力を1行として扱います。」とは、入力を
ABC@CD@DEF
のように一行として扱うという意味ではないのでしょうか?

どなたかご存知の方がいらっしゃいましたらご教授お願い致します。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;

import org.apache.oro.text.perl.MalformedPerl5PatternException;
import org.apache.oro.text.perl.Perl5Util;

public class OroTest3 {

public static void main(String[] args) throws MalformedPerl5PatternException, IOException {

File file = new File("auto11.txt");

BufferedReader in = new BufferedReader(new FileReader(file));
String buf = null;
Perl5Util reg = new Perl5Util();

Vector e = new Vector();

while ((buf = in.readLine()) != null) {

reg.split(e, "/@/s", buf);

for (int i = 0; i < e.size(); i++) {
String str = (String) e.get(i);
System.out.println(str);
}
}
}
}

Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2004-01-03 19:37
引用:

「s 入力を1行として扱います。」とは、入力を
ABC@CD@DEF
のように一行として扱うという意味ではないのでしょうか?


違います。
パターン後の s オプションは、"A\nB\nC@C" のような改行コードを含む文字列が与えられた時の動作の違いを表しているだけです。
示されたプログラムで split() に与えられる入力は、順に "A", "B", "C@C", ... となりますから、今回の場合には違いがありません。

split 結果の最後の要素を次の行に繰り越してと前の記事で書きましたが、その意味はおわかりですか?

予期しない動作とありますが、プログラムは意図した通りではなく書かれた通りに動いているだけです。
意図と動作の違いがどこから来るのかは、各行ごとにどのような処理が実行されているかトレースすれば、何が原因か、どのように修正すれば良いか、次は何を調べるべきかがわかりますよ。

[ メッセージ編集済み 編集者: Gio 編集日時 2004-01-03 19:39 ]

[ メッセージ編集済み 編集者: Gio 編集日時 2004-01-03 19:50 ]
banboo
大ベテラン
会議室デビュー日: 2003/12/05
投稿数: 210
投稿日時: 2004-01-04 01:27
その後、なんとか自分なりに考えて下記のようなプログラムを作成しました。
もっと、改良すべき点が御座いましたらご指摘下さいませ。

このプログラムを元に、教えて頂いた方法に変更していきたいと考えております。

現在
while ((c = fr.read()) != -1) {
と、一文字づつ読みこんで、その文字が「$」かどうか判断しております。
そこで、

>方法(1) BufferedReader#read(char[], int, int) を使う。

の方法のように、したいのですがよく分からない点が御座います。

BufferedReader#read(char[] cbuf, int off, int len)
を使う場合に、len(読みこむ文字数の最大数は)

A
B
C@C
D@D
E
F

の場合、
ABC(3文字)
CD(2文字)
DEF(3文字)

になると思います。このlenの文字数をどのように決定すればよいのかが
わかりません。
ここで、一文字づつ読みこむ方法であれば、「$」の文字が出現するまでの
文字数がlenにあたります。ところが、
while ((c = in.read(cbuf,off,len)) != -1) {

とかくと、「$」の文字が出現するまで、
という部分をどう記述してよいのかがわかりません。

この部分に、もう少し詳細なヒントを頂けないでしょうか?
また、もし、私が勘違いしている点が御座いましたらご指摘下さいませ。

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;

public class OroTest3 {

public static void main(String[] args) throws IOException {

File file = new File("auto11.txt");

FileReader fr = new FileReader(file);

int c = 0;

char[] value;

value = new char[10000];

int i = 0;

Vector e = new Vector();

String str = null;

while ((c = fr.read()) != -1) {

// System.out.println("char = " + (char)c);

if ((char) c == '@') {

str = new String(value, 0, i);

e.add(str);

i = 0;

} else {
value[i] = (char) c;
i++;
}

} //while文の終了

// 最後の文字列部分を追加する

str = new String(value, 0, i);

e.add(str);

for (int j = 0; j < e.size(); j++) {
String strout = (String) e.get(j);
System.out.println("str = " + strout);
}

}
}
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2004-01-04 12:01
いきなり「$」という文字が出てきましたが、なぜこの文字を処理する必要があるのかがわかりません。
正規表現での行終端ないし改行コードを意味しているのでしたら、その旨をきちんと説明してください。

プログラム例を見たところ、余分なメモリ消費もないし、1 文字ずつ読み込むオーバーヘッドは懸念されますが、まずまずの品質だと思います。
BufferedReader が外されていますが、これを導入すればこのクラスがまとまった単位で読み込んでメモリ上にキャッシュしてくれるので、この問題も解決されるでしょう。

改行コードを出力に含めないのであれば、while の次で if ((char) c == '\n') continue; してやれば済みます。

# もしこれが学生レポートで私が指導教官だったら、「A@@\n@@B@\n\n@@」という入力が
# 与えられた時の機能仕様の説明と、その通りに動作するプログラムを求めますが
banboo
大ベテラン
会議室デビュー日: 2003/12/05
投稿数: 210
投稿日時: 2004-01-04 12:58
>いきなり「$」という文字が出てきましたが、なぜこの文字を処理する必要があるのかがわかり>ません。 正規表現での行終端ないし改行コードを意味しているのでしたら、その旨をきちんと>説明してください。

申し訳御座いません。「$」という文字は「@」の書き間違えでした。

スキルアップ/キャリアアップ(JOB@IT)