masatoの日記

やっていきます

テキストファイルを一気に読み込む

Whileループじゃなくて一気に中身を変数にいれたいときはこうする。

my $str = do {
    open my $fh, '<', $file or die $!;
    local $/ = undef;
    <$fh>;
};

local $/ = undefとやるとdoブロック内でのみ改行コードを無視できる。

 readline関数が一度に読み取るのは、$/ に設定されている文字が出現するまでです。$/は、インプットレコードセパレータといい、デフォルトでは \n 、改行文字が設定されています。

すべての行をスカラ変数に読み込む すべての行を配列に読み込む - PerlならサンプルコードPerl入門

Perlで正規表現を使って日本語の文字列にマッチさせる

やりたいこと

日本語の文字列に任意の文字列が含まれるかどうか判定したい。

二通りのやりかたを書く。

正規表現でマッチさせる

use Encode qw/encode/;

# source text
my $str = 'メッセージ”通信エラー”が表示されます。';
my $p = '通信エラー';

$p = encode('utf8', $p);
$str = encode('utf8', $str);

if ($str =~ m/$p/) { # マッチをトライ
    print "Found it!!!";
}

index関数を使う

あるいは、index関数を使って任意の文字列が含まれるかどうかを判定できる。

## index function
if (index($str, $p) >=0) {
    print "Found it!!!\n";
}

メタ文字にマッチさせたいときは、\エスケープしてもよいが、quotemetaすれば自動でエスケープされて便利。

## quotemeta
my $qp = quotemeta $p;
if ($str =~ s/$qp/Communication fault/) {
    print "Replaced: $str\n";
}

名前付きキャプチャを使って複雑なマッチに対処する

正規表現でキャプチャしたグループは、$1, $2...という変数で参照できる。 だけどごちゃごちゃした文字列をキャプチャして参照したいときは、名前で参照できたほうが簡単だ。

キャプチャしたグループに名前をつけるには、(?<tag>)というふうに書く(tagは任意の名前)。 名前付きキャプチャの結果は%+というハッシュに入る。個別の要素を、指定した名前をキーとして参照できる。つまり、$+{tag}という具合。

my $line = 'no:1234 item_name:banana price:160 qty:8';
$line =~ /\Ano:(?<no>\d+)\s*
              item_name:(?<item_name>.*)\s*
              price:(?<price>\d+)\s*
              qty:(?<qty>\d+)\s?*
              \z/x;

print "番号:$+{no}\n";
print "品名:$+{item_name}\n";
print "価格:$+{price}\n";
print "数量:$+{qty}\n";

# 番号:1234
# 品名:banana
# 価格:160
# 数量:8

AjaxをやるときのHTMLとサーバー側コードの書き方を整理する

Ajaxをやろうとして沼だったので、書いて整理したい。

jQuery$.ajaxを使う一方で画面遷移が起こらないようにpreventDefaultでブラウザのリクエストを止めるか、そもそもHTMLでSubmitしないようにする必要がある。

NGパターン

Ajaxなのに画面遷移してしまう

Ajaxなのに画面遷移してしまう残念パターン。

HTMLでSubmitする+jsでpreventDefaultしていない

OKパターン

ちゃんと非同期通信できるのは

HTMLでSubmitしない or HTMLでSubmitする+jsでpreventDefaultする

とても参考になるリンクがあったので貼る。

ふじこのプログラミング奮闘記 - jQueryのバブリングと、「return false;」「e.stopPropagation();」「e.preventDefault();」について

ナビなしで高速道路を走れない

きのう名古屋市内の住まいから豊田市の実家に用事があり、急いでいたので高速道路でいった。

ナビに目的地をセットして車を出した。

しかしナビが示したのは一般道だった。走行中にルート変更できないヤツなので、ナビはあきらめて記憶とカンに頼って走ることに決めた。

何度目かの高速分岐にて、なぜか四日市方面に向かっていた。それは間違いで、結局ものすごい大回りをしていくことになってしまった。

道オンチで迷うことはよくある。

高速の分岐では、「たぶんこっち」で選んだ先は8割くらい間違っている。

早く目的地に着きたいがためにお金を払って高速にのっているのに、時間はかかるし、ガソリンはなくなるし、せつない。

自分に対してアホなのか?という気分だ。

さて不思議なことに道がわからないのに調べることをしない。

それはたぶん、調べるのがめんどくさいからだ。

そもそも調べたことがほとんどないので、調べ方が大変そうに感じてしまう。

道順を記憶することに興味あまりないなあ。

車のドア収容に備えてあるマップルは、いまだかつて活用したことがない。

地理をパラパラ眺めてみたことはある。

いや、そういうことじゃなくて。

道順がわからないと私生活でも仕事でもかなり困るはずだし、実際けっこう困っているのだ。

そろそろ道に詳しくなるために努力したほうがいいかもしれないとぼんやりと考えた。

OmegaTの分節化規則: ピリオドの後で分節化するのを避けるには

OmegaTで英文を分節化するときにうまくいかなかったけど、今日発見があったのでメモします。

やりたいこと

英語で複数の文章を1文節とした場合にピリオドの直後に続くスペースで分節化しない。

NGケース:

f:id:masatoz:20170418155513p:plain

This. is. a. sample. sentence.という、通常ありえない文ではありますが、改行なしで一続きのテキストがあります。 改行のみで分節化してほしい場合、これでは困ります。

方法

「分節化規則」の「初期値」をいじるとピリオドの後で分節化するのを避けることができます。

f:id:masatoz:20170418155144p:plain

ここに前方の正規表現[\.\?\!]+、後方の正規表現\sという項目があります。 .(ピリオド)または?(クエスチョンマーク)、!(エクスフラメーションマーク)の直後に1文字以上のスペースが続く場合に分節化する、ということになっています。

なので、これのチェックをはずします。 設定を変更して、再読み込みします。

こうなりました。OK! f:id:masatoz:20170418155711p:plain

この設定が有効になっていると、デフォルトで用意されている「Mr.」「etc.」などを分節化しない設定が無効になります。

設定をみてみると

f:id:masatoz:20170418160131p:plain

こんな感じです。

後方の正規表現にみな\sがあります。

ということはetc.が来て、直後に一文字以上のスペースが来たら分節化する、の意になります。

しかし.(ピリオド)の直後に一字以上のスペースの方が先に適用されてしまうので、上記の表現はみんな効かなくなってしまいます。

夕食の量をへらしてみる

最近、春が来たということもあって、暖かいので、つられて延々と眠ってしまいがち。

週末なんて10時間以上も寝ている。二度寝、三度寝しているからなんですけどね。

春というのを別にしても、7時間は寝ないと調子が出ない。

もっと短い睡眠時間で元気に生きられるとうれしいという思いがあって、食事の量を減らしてみようと思っています。

ただ、減らしすぎると体重が減るので、一日のカロリー量を保ったまま3食に分配できればよい。

Text::XslateでTMを作ってみた

TMつくりが地味に大変

ある程度成果物がたまったら、訳文を翻訳メモリにして、再利用できるようにまとめておきたい。

CATツールにはOmegaTを使っていて、翻訳メモリはTMX形式だ。中身には原文、訳文、原文言語、訳文言語などが含まれていて、これらはXMLで構造化されている。

翻訳メモリをつくるためのツールはいろいろあって、いくつか試してはみた。

しかしうまくいくときもあるけど、訳文と原文のペアがズレることが多くて悩ましい。

普段はOmegaTのAlignerというテキスト整合用プラグインを使ってるのだけれど、これだと分節化する箇所をうまく設定できない(とういか、やり方がわからない)。

たとえば、「Mr.」とか「etc.」の後のピリオドで分節化するのはよしてほしいのだけど、OmegaTはそこで区切ってしまうのだ。

分節化規則で区切らないように正規表現を入れてはいる。それで正しいはず。はずだが、うまくいかない。

同数の分節からなる原文と訳文ファイルを合体させたい、だけなのに。

前置きが長くなりましたが、余計な分節化が入らないように自作ツールを作ってみました。備忘録的にまとめます。

テンプレートエンジン

TMXファイルの中身はXML。テンプレートを使って自由にテキストファイルを生成できるText::Xslateを使いました。 まず、ひな形を用意します。

材料1:TMXファイルひな形(XML

OmegaTが自動生成するTMXファイルはこんな構造。

<tu>
  <tuv lang="<: $source_lang :>">
    <seg><: $source_body :></seg>
  </tuv>
  <tuv lang="<: $target_lang :>" changeid="<: $translator :>" changedate="<: $change_date :>" creationid="<: $id :>" creationdate="<: $createn_date :>">
    <seg><: target_body :></seg>
  </tuv>
</tu>
<tu>

<: $hoge :>の部分は、Text::XslateのKolonシンタックスで書いた変数展開パターンです。 TMXの要素である言語指定、原文、訳文などをこれらの箇所に挿入していきます。

材料2:スクリプト本体

ファイル名指定

このコードではシェルから実行して、訳文ファイル、原文ファイルをタイプする形にしています。 ファイルは半角スペース区切りでsource.txt target.txtのように入力すると、それぞれ$src$target変数に入ってくれます。 $targetの方はchompしないと改行コードが入るので注意。

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Text::Xslate;
use Time::Piece;

# ソース、ターゲットファイルを読み込む
print "Type file name (source, target):\n";
my ($src, $target ) = split(/ /, <STDIN>);
chomp($target);

言語指定

#言語設定
print "Type languages (source, target. e.g. JA EN):\n";
my ( $srclang, $targetlang ) = split(/ /, <STDIN>);
chomp($targetlang);

テキストファイルを読み込む

シェルでタイプしたテキストファイルを読み込むサブルーチン。 原文と訳文テキストをこれで読み取ります。

sub get_body {
    my $file = shift;
    my @body = ();

    open (my $fh, '<', $file) or die qq/ Can't open "$file": $!/;
    while (<$fh>) {
        chomp;
        push (@body, $_);
    }
    close ($fh);

    return @body;
}

次、読んだテキストをText::Xslateに渡せる形にします。 引数は配列レファレンスにする必要があるようでした。 なので、配列レファレンスにハッシュレファレンスを入れてやります。

こんな感じ。

my $segments = [
    {
      srcbody    => 'Apple',
      targetbody => 'りんご',
      srclang    => 'EN',
      targetlang => 'JA',
    },
    .......
];

ハッシュレフ一つが1文節のペアに対応します。 なので、たとえば100文節のTMを作る場合はこの配列レファレンスに100個のハッシュレフを入れればいいですね!

sub generate_template_arg {
    my ( $src, $target ) = @_;

    # 配列レファレンスにハッシュレフを入れる
    my $segments;
    for my $segment (@$src) {
        my $hash_ref = {
            srcbody  => $segment,
            targetbody => shift @$target,
        };
        push (@$segments, $hash_ref);
    }

    p $segments;
    return $segments;
}

ここまででテキストファイルの中身をText::Xslateに渡せる形にできました。 以下でテンプレートをもとにTMXファイルを作ります。

sub generate_tmx {
    my ( $segments, $srclang, $targetlang ) = @_;

  # イニシャライズ
    my $tx = Text::Xslate->new();

    # 生成するTMXのファイル名を指定
    my $tmx = 'translation_memory.tmx';

    open (my $out, '>', $tmx) or die $!;
    print $out $tx->render('test_template.tx', {
                            segments   => $segments,
                            srclang    => $srclang,
                            targetlang => $targetlang,
                         });
    close ($out);
}

この例では、$segmentsの中にすべての分節が収まっています。 TMXのファイル名はハードコードじゃなくて、引数で指定できるようにしたいところです。

順番が前後しましたが、上記のサブルーチン呼び出し部です。

# ハッシュの配列をもらう(Xslateで展開するため)
my @srcbody = get_body($src);
my @targetbody = get_body($target);

# 原文、訳文をXslateに渡す形にまとめる
my $segments = generate_template_arg( \@srcbody, \@targetbody );

# テンプレートエンジン起動
&generate_tmx( $segments, $srclang, $targetlang );

補足

Excelからソースを取ってくる場合、セル内の改行を適当な符号に置き換えること。

でないと、分節の対応がズレます。

Office TanakaのサイトExcelのセル内改行を置換する方法が詳しく説明されています。

ざっくりいうと、置換窓の検索欄でCtrl+Jを押すと、改行コードが入力できます。(見た目は変化ないです。透明。)訂正:Ctrl+Jするとドットが点滅する状態になりました。Office 365のExcelWindows 10で確認)

それで置換欄になんでもいいのでちょうどよい文字列を入れて置換します。正規表現のメタ文字にならって\nでいいのではないでしょうか。

で、改行コードを\nと置換したファイルを材料に上記のPerlスクリプトでTMXを生成します。

そいつをテキストエディタで開き、\nを本物の改行コードに置換してやれば完成です。

お疲れさまでした。