Groovyで文字列の中に半角英数記号以外の文字があるかを検出する正規表現パターンマッチング(修正・改良版)

if (text =~ /[^ -~]+/) {
    println "半角英数記号以外が含まれている"
} else {
    println "半角英数記号のみ"
}

否定条件でもやってみようとしたけど、思うような結果になるパターンが見付からないので割愛。

正規表現パターンマッチングのPerlとGroovyでの書き方の違い(2010-5-20の修正・補足版)

キーワードが含まれているかを判定

Perl(=~を使った場合)
my $text = "apple banana cherry";
print ($text =~ /banana/ ? "yes\n" : "no\n");
Perl(!~と!/.../を使った二重否定)
my $text = "apple banana cherry";
print ($text !~ !/banana/ ? "yes\n" : "no\n");
Groovy(=~を使った場合)
def text = "apple banana cherry"
println (text =~ /banana/ ? "yes" : "no")
Groovy(==~を使った場合)
def text = "apple banana cherry"
println (text ==~ /.*banana.*/ ? "yes" : "no")
Groovy(matchesを使った場合)
def text = "apple banana cherry"
println (text.matches(/.*banana.*/) ? "yes" : "no")

キーワードが含まれていないかを判定

Perl(!~を使った場合)
my $text = "apple banana cherry";
print ($text !~ /banana/ ? "yes\n" : "no\n");
Perl(=~と!/.../を使った場合)
my $text = "apple banana cherry";
print ($text =~ !/banana/ ? "yes\n" : "no\n");
Groovy(=~と!/.../を使った場合)
def text = "apple banana cherry"
println (text =~ !/banana/ ? "yes" : "no")
Groovy(==~と!/.../を使った場合)
def text = "apple banana cherry"
println (text ==~ !/.*banana.*/ ? "yes" : "no")
Groovy(matchesの結果を!で否定する場合)

matchesで!/.../を使うとコンパイルエラーになるため、結果を!で反転する。

def text = "apple banana cherry"
println (!text.matches(/.*banana.*/) ? "yes" : "no")

Groovyのパターンマッチングに関する記事(2010-05-20)の訂正とお詫び

正規表現パターンマッチングのPerlとGroovyでの挙動が異なる」に関する補足

2010-05-20の記事へのuehajさんのコメントを読んでいただくと分かると思いますが、以下の方法で正しく動くようです。

def text = 'apple banana cherry'
if (text =~ /banana/) {
    println "YES"
} else {
    println "NO"
}

ただし、「!~」は、コンパイルエラーが出て使えないため、Perlと全く同じように書けるわけではないようです。
(text !~ /.../ は、 text ! ~/.../ と解釈され、!演算子が正しくない位置にあると解釈されるため?)

「Groovyで文字列の中に半角英数記号以外の文字があるかを検出する正規表現パターンマッチング」の訂正とお詫び

以下の部分に誤りがありました。

if (text !=~ /.*[ -~]+.*/) {
    println "半角英数記号以外が含まれている"
} else {
    println "半角英数記号のみ"
}

この場合、text != ~/.*[ -~]+.*/ となり、必ず真になるため、マッチングに使えません。
記事に誤りがあった事をお詫びします。
さらに、==~演算子を使うほうのソースも、上で説明している通り、=~演算子を使う方法で書くことができ、その場合、左右の.*は不要となります。(.matchesを使った場合は.*が必要)

Groovyで文字列の中に半角英数記号以外の文字があるかを検出する正規表現パターンマッチング

if (text ==~ /.*[^ -~]+.*/) {
    println "半角英数記号以外が含まれている"
} else {
    println "半角英数記号のみ"
}

マッチング条件を反対にすると、

if (text !=~ /.*[ -~]+.*/) {
    println "半角英数記号以外が含まれている"
} else {
    println "半角英数記号のみ"
}

Javaっぽく書くと、

if (text.matches(/.*[^ -~]+.*/)) {
    println "半角英数記号以外が含まれている"
} else {
    println "半角英数記号のみ"
}

正規表現パターンマッチングのPerlとGroovyでの挙動が異なる

my $text = 'apple banana cherry';
if ($text =~ /banana/) {
    print "YES\n";
} else {
    print "NO\n";
}
def text = 'apple banana cherry'
if (text ==~ /banana/) {
    println "YES"
} else {
    println "NO"
}

同じ正規表現パターンでも、Perlでは"YES"、Groovyでは"NO"となってしまう。
Groovyでマッチングさせるには、次のようなパターンに変える必要がある。

def text = 'apple banana cherry'
if (text ==~ /.*banana.*/) {
    println "YES"
} else {
    println "NO"
}

どうやら前後に空文字を含む任意の文字列があると宣言しなければならないらしいので要注意。(Groovyのバージョンは1.6.8)

PerlでSQL接続/操作を学ぶための自作ドリル

DBI経由でPostgreSQLに接続し、データの登録/参照を行う。

問題

CSVファイルの内容をDBに登録する

DBとテーブルはPgAdmin3で用意する。
CSVファイル(sample.csv)

name,email,address
"山田 太郎",taro.yamada@xxx.com,"東京"
"田中 次郎",jiro.tanaka@xxx.com,"大阪"
"阿部 三郎",saburo.abe@xxx.com,"名古屋"

実行結果

$ insert_sql.pl sample.csv

DB登録内容(テーブル)

name email address
山田 太郎 taro.yamada@xxx.com 東京
田中 次郎 jiro.tanaka@xxx.com 大阪
阿部 三郎 saburo.abe@xxx.com 名古屋
DBの内容をXMLファイルに書き出す

DB登録内容(テーブル)

name email address
山田 太郎 taro.yamada@xxx.com 東京
田中 次郎 jiro.tanaka@xxx.com 大阪
阿部 三郎 saburo.abe@xxx.com 名古屋

実行結果

$ select_sql.pl sample_out.xml

出力結果XMLファイル(sample_out.xml)

<?xml version="1.0" encoding="UTF-8"?>
<addressBook>
  <person>
    <name>山田 太郎</name>
    <email>taro.yamada@xxx.com</email>
    <address>東京</address>
  </person>
  <person>
    <name>田中 次郎</name>
    <email>jiro.tanaka@xxx.com</email>
    <address>大阪</address>
  </person>
  <person>
    <name>阿部 三郎</name>
    <email>saburo.abe@xxx.com</email>
    <address>名古屋</address>
  </person>
</addressBook>
続きを読む

XML::TreePPを使ってみる

XMLファイルを読み込んで内容を表示する。
ソースコード

use strict;
use warnings;
use utf8;
use IO::File;
use XML::TreePP;
use Data::Dumper;

binmode STDIN, ':utf8';
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';

# Input Check
if (@ARGV != 1) {
  print STDERR "Please input XML file name.\n";
  exit 1;
}

my $xmlin_name = $ARGV[0];

# File Check
unless (-f $xmlin_name) {
  print STDERR "$xmlin_name is not found.\n";
  exit 1;
}

# Input XML
my $xml_parser = XML::TreePP->new(utf8_flag => 1, use_ixhash => 1);
my $xml = $xml_parser->parsefile($xmlin_name);

print Dumper($xml);

my $persons = $xml->{'addressBook'}{'person'};

# Output XML Data
for (my $cnt = 0; $cnt < @$persons; $cnt++) {
  print '(' . ($cnt + 1) . ')' . "\n";
  foreach my $key (keys %{$persons->[$cnt]}) {
    print "\t" . $key . ' : ' . $persons->[$cnt]{$key} . "\n";
  }
}

exit 0;

XMLファイル(sample.xml)

<?xml version="1.0" encoding="UTF-8"?>
<addressBook>
  <person>
    <name>山田 太郎</name>
    <email>taro.yamada@xxx.com</email>
    <address>東京</address>
  </person>
  <person>
    <name>田中 次郎</name>
    <email>jiro.tanaka@xxx.com</email>
    <address>大阪</address>
  </person>
  <person>
    <name>阿部 三郎</name>
    <email>saburo.abe@xxx.com</email>
    <address>名古屋</address>
  </person>
</addressBook>

実行結果(念のため、読み込んだ直後のデータをDumpする。)

$ perl input_xml_treepp.pl sample.xml
$VAR1 = {
          'addressBook' => {
                           'person' => [
                                       {
                                         'name' => "\x{5c71}\x{7530} \x{592a}\x{90ce}",
                                         'email' => 'taro.yamada@xxx.com',
                                         'address' => "\x{6771}\x{4eac}"
                                       },
                                       {
                                         'name' => "\x{7530}\x{4e2d} \x{6b21}\x{90ce}",
                                         'email' => 'jiro.tanaka@xxx.com',
                                         'address' => "\x{5927}\x{962a}"
                                       },
                                       {
                                         'name' => "\x{963f}\x{90e8} \x{4e09}\x{90ce}",
                                         'email' => 'saburo.abe@xxx.com',
                                         'address' => "\x{540d}\x{53e4}\x{5c4b}"
                                       }
                                     ]
                         }
        };
(1)
	name : 山田 太郎
	email : taro.yamada@xxx.com
	address : 東京
(2)
	name : 田中 次郎
	email : jiro.tanaka@xxx.com
	address : 大阪
(3)
	name : 阿部 三郎
	email : saburo.abe@xxx.com
	address : 名古屋
$ 

このモジュールも、XML::LibXML::Simple同様、マップで取得できるのは便利かも知れない。
でも、異なる名前の要素の順番が、そのままでは保持されないのは同じ。
ただ、use_ixhashオプションを1にする事で、マップの順番が保持されるらしい。(Tie::IxHashモジュールが必要)
さらに、utf8エンコードの文字データを取り込むためには、utf8_flagオプションを1にしておく必要がある。