2015年8月31日月曜日

年齢を計算する関数

過去に実装した年齢計算プログラムのロジックが残念なことになっていましたので、考えを整理しました。
記事の最後に載せたプログラムのコメントにも書きましたが、ポイントは2つあります。
  1. 基準日の年と生年の差
  2. 基準日がその年の誕生日よりも前のときは1を減算する
2つ目の条件につきましては、生年月日の年を基準日の年と置き換え、年齢を求める日付と比較すれば判定できます。
「年齢を求める日付」はいい言葉が思いつきませんでした。いい言葉をご存知の方はコメントして頂けると助かります。
2015/8/31
NARITA Shoさんから「Excel 界隈では「基準日」という語が使われているみたいですね。」とのコメントを頂きました。ありがとうございました。

以下、xyzzy lispとC#での実装を記載します。
2015/9/2
C#での実装例を追加しました。

2015年6月1日月曜日

Javaの正規表現で後方参照を使用する

テキスト処理で重要になる正規表現と後方参照。
Javaで実装するサンプルを2つ紹介します。

$nを使用する方法
Matcher#replaceAllメソッドや、Matcher#replaceFirstメソッドは$nで後方参照を使用できます。
String regex = "\\[([0-9]+)\\]";
String src = "[1] [2] [3] [4] [5]";
 
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(src);
String  result1 = matcher.replaceAll("($1)");
System.out.println(result1);
// => (1) (2) (3) (4) (5)


Matcher#groupを使用する方法
Matcher#groupメソッドを使用する方法は多少複雑です。
正規表現にマッチする部分は、Matcher#startメソッドとMatcher#endから取得できます。
この部分文字列を置き換えてしまえばいいというわけです。
こちらの方法では、後方参照で取得した文字列を元にして、編集を加えられるというメリットがあります。
サンプルではアラビア数字から漢数字へ置き換えています。
String regex = "\\[([0-9]+)\\]";
String src = "[1] [2] [3] [4] [5]";
String[] kan = {"一", "ニ", "三", "四", "五"};
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(src);
StringBuilder result2 = new StringBuilder();
int lastEnd = 0;
String outOfMatch = "";
while(matcher.find()) {
    outOfMatch = src.substring(lastEnd, matcher.start());
    String group = matcher.group(1);
    int n = Integer.parseInt(group);
    String kanN = kan[n-1];
    result2.append(outOfMatch);
    result2.append("(" + kanN + ")");
    lastEnd = matcher.end();
}
outOfMatch = src.substring(lastEnd);
result2.append(outOfMatch);
System.out.println(result2);
// => (一) (ニ) (三) (四) (五)

以下、サンプルソース全体です。

2015年5月11日月曜日

Javaで文字列を表示した時の幅を取得する

FontオブジェクトとFontMetricsオブジェクトを使用して、簡単に取得できます。
簡単なのでいきなりサンプルプログラムです。
import java.awt.Font;
import java.awt.FontMetrics;
import sun.font.FontDesignMetrics;

public class Program {

    public static void main(String[] args) {
        // Fontオブジェクトをインスタンス化する
        // FontオブジェクトからFontMetricsオブジェクトを取得する
        // FontMetrics#stringWidthメソッドで文字列の幅を取得する
        Font font = new Font("Monospace", Font.PLAIN, 12);
        FontMetrics fontMetrics = FontDesignMetrics.getMetrics(font);
        String data = "Hello FontMetrics";
        int width = fontMetrics.stringWidth(data);
        System.out.println(width);
    }
}

※このプログラムをコンパイルすると警告が出ます。

2015年4月27日月曜日

JavaのExecutorServiceのサンプル

Javaでスレッドを使用するには、ExecutorService#invokeAllを呼ぶのが簡単です。ExecutorService#inokeAllは別スレッドで実行するCallableオブジェクトのコレクションを引数として受け取り、処理がすべて完了するまで現在のスレッドをブロックします。処理の結果はFutureのリストとして返ります。
サンプルとして、複数のホストに対してpingコマンドを発行するプログラムを作成しました。
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class Program {
    
    public static void main(String[] args) throws Exception {

        ExecutorService svc = Executors.newFixedThreadPool(3);

        try {
            List<Callable<Integer>> listCallable = new ArrayList<>();
            for (String ipAddress : args) {
                listCallable.add(new PingCommand(ipAddress));
            }
            List<Future<Integer>> listFuture = svc.invokeAll(listCallable);
            for (Future<Integer> future : listFuture) {
                System.out.println(future.get());
            }
        } finally {
            svc.shutdown();
        }
    }

    private static class PingCommand implements Callable<Integer> {
        private final String ipAddress;

        private PingCommand(String ipAddress) {
            this.ipAddress = ipAddress;
        }

        public Integer call() {
            try {
                String[] args = {"ping", ipAddress, "-c", "3"};
                Process proc = new ProcessBuilder(args).start();
                return proc.waitFor();
            } catch (Exception e) {
                e.printStackTrace();
                return -1;
            }
        }
    }
}

参考
ExecutorService
Callable
Future

2015年4月20日月曜日

Windows PowerShellでExcelが出力したCSVを読み込むには

先日、Excelで作成されたドキュメントを一括処理する作業をしていました。
最近「Windows PowerShellいいよ」という話をみかけたので、Windows PowerShellで自動化することにしました。

Windows PowerShellには、CSVを取り込むためのコマンドレットがあり、"Import-Csv"といいます。
ところがこのコマンドレット、Windows 7に標準でインストールされているバージョンでは、取り込む対象のエンコーディングを指定できないという致命的な欠点があります。Excelが出力するCSVのエンコーディングはShift-JIS。一方Import-CSVが対応しているのはUnicode。困りました。

解決策は2つあります。
1. Windows PowerShellをアップデートする
Windows PowerShell 3.0以降、Import-CSVに-Encodingパラメータが追加されました。"-Encoding Default"を指定すれば、Shift-JISのファイルにも対応できます。アップデートファイルは、下記参考の"Windows Management Framework 4.0"から取得してください。
Import-Csv -Path .\Book1.csv -Encoding Default

2. Get-ContentとConvertFrom-Csvを使用する
Get-ContentはShift-JISに対応していますので、Get-ContentとConvertFrom-Csvをパイプでつなぐことで読み込めます。
Get-Content .\Book1.csv | ConvertFrom-Csv

参考
Windows Management Framework 4.0
ConvertFrom-Csv
Get-Content
Import-Csv

2015年3月23日月曜日

Clojureでawtの画面を表示

ClojureからawtのWindowを表示するだけのサンプルプログラムです。
(ns myawt)

(import '(java.awt Frame)
        '(java.awt.event WindowListener))

(def window-width 600)
(def window-height 480)

(doto (Frame.)
    (.addWindowListener
        (proxy [WindowListener] []
            (windowActivated [e])
            (windowClosed [e] (System/exit 0))
            (windowClosing [e] (.dispose (.getWindow e)))
            (windowDeactivated [e])
            (windowDeiconified [e])
            (windowIconified [e])
            (windowOpened [e])))
     (.setSize window-width window-height)
     (.setVisible true))
windowClosedとwindowClosingのイベントハンドラを実装するのがポイントです。

参照
Frame
WindowListener

2014年9月29日月曜日

行指向ファイル処理フレームワーク

業務でファイルを一行ずつ読み込んで処理するプログラムをコーディングしているのですが、提供された設計書のフローチャートに従って実装を進めていたら、脳みそがぐちゃぐちゃにかき回されるようないらだちを覚えました。
"ファイルを一行ずつ読み込んで処理する"という要件は、あちこちで需要がありそうなので、モデルを考えてみました。 Process、FileFinder、FileHandler、LineHandlerの4つのクラスで構成します。