アーカイブ

‘programming’ タグのついている投稿

CRLFはデータ、LFは改行としたCSVファイルの読み込み

CSVファイルで、データにCRLFが入っていて、レコードの区切りがLFというものを1行ずつ処理する必要に迫られたので、作りました。言語はJavaです。実際、そんなデータがあるのかって話ですが、あるから作ったのですよ。

BufferedWriterのreadLineでは、CRLFだろうが、LFだろうが、改行コードを行の区切りとして読み込みます。今回の要件は、CRLFはデータとして、LFのみはレコードの区切りとして扱わなければなりませんので、readLineでなく、readを使ってCRLFは改行として扱わないということになります。

public class ReadLineSample {

    private Reader r = null;

    private static final char CR = '\r'

    private static final char LF = '\n'

    public ReadLineSample(Reader r) {
        this.r = r;
    }

    public String readLine() throws IOException {

        StringBuilder sb = new StringBuilder();

        // 読み込みフラグ。改行コードCRを検出した場合にON。
        boolean hasCR = false;

        while(true) {
            int i = r.read();
            if(i < 0) {
                // ストリームの終端

                if(sb.length() == 0) {
                	// バッファに文字を読み込んでいない場合
                    return null;
                }

                   return sb.toString();
            }

            char ch = (char)i;

            if(ch == LF && !hasCR) {
                // 改行コードLFであり、1文字前がCRでない
                return sb.toString();
            }

            sb.append(ch);

            if(ch == CR) {
                // CR
                hasCR = true;
                continue;
            }

            // フラグをOFF
            hasCR = false;
        }
    }
}

ストリームの終端でnullを返すので、呼び出し側はBufferedReaderのreadLineを使う時と同じロジックになります。このままでも使えるし、メソッドだけをコピペしても使えます。シグネチャが同じなので、BufferedReaderのサブクラスにして、readLineをオーバーライドしても使えると思います。

実際に使いどころがあるかというと、微妙なところです。調べてみても見つからないので、晒してみようっていうだけです。ないってことは、誰もこんなもの必要としてないってことなんだろうなぁ。

カテゴリー: プログラミング タグ: ,

GaucheでFizzBuzz

新しい言語を学ぶと、だいたいHello World!からやります。んで、分岐と繰り返しの文法を覚えたらFizzBuzzを書いてみることにしています。今回はLISPの一種であるGaucheでやってみました。Scalaをやる上で関数型言語のノウハウを勉強するためにGaucheに手を出したのです。

FizzBuzz問題は簡単です。1から100までをカウントアップして、3の倍数の場合はFizzを出力し、5の倍数の場合はBuzzを出力し、3の倍数かつ5の倍数の場合はFizzBuzzを出力し、それ以外の場合は数値をそのまま出力するだけです。分岐と繰り返しがわかっていれば作れるプログラムです。

ただ、関数型言語の場合、繰り返しは再帰を指すようです。つまり、CやJavaでfor文を使う部分を再帰で行うわけです。繰り返し構文(実際は関数)を使ったら、コンセプトから外れるので、再帰でFizzBuzzということになります。

(define (fizzbuzz n)
  (if (< 100 n)
      '()
      (cons (cond
       ((= (remainder n 15) 0) (string-copy "FizzBuzz"))
       ((= (remainder n 5) 0) (string-copy "Buzz"))
       ((= (remainder n 3) 0) (string-copy "Fizz"))
       (else n)) (fizzbuzz (+ n 1)))))

かなり印象が変わりますね。シンプルするために、1から3までをカウントアップする関数を作ってみます。

(define (countup n)
  (if (< 3 n)
      '()
      (cons n (countup (+ n 1)))))

これは以下のコードと同じです。

 (cons 1 (cons 2 (cons 3 '())))

関数の内側から評価されるので、以下のようになります。

  1. (cons 3 ‘())が評価され、(3)が返る。
  2. (cons 2 ‘(3))が評価され、(2 3)が返る。
  3. (cons 1 ‘(2 3))が評価され、最終的な結果は(1 2 3)

手続き型の言語だと、馴染みがないので理解しづらいところです。

カテゴリー: プログラミング タグ: ,

ScalaでServletを使ってみる

Scalaを仕事をサボって勉強しています。ScalaはJava VM上で動くオブジェクト指向+関数型言語で、Javaクラスを使ったり、ScalaクラスをJavaで使ったりできるんですね。とても楽しいです。

インタープリタで対話的に実行できるし、スクリプト言語のように実行できるし、コンパイルして実行することもできます。実行環境は、MacPortsで入れることができるので、インストールもお手軽にできます。

んで、Scalaをコンパイルすると、.classファイルができる。つまり、Javaをコンパイルした時と同じモノが出来るわけです。このおかげで、JavaとScalaはシームレスな連携をするので、ScalaでWebアプリを実装できるんじゃねーかと思い、やってみた。

環境は以下の通り。

  • Mac OS 10.5.6
  • Eclipse 3.4.2 + Scala Eclipse Plugin 2.7.3 final
  • JVM 1.6.0
  • Tomcat 6.0.18

EclipseはJava EEの開発なので、WTPが入ってるヤツです。Tomcatでなく、GlassFishあたりでも動きますが、スタンダードなのを使っています。Windowsのことは知りませんが、同じようにすれば動くんじゃないですかね?

Tomcatを入れる

Tomcatをダウンロードします。

http://tomcat.apache.com/index.html

解凍して適当なディレクトリに置きます。以下のような感じです。

~/tomcat/apach-tomcat-6.0.18

http://localhost:8080/で動作確認だけできればいいので、落としてコピーして終わりです。コマンド叩いてメンドーなことはしません。

Eclipseにプラグインを入れる

Scala Eclipse Pluginは、以下のアップデートサイトから手に入れることが出来ます。

http://www.scala-lang.org/scala-eclipse-plugin

Scala実行環境もこれで入りますが、PATHは通らないので、対話的に実行したりする場合は別途MacPortsとかしたほうがいいかもしれません。

Scalaのプラグインを入れたので、Scalaプロジェクトが作れるようになっているはずです。

作成

プロジェクトの構成ですが、Scalaプロジェクト+動的ウェブプロジェクトという構成にします。動的ウェブプロジェクトのソースフォルダにScalaのソースが作成できないため、Scalaプロジェクトを別にしています。パスを通せば問題ないので、この構成作っていきます。

新規作成からScala Projectを作ると、見慣れた感じのプロジェクトができるので、ソースフォルダにパッケージを作ります。なんでもいいんですが、「jp.my.webapp.servlet」という名前で話を進めます。

このパッケージにScalaソースファイルを作成します。javax.servlet.httpのクラスを使用するため、ビルドパスにサーバ・ランタイムを設定する必要があります。

package jp.my.webapp.servlet

import javax.servlet.http.{
  HttpServlet,
  HttpServletRequest => HSReq,
  HttpServletResponse => HSResp
}

class TestServlet extends HttpServlet {

  override def doGet(req :HSReq, resp :HSResp) {
    val message =
      <HTML><HEAD><TITLE>Hello, Scala</TITLE></HEAD>
      <BODY><P>Hello, Scala with Servlet</P></BODY></HTML>
    resp.getWriter().print(message)
  }
}

import文は{}で括ることによって、パッケージから複数のクラスをimportできます。「クラス名=>省略名」という書き方も出来ます。ScalaではXMLリテラルを書くことができ、整形文で文法にエラーがなければHTMLもリテラルにできます。XMLリテラルは、scala.xml.Elem型で、printメソッド内でtoStringにより文字列化されます。

これでScalaによるServletは終了です。次は、webプロジェクトです。

こちらはさらに簡単です。パスを通して、web.xmlを書いて終わりです。プロジェクトはwebappという名前で作りました。

パスは、Scalaプロジェクトとscala-library.jarに通します。Java EE Module Dependencies(日本語化してると「Java EE モジュール依存関係」だかです)にプロジェクトとjarを追加します。scala-library.jarはScalaプロジェクトを見ればパスが書いてありますが、あまりに深いところにあったので、MacPortsで入れた実行環境の方に通してしまいました。

web.xmlにservletとservlet-mappingを記述します。

<servlet>
  <servlet-name>HelloScala</servlet-name>
  <servlet-class>jp.my.webapp.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>HelloScala</servlet-name>
  <url-pattern>/sayHello/</url-pattern>
</servlet-mapping>

あとは新規作成でサーバを作り、解凍したTomcatのディレクトリを指定すると、サーバビューにTomcatが登録されるので、webappプロジェクトをドラッグ&ドロップで追加します。そして、サーバを起動し、ブラウザでhttp://localhost:8080/webapp/sayHello/へアクセスしてみます。

アクセスした様子

アクセスした様子

ウェブフレームワークは使わずにやってみましたが、StrutsやSpring、DWRといったものを使ってもよさそうです。Scalaのウェブフレームワークもあるようですね。最初はDWRでAjaxを目論んでいたんですが、クラスパス周りでハマり、エラーの切り分けがよくわからないので、フレームワークなしとなりました。

ソースに関しては、IBMのdeveloperWorksとほぼ同じです。実装については、このエントリーよりも優しく書いてあるので、そちらを参考にした方がいいと思います。

参考:多忙な Java 開発者のための Scala ガイド: Scala とサーブレット

カテゴリー: プログラミング タグ: , ,

Javaで末尾の全角/半角スペースをトリムする

夜も遅いっていうか、もう朝なので簡単にメモ。

Javaで文末の全角半角スペースをトリムするという、実に簡単な実装なんだけど、いくつか実装方法があって、性能としてどれが最も優れているか気になったので、テストしてみました。実装方法は以下の3つ。

  1. 正規表現(String#replaceAll)
  2. StringBuilderで後方から走査して、半角/全角スペースの場合は削除する
  3. char配列で後方から走査して、半角/全角スペースでない場合は新しいchar配列に格納する。

上記の実装をstaticメソッドにして、それぞれ1000回ずつ呼び出して、プロファイルしてみました。環境はMac OS 10.5、JDK1.5、NetBeans 6.5です。

結果は、3>2>1の順で高速に動きました。1は正規表現を使っているので最も遅かったですね。

  1. 正規表現:7.85ms
  2. StringBuilder:2.45ms
  3. char配列:0.464ms

正直、StringBuilderとchar配列でここまで差が出るとは思わなかった。配列とStringでインスタンスを2回作るので2と3はそんなに差が出ないと思ったんですけどね。何事もやってみるもんです。

以下はソース。乱暴に書いてます。

public class Main {

private static final String VALUE = "あいう    ";

    public static void main(String[] args) {

        for(int i = 0; i < 1000; i++) {
            trim1(VALUE);
        }

        for(int i = 0; i < 1000; i++) {
            trim2(VALUE);
        }

        for(int i = 0; i < 1000; i++) {
            trim3(VALUE);
        }
    }

    public static String trim1(String value) {
        return value.replaceAll("( | )+\\z", "");
    }

    public static String trim2(String value) {

        StringBuilder sb = new StringBuilder(value);

        for(int i = sb.length()-1; 0 <= i; i--) {
            char c = sb.charAt(i);
            if(c == ' ' || c == ' ') sb.deleteCharAt(i);
            else break;
        }
        return sb.toString();
    }

    public static String trim3(String value) {

        char[] ary = value.toCharArray();
        char[] trimed = new char[ary.length];

        for(int i = ary.length-1; 0 <= i; i--) {

            if(ary[i] == ' ' || ary[i] == ' ') continue;

            else trimed[i] = ary[i];

        }

        return new String(trimed);
    }
}
カテゴリー: プログラミング タグ: ,