sironekotoroの日記

Perl で楽をしたい

Mac に Parallels で Windows10 を入れ、その上で VPN 接続して弥生会計を使いたい!

MacParallels で Windows10 を入れ、その上で VPN 接続して弥生会計を使いたい!

そういう人も日本に3人くらいいるかもしれない、という感じで書いてみます。中身は薄いです。

弥生会計とは・・・?

シェア6割を誇ると言われる企業用の会計ソフトです。

こんな感じです。

f:id:sironekotoro:20200429133456p:plain

複式簿記で入力していきます。

簿記では最終的な貸借対照表(BS)や損益計算書(PL)に至るまで、途中途中で精算表を作っていきます。

が、会計ソフトだとそういった帳票の転記もやってくれるので、とても楽です。

というか、会計ソフトがない頃の経理業務を想像すると地獄みしかないですね・・・

なぜ Parallels なのか

弥生会計Mac 対応しておらず、Windows 用のクライアントソフトしかないからです。

一応、自己責任で Parallels 経由で利用できますよ、と公式ページにもあります。

support.yayoi-kk.co.jp

Mac の他に別途 Windows ノートを用意するということも考えましたが、リモートワークの昨今、ノートPCを2つも持って歩きたくない・・・という怠惰な理由です。

あと「やってみたかった」という純粋な興味。

作業環境

Mac への Parallels インストールと Windows10 のインストール、その Windows10 上での弥生会計インストール、これはうまくいきました。全くトラブルなし。

会社で利用している弥生会計は、大元のデータを Microsoft SQL Server に保存しています。

社内ネットワークに接続し、サーバへ接続してデータ編集、これも問題なくできました。

詰まったところ

が、VPN接続環境下ではサーバにアクセス出来ない現象が発生しました。

弥生会計の認証は通るものの、そのあとのサーバへの接続のところで失敗します。

VPNmacOS 側で接続した場合も Windows10 から接続した場合も接続は可能です。

また、同サーバにある共有フォルダ(SMB)にはアクセスが可能(サーバー名、IPアドレスとも)なことから、弥生会計の設定や何かに問題があるのでは?と推測しました。

ただ、素の Windows10 から利用していないので、もう一つの方法として MacSSD からパーティションを分けて BootCamp に Windows10 をインストールし、そこに弥生会計をインストール、VPNの設定を作って接続してみました。

Parallels 経由ではない、素の Windows10 環境です。・・・が、結果は変わらず。

ううんー???

となったところで、インフラ担当さんより、サーバを名前ではなくIPで指定してみたら?とのアドバイス

共有フォルダ(SMB)だと名前で解決できたけど、そうか、弥生の名前解決の仕組みは別か・・・!

こんな感じ(サーバー名とかIPアドレスは適当に変更してます)

  • 元の設定: KEIRI\YAYOI
  • 変更した設定: 192.168.1.100\YAYOI

でアクセスしたところ、無事データの取得に成功しました。さすがインフラ担当、頼りになる!

ということで、色々と試行錯誤はしましたが、 MacParallels 入れて Windows10 をインストールし、その Windows10 内の弥生会計からVPN経由で会社サーバーへの接続は可能だった!ということで記事を締めさせていただきますありがとうございました。

最近のお仕事 2020年04月

色々あって、3月の終わり頃から会社の経理業務を手伝っています。

経理業務の経験はないのですが、履歴書に簿記2級って書いてたのを覚えてくれていた人がいたようです。

既存サービスの保守とかアップデートをしつつ、経理の世界に足を突っ込んでいるような状態です。

それに伴い、触れる環境が随分変わりました。

こんな感じです。

前任者からの急ぎの引き継ぎで、Excelベースの便利シートなどがあるんですが、よくわからなかったりして「超古代の進んだ文明を理解できない原始人類」みたいな気分を日々味わってます。

とはいえ、割と楽しくお仕事はしており、これは周りの同僚さんのサポートあればこそです。ありがたいです。

弥生会計については触るのも初めてですが、体験版のついた参考書を買って練習したりしています。商業高校の生徒さんはこういうのやってるのか・・・負けてるわ・・・ってなりながら勉強しております。

www.jikkyo.co.jp

そういえば、今年は Go でちょこっとお手伝いしたり、 PHP で画像合成ツール改修したりとお仕事 Perl 書いてないっすね。経理業務では隙あらば Perl で業務を楽にしていきたいです。

Mac英語キーボード(US)と Windows10 のかな漢字変換キーバインドを合わせる

下記のブログさまさまです・・・。ありがたいありがたい。

www.shujima.work

Perlでファイルの文字コードを指定して読み込み&書き込み

一昨日の金曜日、ムキー!ってなったことを、落ち着いて週末に解決するべく色々やった記録です。

まず、このGoogle Spread Sheet 開いてください。普通に開けると思います。

docs.google.com

はい。タイトルで察していただけると思うんですが、そういうことです。

では、画面左上のタイトルの下にある [ファイル] メニューからダウンロードを選び、[カンマ区切りの値 csv] を選びます。

ダウンロードのダイアログが出るので、一度どっかに mojibake.csv ってファイル名で保存してください。

保存したcsvファイルですが、VSCode や SublimeText で普通に開き、中身を確認することができます。Windows のメモ帳(notepad.exe)や macOS のテキストエディットでも文字化けは起こりません。

ではこれを Office365 の Excel で開いてみます。

見事に文字化けです!

f:id:sironekotoro:20200425150657p:plain

Office365 は macOS でも利用できるのですが、同様に文字化けします。

憎いですね。許しがたいですね。

このような状況はよくあります。

これは「文字コード」の違いが原因です。文字コードについては色々大変なのでここでは説明しません。

説明しきる力量がないともいいます・・・

という差異が文字化けを生んだわけです。

Excel(Office365) で utf8 のcsvファイルを文字化けさせずに読み込む

もちろん、Excel なので(信頼感)こういったファイルをちゃんと取り込む術があります。

まず、csvファイルを 読み込まずExcel を開き、メニューから [データ] を選択、その中の [テキストファイル] を選びます。画像の左から2つ目のアイコンのところです。

f:id:sironekotoro:20200426135231p:plain

ウィザードの画面が出ます。

f:id:sironekotoro:20200426135326p:plain

[区切り記号付き] を選び、[元のファイル] のプルダウンメニューから [Unicode (UTF-8)] を選びます。

f:id:sironekotoro:20200426135825p:plain

下のプレビューで見ると(字が細いですが)、正しく表示されていることが確認できます。

あとはなりなりに進んでいくと、無事にExcelのシートに取り込むことが可能です。

Perlでやる場合

・・・別にPerlでやらなくても良いのでは?導入が長いのでは?

わかります。うちもこのエントリをボツにしようかと思いましたもん。

でも、仮にこういったファイルが100個あったとして、一個一個心を込めてExcelのウィザードを起動するの・・・?

という心の声が聞こえてきたんで書きます。

想定環境は macOS 上かPerl入学式で作った msys2 上の環境です。mojibake.csv と同じ場所にスクリプトを作ります。

それと、今回扱うのはcsvファイルですが内容を変更しないので、csv関係のモジュールは使いません。

文字コード指定して読み込んで、文字コード指定して書き出すのみ、です。

単にファイルを指定して読むだけ

前回、前々回でやった範囲です。復習ですね。

#!/usr/bin/env perl
use strict;
use warnings;

my $input_file = 'mojibake.csv';

open my $FH, '<', $input_file;
for my $line (<$FH>) {
    chomp $line;
    print $line . "\n";
}
close $FH;

文字コードを指定して読み込んで表示する

上記のコードとの違い、わかりますでしょうか。

#!/usr/bin/env perl
use strict;
use warnings;

use Encode;    # 文字コードを扱う便利モジュール

my $input_file = 'mojibake.csv';

open my $FH, '<:encoding(utf8)', $input_file;   # utf8の文字コードのファイルを読み込む
for my $line (<$FH>) {
    chomp $line;
    print encode('utf8', $line) . "\n"; # 文字コードを指定して出力
}
close $FH;

Perl文字コードが異なるファイルや入出力に対応するときは Encode モジュールを使うのが鉄板です。標準モジュールなのでインストールは不要です。

use Encode;

ファイルを読み込むときの第二引数 < に、文字コード指定の :encoding(utf8) がついています。

open my $FH, '<:encoding(utf8)', $input_file;   # utf8の文字コードのファイルを読み込む

ファイルの文字コードがわかっている場合には、このように文字コードを指定して読み込みます。

そして、処理した結果を出力する時(今回は print でターミナルに出力)には、同じように文字コードを指定しています。

print encode('utf8', $line) . "\n";`  

mac と msys2 のターミナルの文字コードは utf8 なので、encode の utf8 で指定して print しています。

文字コードを指定してファイルに書き込む(utf8)

読み込んで書き込む、ということで、ファイルハンドルも2つ用意します。$ReadFH$WriteFH です。

書き込みの際のファイルハンドルは第2引数の不等号の向きが > とファイル名(またはファイル名が入っている変数)側を向いています。そして、文字コードの指定 :encoding(ctf8) もついています。

open my $WriteFH, '>:encoding(utf8)', $output_file;    # utf8の文字コードでファイルに書き込む

まずはutf8の文字コードで書き出してみます。

#!/usr/bin/env perl
use strict;
use warnings;

use Encode;    # 文字コードを扱う便利モジュール

# 読んだファイルの内容をためておく配列
my @lines;


# ファイル読み込み処理
my $input_file = 'mojibake.csv';

open my $ReadFH, '<:encoding(utf8)', $input_file;   # utf8の文字コードのファイルを読み込む
for my $line (<$ReadFH>) {
    chomp $line;
    push @lines, $line;
}
close $ReadFH;


# ファイル書き込み処理
my $output_file = 'utf8.csv';

open my $WriteFH, '>:encoding(utf8)', $output_file;    # utf8の文字コードでファイルに書き込む
for my $line (@lines) {
    print $WriteFH $line . "\n";
}
close $WriteFH;

文字コードを指定してファイルに書き込む(cp932)

次に、いよいよExcelが素で解釈してくれる文字コードcp932 で書き込みます。

と言っても、上記のコードの1行、書き込む際のファイルハンドルの文字コード指定 utf8cp932 に変えただけです。

コードはこちら。クリックすると展開されます

#!/usr/bin/env perl
use strict;
use warnings;

use Encode;    # 文字コードを扱う便利モジュール

# 読んだファイルの内容をためておく配列
my @lines;


# ファイル読み込み処理
my $input_file = 'mojibake.csv';

open my $ReadFH, '<:encoding(utf8)', $input_file;   # utf8の文字コードのファイルを読み込む
for my $line (<$ReadFH>) {
    chomp $line;
    push @lines, $line;
}
close $ReadFH;


# ファイル書き込み処理
my $output_file = 'cp932.csv';

open my $WriteFH, '>:encoding(cp932)', $output_file;   # cp932の文字コードでファイルに書き込む
for my $line (@lines) {
    print $WriteFH $line . "\n";
}
close $WriteFH;

これでやっと、Excelでそのまま開いて文字化けしないファイルができました。

Excelですんなりファイルを読み込み、文字化けもせず。素晴らしい!

f:id:sironekotoro:20200426150340p:plain

逆(cp932 -> utf8)もいけます

もちろん、Excel をはじめ Windows アプリが cp932 で出力した csv ファイルを読み取り、utf8 で書き出すことも可能です。

官公庁のcsvファイルなんかはだいたい WindowsExcel で作られているみたいなので、こちらの方が数は多いでしょうね。

例えば厚生労働省の人口動態データです。右上の [ダウンロード] ボタンからダウンロードしてみてください。

www.data.go.jp

これを文字コードを指定せず、Perlで読み込んで出力すると文字化けします。

f:id:sironekotoro:20200426153527p:plain

では、文字コードを指定して表示してみます。

#!/usr/bin/env perl
use strict;
use warnings;

use Encode;

my $input_file = 'mi010000.csv';

open my $FH,  '<:encoding(cp932)', $input_file;
for my $line (<$FH>){
    chomp $line;
    print encode('utf8',$line);
}
close $FH;

無事、文字化けせずに表示ができました。

f:id:sironekotoro:20200426155944p:plain

次回

1個のcsvファイルを処理するにはこれで良いと思うんですが、これが100個あったときにはファイル名を変えつつ実行しなくてはいけないのでは・・・?

心を込めて100回、ファイル名を変えるの?

・・・

ということで、次回はフォルダから中のファイルを拾う方法てのをやります。

おまけ

6年くらい前、Perl勉強してこ!ってときに使っていたのは Windows マシンだったのですが、Webから拾ってきた諸々が文字化けするのが解消できなかったんですね。

ですので、Macを買ったのでした。これぞ富豪的解決!・・・とはならず、今度は Windows から出力したファイルを mac で扱おうとして文字化けしてしまい、苦しみは尽きないのでした。

昨今、文字コードはアプリケーション側で吸収することも多く、文字コードの差異で苦しむのはプログラムを書くときくらいかも知れません。

Perlでcsvファイルを読み込んで 配列|ハッシュ にする

先週からの続きです

先週でテキストファイルの読み書きができるようになりました。

sironekotoro.hateblo.jp

ということで、今回は代表的なテキストファイルのデータとしてCSVの処理方法をやっていきます。

CSVファイル

Comma Separated Values の頭文字を取った名前の通り、カンマ区切のデータのことです。

1行にカンマ区切りで複数のデータのがあり、それが複数行ある・・・というです。

この1行のことをレコードと呼び、レコードの中のカンマで区切られたところをフィールドと呼びます。

Perl入学式のリファレンス回の練習問題からデータを持ってcsv風に書くとこんな感じです。

name,country,perl,python,ruby,php,binary
Alice,England,60,80,80,50,30
Bob,America,40,10,50,30,50

では、これを records.csv というテキストファイルにして、色々と処理をしていきます。

csvファイルを単に読むだけ

先週の復習です。csvファイルと今回作成するPerlスクリプトは同じ場所に置いて実行してください。

#!/usr/bin/env perl
use strict;
use warnings;

my $filename = 'records.csv';
open my $FH, '<', $filename;
for my $line (<$FH>){
    chomp $line;
    print $line . "\n";
}
close $FH;

csvを読み込んで配列にする

データが詰まっている1行はカンマ区切りなので、split で分割して配列に入れるだけです。

#!/usr/bin/env perl
use strict;
use warnings;

my $filename = 'records.csv';
open my $FH, '<', $filename;

for my $line (<$FH>) {
    chomp $line;
    my @field = split /,/, $line;   # カンマ区切りで配列に格納
    print "@field" . "\n";          # 配列をprint
}

close $FH;

ファイルの読み込みと、読み込んだデータの処理を分けて書くときとには配列のリファレンスを使うことになると思います。

こんな感じかなー。無名配列でもっと短くかけますが、そこは各自で・・・

#!/usr/bin/env perl
use strict;
use warnings;

my @records;    # 配列リファレンスを格納する配列

my $filename = 'records.csv';
open my $FH, '<', $filename;

for my $line (<$FH>) {
    chomp $line;
    my @field = split /,/, $line;    # カンマ区切りで配列に格納

    my $record = \@field;            # 配列リファレンスにする
    push @records, $record;   # 配列リファレンスを格納用の配列に入れる
}

close $FH;

# 結果表示
for my $record (@records) {
    my @record = @{$record};    # デリファレンスして配列に戻す
    print "@record" . "\n";     # デリファレンスしたものを表示
}

csvを読み込んで配列にする(モジュールを使う)

ここまで書いてきてなんですが、自分でパース(データを分けること)することはお勧めしません。

例えば、フィールドのデータ内に , があると、split はそこで分割してしまいます。

Perlcsvを扱うモジュールといえば Text::CSV_XS が定番です。Text::CSV_XS であれば、そのようなデータにもうまく対応してくれます。

(ただし、処理するファイルの前提はある)

また、スクリプトの上の方に use Text::CSV_XS; とあると、「あー、csvを何かするスクリプトなのね」と理解の一助になります。

なお、標準モジュールではないのでインストールが必要です。

$ cpanm Text::CSV_XS

perldoc.jp

利用例としてはこんな感じです。

#!/usr/bin/env perl
use strict;
use warnings;

use TEXT::CSV_XS;

my $csv = Text::CSV_XS->new();    # csvを扱う便利オブジェクト

my $filename = 'records.csv';
open my $FH, '<', $filename;
for my $line (<$FH>) {
    chomp $line;
    my $status = $csv->parse($line); # CSV文字列をパースしてフィールド群に切り分ける
                                     # $status は成否判定が入っている(今回は使わない)
    my @columns = $csv->fields();    # パースされたフィールド群を配列に入れる
    print "@columns" . "\n";
}
close $FH;

csvを読み込んでハッシュにする

こちらの使い方も多いと思います。

ハッシュにするにあたって考えるのは、keyvalue をどうするか?ということです。

今回はこんな感じのデータ構造を作ってみます。配列の中にハッシュリファレンスが入っています。

ハッシュなので順不同なのですが、見やすさ優先で key でソートしてます。

$VAR1 = [
          {
            'binary' => '30',
            'country' => 'England'
            'name' => 'Alice',
            'perl' => '60',
            'python' => '50',
            'ruby' => '80',
          },
          {
            'binary' => '50'
            'country' => 'America',
            'name' => 'Bob',
            'perl' => '40',
            'python' => '30',
            'ruby' => '50',
          }
        ];
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

my @records;    # ハッシュリファレンスを格納する配列

my $filename = 'records.csv';
open my $FH, '<', $filename;

for my $line (<$FH>) {
    chomp $line;
    next
        if $line
        =~ /^name/;  # nameから始まる項目名の行だったら飛ばす

    my @field = split /,/, $line;    # カンマ区切りで配列に格納

    # ハッシュのデータ構造にする
    my %record = (
        name    => $field[0],
        country => $field[1],
        perl    => $field[2],
        python  => $field[3],
        ruby    => $field[4],
        python  => $field[5],
        binary  => $field[6],
    );

    my $record = \%record;    # ハッシュリファレンスにする
    push @records,
        $record;   # ハッシュリファレンスを格納用の配列に入れる
}

close $FH;

print Dumper \@records;

csvを読み込んでハッシュにする(モジュールを使う)

ここまで長く書いてきてなんですが、ハッシュにするときもモジュールを使うのが楽でおすすめです。

metacpan.org

こちらも標準モジュールではないのでインストールが必要です。

$ cpanm Text::CSV::Simple

3行で収まっちゃった・・・

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

use Text::CSV::Simple;

my $filename = 'records.csv';

my $parser = Text::CSV::Simple->new;
$parser->field_map(qw/name country perl python ruby php binary/);
my @data = $parser->read_file($filename);

print Dumper \@data;

というわけで

駆け足ながらPerlでのcsvファイル処理について書いてみました。

誰向けの記事かというと、6年くらい前の自分向けの記事です。

Perlでテキストファイルの中身を表示する

色々書いたけど省略

  1. CSVファイルを処理する方法について書いておきたい
  2. そもそも、ファイルの入出力についてPerl入学式の現行カリキュラムでやってない
  3. しかもいい感じのCSVファイルのサンプル、Shift-JISフォーマットじゃん
  4. ・・・一歩ずつ、ファイル入出力からやっていこう

Perlでテキストファイルの中身を表示する

では早速ファイルを用意します・・・と思って適当なの探したんだけど、良い感じのデータがない・・・ので、プログラムファイル自身を表示する、というふうに方向転換します。

read_file.pl という名前のスクリプトを作り、実行してみます。

Perlでファイルを読み込む(手作業編)

#!/usr/bin/env perl
use strict;
use warnings;

my $filename = 'read_file.pl';    # ファイル名を指定する

open my $FH, '<', $filename;       # ファイルハンドルを宣言する

for my $line (<$FH>) {  # 行入力演算子で1行ずつ読み込む
    chomp $line;
    print $line . "\n";
}

close $FH;    # ファイルを閉じる

実行すると、スクリプト自身を表示します。

Perl入学式の現行カリキュラムではファイルの取り込みや書き込みは学習しませんので、ここで少し解説です。

Perlではファイルハンドルというものを使ってファイルの読み書きを行います。

ファイルハンドルは「ファイルを扱うもの」くらいに思っておいてください。直訳じゃん・・・

コードの中では $FH という変数がファイルハンドルです。

open という関数を使ってファイルハンドルを宣言してファイルを open し、使い終わったらファイルハンドルを close します。

open my $FH, '<', $filename;    # ファイルハンドルにファイルを読み込ませる

# 何らかの処理を書く

close $FH;                          # ファイルハンドルを終了する

これだけだと、ファイルハンドルを設定して終了しただけです。

この、 openclose の間に処理を書きます。今回書かれているのは・・・

for my $line (<$FH>){
    chomp $line;
    print $line . "\n";
}

おなじみのfor文です。 <$FH> 以外のところはPerl入学式の第2回でやったところです。こんな感じですね。

for my $line ( 0..9 ){
    chomp $line;
    print $line . "\n"; # 0 から 9 までを改行して表示する
}

では、この <$FH> はどういう意味でしょう・・・となる前にもう一つ、Perl入学式でやった標準入力を思い出してみます。

#!/usr/bin/env perl
use strict;
use warnings;

my $line = <STDIN>;
chomp $line;
print $line . "\n";

似ていますね、 <$FH><STDIN>

この <> は行入力演算子というもので、その名の通り、行単位で1行ずつ読み込んでくれるものです。

<STDIN> の場合には標準入力を enter キーが押されるごとに、<$FH>の場合にはファイルを1行ずつ読み込んでいます。

perldoc.jp

こんな感じで、

  1. oepn でファイルハンドルを宣言し、ファイルを開く

  2. 開いたファイルから1行読み取り、何か処理をする(今回は表示する)

  3. close でファイルハンドルを閉じる

という一連の処理がファイルの処理の基本となります。

Perlでファイルを書き込む(手作業編)

こちらでもファイルハンドルを使っていきます。

今回はFizzBuzzをファイルに出力してみます。

#!/usr/bin/env perl
use strict;
use warnings;

my $filename = 'fizzbuzz.txt';
open my $FH, '>', $filename; # ファイルを書き込むときは不等号の向きが ファイル名側になる

for my $n ( 1 .. 100 ) {
    if ( $n % 3 == 0 && $n % 5 == 0 ) {
        print $FH "FizzBuzz" . "\n";    # 一見普通のprint文・・・?
    }
    elsif ( $n % 3 == 0 ) {
        print $FH "Fizz" . "\n";
    }
    elsif ( $n % 5 == 0 ) {
        print $FH "Buzz" . "\n";
    }
    else {
        print $FH $n . "\n";
    }
}

close $FH;

実行しても特に表示は出ませんが、fizzbuzz.txt というファイルがスクリプトと同じ場所に作成されています。

では解説です。

fizzbuzzの処理を open, close で囲っています。ここはファイルを読み込んだ時と同じです。

ただし、open したときの不等号の向きに注意してください。

open my $FH, '>', $filename; # ファイルを書き込むときは不等号の向きが ファイル名側になる

不等号がファイル名側を指しているのがファイルへの書き込みです。

そしてもう一つ。

print $FH "FizzBuzz" . "\n"; # 一見普通のprint文・・・?

print のところにファイルハンドル $FH が入っています。

これにより、各数字のfizzbuzzの結果がファイルハンドル経由でファイルに書き込まれています。

カンマは無しで、print と表示したい文字列や変数の間にスペース区切りでファイルハンドルを記述します。

ファイルの読み込みと書き込みを同じスクリプトで行う

ファイルハンドルを2つ用います。

今回は先に作成した fizzbuzz.txt を読み込んで、逆順のファイルを作成する、というスクリプトを書いてみました。

#!/usr/bin/env perl
use strict;
use warnings;

my $read_filename = 'fizzbuzz.txt';
my $write_filename = 'reverse_fizzbuzz.txt';

my @array = ();

# ファイルの読み込み
open my $READ, '<', $read_filename;
for my $line (<$READ>) {
    chomp $line;
    push @array, $line;
}
close $READ;


# ファイルの書き込み
open my $WRITE, '>', $write_filename;
for my $line (reverse @array){
    print $WRITE $line . "\n";
}
close $WRITE;

ファイルに追記したい(手作業編)

ファイハンドルを宣言するときの不等号を >> とします。直感的!

ファイル入出力をモジュールで

IO::Fileがメジャーだと思います。Perlの組み込みモジュールであり、use IO::File するだけで利用できます。

fizzbuzz.txt を読み込むスクリプトです。

#!/usr/bin/env perl
use strict;
use warnings;

use IO::File;

my $FH = IO::File->new('fizzbuzz.txt', '<');
for my $line ( <$FH> ){
    print $line;
}
$FH->close();

Perl入学式 2019 in東京 秋開講 第5回 は中止となります

Perl入学式 2019 in東京 秋開講 第5回は中止となります

第1回〜第4回まで講師をやったジャージの人です。

Perl入学式 2019 in東京 秋開講 第5回は延期ではなく中止となります。

これは2020年初頭から蔓延し、猛威を振るっている新型コロナウイルス(COVID-19)の影響によるものです。

Perl入学式は「解説と練習問題を繰り返し、不明点があればサポーターが近くで教える」という、ワークショップ形式です。

呼気・接触で広がる新型コロナウイルスの性質を考えると、ワークショップ形式の勉強会を開催することはできません。

テキストは公開しております!

従来通り、第5回のテキストを公開しています。

github.com

また、講義で使っているスライド形式のものはこちらになります。

appslideshare.tugougaii.site

自作のスライドはこちらです。使いたかったなぁ・・・

docs.google.com

問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)や、twitterハッシュタグ #Perl入学式 をつけて聞いてみてください。

応答速度、監視頻度などの面からSlackの方をお勧めします。

第5回について

Perl入学式の第5回は第1回〜第4回までとは少し毛色の異なる回です。

第1回〜第4回がPerlの基礎をやってきたのに対し、第5回はその基礎を使う一場面としてWebアプリを作ります。

Perl入学式ではMojolicious(モジョリシャス)というWebフレームワークを利用してWebアプリの作り方を学びます。最初に生成される雛形を拡張し、フォームからの入力、加工、Webアプリ上での表示を可能にしていきます。

講義スライドでは、その時点でのコードの追加・修正履歴を追えるようにしています。

ぜひ挑戦してみてください!

2020年度のPerl入学式

今年度前半のワークショップ形式の開催は難しそうです。オンラインでの開催などを模索して、実施していきます。

printfと私

コロナウイルス禍の昨今

Perl入学式はもとより、Perl のお祭り YAPC::Kyoto 2020 や他の技術系カンファレンスも延期になってしまい、なんとも寂しい限りです。

特にYAPC::Kyoto 2020 は前々日から京都入りして、鞍馬寺比叡山、福知山など、ちょっと遠いところに行こうと思っていたので、本当に残念です。

で、そんな状況とはあんまり関係なく、printfの話をしたいと思います。

printf , sprintf

Perl入学式では取り上げていないprintfなんですが、とても便利な関数です。Perlの組み込み関数なので、モジュールのインポートなどは不要です。

perldoc.jp

通常のprintは引数にとったものをそのまま表示するだけです。

print "hogehoge\n"; # hogehoge

print "hogehoge", "fugafuga", "piyopiyo", "\n"; # hogehogefugafugapiyopiyo

このprintの末尾にfを加えたprintfでは引数を2個以上取ることになります。

printf("%s\n", "hogehoge"); # hogehoge

printも、printfも同じく、 hogehoge と表示して改行するだけのコードです。

printf の最初の引数 "%s\n" は文字列を代入して改行、という意味になります。第二引数である文字列が%sのところに入る訳ですね。

文字列ではなくて、数字も代入できます。第一引数の違いに注目です。

printf("%d\n", 5.30);   # 5

printf("%f\n", 5.30);   # 5.300000

第二引数は双方共5.30 で同じものですが、表示結果は異なります。

"%d\n"の場合には整数、"%f\n"の場合には小数点第6位まで表示されています。

このように、引数に書式を適用して(フォーマットして)出力できるのがこのprintfなのです。

小数点の桁数は、以下のように調節が可能です。

printf("%.2f\n", 5.30);   # 5.30

整数の方も、0埋めが可能です。

printf("%02d\n", 5);   # 05

例えば、日付なんかで2020-03-28 と表示したいときなんかに0埋めができることを知っていると楽です。

printf("%04d-%02d-%02d\n", 2020, 3, 20);   # 2020-03-20

sprintf はフォーマットした文字列や数字を変数に格納するときに使います。引数などはprintfと同じです。

my $date = sprintf("%04d-%02d-%02d\n", 2020, 3, 20);
print $date;    # 2020-03-20

この記事を書こうと思ったきっかけ

かつてのうちは「printf、なんか引数多いし、%sとか%dとかよくわからんし、printでいいよね」と思い綺麗に忘れ去ったのでした。

その結果、このようなサブルーチンが自分のコードのそこかしこから発掘されたのです。

sub add_zero {
    my $num = shift;
    if (length $num == 1){
        $num = 0 . $num;
    }
    return $num;
}

print add_zero(1) . "\n";   # 01
print add_zero(12) . "\n";  # 12

これは、引数にとった数字が1桁だったら0を左側に1つつける(0埋めする)、というサブルーチンです。

しかし、数字と文字のチェックをしていないため、

print add_zero('a') . "\n";  # 0a

という結果が返ってきます。

printfであれば、第一引数が数字%dであるのに文字が入っていると警告が出ます。

printfを知っていれば、このサブルーチン書かなくてもよかったのになー、という思いも込めて(あとしばらくブログ書いてなかったなーという反省もあり)このエントリを書いたのでした。

Perl入学式の小ネタ:正規表現の文字境界

前振り

あなたはあるブログプラットフォームの開発者です。

昨今流行している新型コロナウイルス「COVID-19」について投稿される記事も多くなってきました。

掲載される情報は正しいものもありますが、アクセス数狙いの過激な文言を含む記事もあります。

そのため、COVID-19に関する記事については画面の上部に「信頼しうるソースへのリンク」を掲示することになりました。

開発者は「covid」「COVID-19」「コロナウイルス」「新型コロナ」「発熱」など、いくつかの単語を用意し、記事を正規表現で検索します。

検索結果にマッチした記事だった場合、画面に注意書きを掲載する、という仕組みを作りました。

今回はCOVIDという文字を含む記事、という想定で書いてみました。判定には以下の正規表現を用います。

$entry =~ /COVID/i 
#!/usr/bin/env perl
use strict;
use warnings;

my @entries = (
    'No.1: covid-19の症状について',
    'No.2: COVID-19の症状について',
    'No.3: COVID19の症状について',
    'No.4: COVIDの症状について',
);

for my $entry (@entries) {
    if ( $entry =~ /COVID/i ) {
        print "WARNING!: $entry\n";
    }
}

# 実行結果
# WARNING!: No.1: covid-19の症状について
# WARNING!: No.2: COVID-19の症状について
# WARNING!: No.3: COVID19の症状について
# WARNING!: No.4: COVIDの症状について

Perl入学式の第3回で学習した範囲です。 /i とオプションをつけることで、大文字小文字にかかわらずマッチさせることが可能です。

しかし・・・?

実装後、いくつかのCOVID-19とは関係ない記事に警告が表示されているとの報告がユーザーから寄せられました。

調査の結果、ニコニコ動画のURLを含む記事も正規表現にマッチする結果となっていることが判明しました。

#!/usr/bin/env perl
use strict;
use warnings;

my @entries
    = (
    '<a href="https://www.nicovideo.jp/watch/sm9">再ブレイク!レッツゴー陰陽師</a>'
    );

for my $entry (@entries) {
    if ( $entry =~ /COVID/i ) {
        print "WARNING!: $entry\n";
    }
}

# 実行結果
# WARNING!: <a href="https://www.nicovideo.jp/watch/sm9">再ブレイク!レッツゴー陰陽師</a>

なるほど、URLの一部に covid という語が含まれています。

本来のCOVID という語のみを検出し、niconico動画のURLを除外するためにはどうすれば良いでしょうか?

文字境界の /b

正規表現の対象語の前後(あるいは必要なところ)に '/b' を置くことで、より精緻にマッチさせることが可能になります。

\b Match a word boundary

文字の境界とはなんぞや?というと下記のリンク先にも書いてありますが、こういうことです。

単語境界(\b)は\W にマッチングする文字列の始まりと終わりを 連想するような、片側を \w、もう片側を \W で挟まれている点です。

perldoc.jp

\w\W についてはPerl入学式でも解説してあります。

\w ... アルファベット, 数字, アンダーバーの1文字 [a-zA-Z0-9_]と同じ意味です.

\W ... アルファベット, 数字, アンダーバー以外の1文字 [^a-zA-Z0-9_]と同じ意味です.

github.com

以上を踏まえて、正規表現を書き直してみます。

$entry =~ /\bCOVID/i 
#!/usr/bin/env perl
use strict;
use warnings;

my @entries
    = (
    'No.1: covid-19の症状について',
    'No.2: COVID-19の症状について',
    'No.3: COVID19の症状について',
    'No.4: COVIDの症状について',
    'No.5: <a href="https://www.nicovideo.jp/watch/sm9">再ブレイク!レッツゴー陰陽師</a>',
    );

for my $entry (@entries) {
    if ( $entry =~ /\bCOVID/i ) {
        print "WARNING!: $entry\n";
    }
}

# 実行結果
# WARNING!: No.1: covid-19の症状について
# WARNING!: No.2: COVID-19の症状について
# WARNING!: No.3: COVID19の症状について
# WARNING!: No.4: COVIDの症状について

無事、niconico動画のURLについてはマッチしなくなりました。

今回、COVIDの前にだけ \b を置いたのは、COVIDの後に「-(ハイフン)数字」や「数字」が続くパターンがあり、文字の境界である \w, \W の判別を行なっていないためです。

  • ハイフンが0個以上ある
  • 数字が0個以上ある

という条件を追加することで、より正確なにマッチさせることが可能になります。(マッチしたものは使わないので?:を使っています)

$entry =~ /\bCOVID(?:(-)?\d+)?\b/i

というわけで

Perl入学式 in東京 第5回は延期となってしまいましたが、こういった小ネタを拾っていきたいと思っています。

後、「これをするにはどうしたら良いか?」とか「これをやりたい」「書いてみたがうまく動かない」みたいなのがある方、Perl入学式Slackに書き込んでいただくと、寄ってたかって回答が来ます。ぜひご利用ください。

うまく動かないコードなどはblogやgistなどに貼り付けていただくと、より質の高い回答が来ると思います。

docs.google.com

元ネタ

noteの深津さんのtweetでした。