SEO:ブログ記事を検索結果から消さないために

ブログを始めたのは2008年1月からですが,ブログのアクセス解析を見ると,最初は少なかったアクセス数が3月頃に増え始めて3月後半から4月前半にピークを迎えました(最も多い日で訪問者60,アクセス数90)。しかしその後がっくり落ちて,1日のアクセス数が5件にも届かなくなってしまっていました。ブログの更新頻度が低く記事の数も少なかったというのもありますが,それにしても落差がありすぎるのはなんでだろうと思っていろいろ調べてみたところ,次のようなことがわかりました。

  • 当ブログの訪問者のほとんどはGoogle検索からやってくる。
  • ブログの場合,Googleの検索結果にはGoogleブログ検索へのPingでも載るが,Pingで載った場合は一定のアクセスがある記事を除き数週間程度で検索結果から消えてしまうようだ。
  • Googleの検索からは数週間で消えるが,「Googleブログ検索」からは検索可能。しかしそもそもGoogleブログ検索からやってくる人はほとんどいない。
  • 記事のリンクがココログの「ココフラッシュ」や「瞬!コレ」などに載ることがあり,これらのページがGoogleにインデックスされてそこからアクセスしてくる人がまれにいる。しかし「ココフラッシュ」は同一カテゴリの記事の投稿があれば古いものから消えていくし,「瞬!コレ」への登録は全く制御できないので当てにはできない。
上記のうち,アップしてから一定期間が経過したブログ記事がGoogleの検索結果から消えてしまっているのを発見したときは衝撃を受けました。誰も興味を持たない話題をアップしているとか,検索結果の順位が低くてアクセス数が少ないならまだ納得がいきますが,検索結果から消えてしまっては…そりゃ誰も来ませんね。ということで,この記事ではブログ記事が検索サイトのインデックスから消えないようにするためにこちらでやってみたこと,まだやってないけど調べたことなどを紹介いたします。

ブログのURLを検索サイトに登録する
検索サイトにURLを登録することで,検索サイトにウェブクローラが巡回してほしいサイトを知らせることができます。各検索サイトへは以下から登録できます。但し登録を行ったサイトが検索結果に表示されることは保障はされていません。
 Google Web登録登録確認 ディレクトリ登録
 Yahoo! Web登録登録確認解説カテゴリ登録
 bing Web登録 解説 
goo及びBIGLOBEはGoogleのインデックスを使っているそうです。(出典1)(出典2

ウエブマスターツールに登録する
GoogleウエブマスターツールではGoogleに収集された自分のサイトについて,ウェブクロールの際に発生したエラー一覧,Google検索で自分のサイトが表示されたときの検索語一覧,インデックス情報のモニタ,robots.txtの文法チェック,サイトマップ(後述)の登録更新,削除したWebページのGoogleインデックスからのURL削除,グーグル八分になったときのサイトの再審査リクエストなどを行うことができます。
とりあえず(もっていなければ)Googleアカウントを取得して,ウエブマスターツールに自分のブログを登録してみることをお勧めします。
なお,ウェブマスターツールのすべての機能を使うには,ブログのHTMLファイルに認証コードを埋め込むか,トップディレクトリに認証ファイルを置く必要があります。
※ココログの場合,HTMLファイルを直接編集できるのは「プロ」だけです。
Yahoo!でウェブマスターツールに相当するのが「サイトエクスプローラ」になるかと思います。サイトマップの登録もここから行います。こちらにもYahoo! JAPAN IDを取得して登録してみることをお勧めします。
Googleウェブマスターツールログインヘルプ
Yahoo!サイトエクスプローラログインヘルプ

サイトマップを作る
ウェブクローラが参照するためのTXTまたはXML形式のサイトマップを用意することで,ブログの個別の記事をウェブクローラが見つけやすくなります。ブログでサイトマップを利用するには,ブログの少なくともトップディレクトリへファイル転送できる機能が提供されている必要があります。
サイトマップのファイル名は任意です。1つのサイトで複数のサイトマップを持つことができます。サイトマップは他のサイトや上位ディレクトリのURLを記述できません。

TXT形式のサイトマップは,テキストファイルにサイトを構成するページのURLを「http://」を含めて1URLごとに改行して記述します。
XML形式のサイトマップは,各ページのURLのほかにオプションで,最終更新日(lastmod),更新頻度(changefreq),ページの優先度(priority)を記述できます。
XML形式のサイトマップの書き方についての詳細は,[Google][Yahoo!][本家sitemaps.org]を参照してください。

例:当ブログのサイトマップ(テキスト形式)
http://endeavour.cocolog-nifty.com/developer_room/index.html
http://endeavour.cocolog-nifty.com/about.html
http://endeavour.cocolog-nifty.com/developer_room/cat8156880/index.html
http://endeavour.cocolog-nifty.com/developer_room/cat8157219/index.html

(略)
注:当ブログのサイトマップは実際にはXML形式です。

例:当ブログのサイトマップ(XML形式)
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">

<url>
<loc>http://endeavour.cocolog-nifty.com/developer_room/index.html</loc>
<changefreq>weekly</changefreq>
<priority>0.3</priority>
</url>
(略)
</urlset>
作成したサイトマップをブログのトップディレクトリに保存して,検索サイトにサイトマップの登録更新を伝えます。その方法は,
  • Googleの「ウェブマスターツール」やYahoo!の「サイトエクスプローラ」でサイトマップを登録または更新する。(「サイトエクスプローラ」では「フィード」にサイトマップを登録する。)
  • 「robots.txt」に「Sitemap: http://サイトマップのURL」行を追加する。robots.txtは通常ウェブクローラに対して巡回してほしくないディレクトリパスを指定するために使いますが,サイトマップのURLを指定することもできるようになったようです。
例:上記XML形式のサイトマップを指定した当ブログのrobots.txt
User-Agent: *
Sitemap: http://endeavour.cocolog-nifty.com/endeavour_sitemap.xml

注:ココログに「Googleサイトマップ」という機能があるのを最近見つけました。
当方では自力でサイトマップを作ってしまったのでこの機能をOFFにしていますが,この機能をONにすればサイトマップの作成はGoogleに対しては必要ないかもしれません。但しYahoo!に対しては別途サイトマップの送信等の処理が必要かと思われます。ココログの「Googleサイトマップ」の設定は「ブログ」→「設定」→「更新通知」で行います。

メタ情報を設定する
ココログでは各HTMLファイルにkeywordsとdescriptionのメタタグを追加するようになりました。
ここで追加するメタタグのうち,descriptionはブログのサブタイトルがデフォルトで設定されますが,「ブログ」→「設定」→「更新通知」の「meta情報の生成」で変更することができます。
このメタタグの効果はまだよくわかりませんが,すべての記事に同じメタタグが挿入されてしまい,記事ごとの説明文を設定できないのが少々困りものです。(かといって,できたらできたで面倒くさい!となってしまいますが…笑)Googleウェブマスターツールでも「重複description」の警告が大量に出てしまいます。

ブログランキングサイトへの登録
にほんブログ村 IT技術ブログへとか人気ブログランキングへとかのことです。前者の「にほんブログ村」の場合,「トラコミュ」というブログ記事のトラックバックでコミュニティを構成する機能があり,アクセス数に左右されない外部リンクが確保できます。

これまでの成果
サイトマップの登録を8月に行ったところ,検索結果から消える現象にかなりの改善が見られました。また,これに伴いアクセス数も回復しつつあります。
ただし新たな問題も見つかっています。それは,サイト内でリンクを張っていた場合にその中の代表ページをGoogleが判断してそれ以外のページをインデックスから削っていると思われる問題です。これはこのブログの記事では「Java:CSVパーサを作る」の「その1~3」のうち,検索結果に表示されるのが「その2」だけ,といった形で現れてきています。

…あとはこのブログの記事の質と量の問題でしょうか。

■関連書籍をAmazonで検索:[SEO][Google][Yahoo!]
できる社長はネットで売らない―WEB化社会の営業革新

にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ Webサイト構築へ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
Sony Style(ソニースタイル)アフィリエイトならリンクシェア

| | トラックバック (0)

Mac OS X Server:PostfixでOP25B対応

【OP25Bとは】
Outbound Port 25 Blockingの略です。ISP(インターネットサービスプロバイダ)各社が,自ドメインの利用者がユーザ回線を使ってインターネットへスパムメールを送信するのを阻止するために導入しているもので,プロバイダのユーザ回線からインターネットへ出て行く外向きのTCP25番ポートへの接続を塞いでしまう,というものです。

【OP25Bの影響】
OP25BはSMTPに使う25番ポートを塞ぐので,POPIMAPには全く影響ありません。
OP25Bはユーザ回線に対して実施されるもので,固定IPの回線には影響ありません。
OP25Bは内から外へ出て行く25番ポートへの接続を阻止するので,外から内へ入ってくる接続には影響しません(外から内へ入ってくる25番ポートへの接続を阻止するのは「IP25B」)。
使用しているISPがOP25Bを導入したとき,ユーザへの影響には以下の3つのケースがあります。

  1. メールクライアントからプロバイダが提供する送信用SMTPサーバを使ってメール送信する場合,ISPのメールサーバはインターネットに出て行く手前にいるため接続はOP25Bに阻止されずにこれまでどおり25番ポートを使えることが多く(プロバイダによる),この場合は特に影響ありません。
  2. メールクライアントからインターネットの向こう側にいる送信用SMTPサーバを使ってメール送信しようとした場合(独自メールサーバのアカウントからメールを送る場合など),メールクライアントとSMTPサーバの接続がOP25Bで阻止されます。
  3. ユーザプログラム(ボット含む)や独自メールサーバがユーザ回線を使ってメールを転送する場合,OP25Bで阻止されます。独自メールサーバからメールを転送するには固定IPが必須になります。
上記のうち,OP25Bは3.でのメール送信阻止を狙ったものです。それに対して2.は関係ないのに巻き添えを食らっており,対策が必要です。

【メールクライアントのための対策:サブミッションポート】
メールクライアントのための独自メールサーバの対策として,メールクライアントが25番ポートで接続してくることができないので,メールクライアントに対して25番以外のポートから接続できるように設定する必要があります。
そのための代替ポートとして「RFC2476」にて「サブミッションポート(TCP587番ポート)」が定められています。
サブミッションポートはメールクライアントからSMTPサーバに接続するときに使い,SMTPサーバどうしのメール転送は従来どおり25番ポートを使います。また,サブミッションポートへの接続にはSASL認証(SMTP Auth)を課すことで,「単にポート番号が変わっただけ,587番ポートからスパム送り放題」とならないようにします。

※サブミッションポート以外の対応策として,SMTPサーバをSSL化し,SSL用のポートを使用する,という手もあります。

【Mac OS X Serverで稼動するPostfixでサブミッションポートを利用する手順】
1)SASL認証を有効にする
Mac OS X ServerにプリインストールされているPostfixでは,SASL認証のためのモジュールが最初から組み込まれています。SASL認証を有効にするには,まず「サーバ管理」ツールにて「コンピュータとサービス」で「メール」を選び,「設定」→「詳細」→「セキュリティ」でセキュリティパネルを表示します。ここで「認証」→「SMTP」列のいずれかのチェックボックスをONにして「保存」ボタンを押すとSASL認証がONになります。

※SASL認証のユーザ名とパスワードは,Mac OS X Serverではワークグループマネージャに登録したユーザ名とパスワードになります。すなわちメール受信用のユーザ名とパスワードと同一になります。

2)サブミッションポート(TCP587番ポート)を有効にする
「/etc/postfix/master.cf」ファイルを編集します。事前にバックアップを取った上で「sudo vi」などでファイルを開き、

#submission inet n - n - - smtpd
# -o smtpd_etrn_restrictions=reject
この2行について,最初の「#」(コメントアウト)を削除します。(注:「-o」の前のスペースを削除してはいけません。)
ここでmaster.cfを保存して終了し,コマンドラインで「sudo postfix check」と入力してエラーチェックを行います。問題なければ「サーバ管理」でメールサービスをリスタートするか,コマンドライン上で「sudo postfix reload」コマンドを実行すると,サブミッションポートが有効になります。このときサブミッションポートに適用されるパラメータ値は,main.cfで定義された25番ポートのパラメータ値と同じです。

3)サブミッションポートのパラメータ設定
サブミッションポートで適用されるパラメータ値は「/etc/postfix/main.cf」で定義した25番ポート用の値がデフォルトとして適用されます。
サブミッションポートで適用されるパラメータ値を25番ポートとは別の値にしたい場合は,master.cfのsubmissionセクションの末尾に「-o」+「パラメータ=値」で設定したいパラメータ値を追記することで,サブミッションポートのパラメータ値が上書きされます。このとき「-o」+「パラメータ=値」の記述で気をつけなければならないのは,
  • 「-o」の前後にスペースが必要。行頭のスペースは前の行からの継続を意味する。
  • スペースで区切って解釈されるため,「パラメータ=値」は途中でスペースを入れない。
の2点です。
設定例1:
submission inet n - n - - smtpd
 -o smtpd_etrn_restrictions=reject
 -o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject

「パラメータ=値」の途中でスペースを入れることができませんが,「check_sender_access tyep:table」などのように間にスペースを入れなければならないパラメータ値もあります。このような値を指定する場合は,main.cfでユーザ定義のパラメータ値を定義して,master.cfでこれを指定することができます。具体的には,main.cfにて「name = パラメータ値」とパラメータ値に名前を付け,master.cfでこの名前を「${name}」と呼び出すと,これがパラメータ値に展開されます。
設定例2:
main.cfの設定:ユーザ定義パラメータ「check_sender_mydomain」の定義
check_sender_mydomain = check_sender_access hash:/etc/postfix/permit_mydomain

master.cfの設定:ユーザ定義パラメータ「check_sender_mydomain」の利用
submission inet n - n - - smtpd
 …
 -o smtpd_sender_restrictions=permit_mynetworks,${check_sender_mydomain},reject
main.cf及びmaster.cfを編集保存したら「sudo postfix check」でエラーがないか確認し「sudo postfix reload」で修正内容を実行環境に反映します。

以上でPostfixの設定は完了です。設定完了後LAN内からメールを送信してみてうまくいったら,外部と接続するルータなどでもサブミッションポートへの接続を許可する設定にして,今度は外部からメール送信できるか確認します。

■関連情報
ISPによるOP25B 実施状況((財)日本データ通信協会・迷惑メール相談センター)
各ISPによるOP25Bの解説ページインデックスOP25B連絡会
Postfix 2.1日本語ドキュメント:[基本設定][設定パラメータ][master.cfファイルフォーマット][master.cfコメント訳文]
Appleサポート:Mac OS X Server 10.4送信メールを設定する
Apple Server 製品ドキュメント:メールサービスの管理[PDF]

■関連書籍をAmazonで検索:[Postfix][Mac OS X Server]
Postfix実用ガイド

■Apple Store:アップルサーバ3点セット:[Mac OS X Server][Xserve][Apple Remote Desktop]

にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ ネットワーク・SEへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
東急ハンズのネット通販「ハンズネット」120×60サイズioPLAZA【アイ・オー・データ直販サイト】

| | トラックバック (1)

Mac OS X Server:Postfixとメールクライアントの接続問題

【問題】
Mac OS X Server 10.4.x(Tiger Server)のメールサーバ(Postfix)を設定するとき,「/etc/postfix/main.cf」ファイルのパラメータのうち,以下の各パラメータのチェックを厳しくすると,このメールサーバに接続するメールクライアントからメール送信ができなくなることがあります。

パラメータ内容
smtpd_client_restrictionsクライアントからの接続要求のチェック
smtpd_helo_restrictionsSMTP HELOコマンドのチェック
smtpd_sender_restrictionsMAIL FROMコマンドのチェック
smtpd_recipient_restrictionsRCPT TOコマンドのチェック
各パラメータの詳細はこちら

【原因】
SMTPは,メールサーバ(MTA)間のメール転送もメールクライアントからのメール送信も同じ25番ポートで受け付けます。そのため両者が同じルールでチェックされてしまい,メールサーバに対してだけ厳しくチェックされるべきパラメータが,メールクライアントにも適用されてしまいます。

【対策】
SASL認証(SMTP Auth)をONにすると,上記の各パラメータチェックで「permit_sasl_authenticated」の指定が意味を持つようになります。各パラメータに「permit_sasl_authenticated」を加えることで,SASL認証にパスしたメールクライアントソフトはそのパラメータのチェックをスキップしてメール送信ができるようになります。下記の設定例では,クライアントのメールソフトからの接続を「reject_invalid_hostname」が阻止してしまうのを「permit_sasl_authenticated」が防いでいます。特定のパラメータ値を「permit_sasl_authenticated」で防ぐには,そのパラメータ値より前に「permit_sasl_authenticated」を入れる必要があります。
設定例:
smtpd_helo_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_invalid_hostname,permit

Mac OS X ServerにプリインストールされているPostfixには,SASL認証に必要なモジュールがデフォルトで組み込まれています。このSASL認証をONにするには,「サーバ管理」ツールにて「コンピュータとサービス」で「メール」を選び,「設定」→「詳細」→「セキュリティ」でセキュリティパネルを表示します。
ここで「認証」→「SMTP」列のいずれかのチェックボックスをONにして「保存」ボタンを押すと,SASL認証がONになります。
ここでの設定は「/etc/postfix/main.cf」ファイルのパラメータ「smtpd_sasl_auth_enable」「smtpd_pw_server_security_options」などに反映されます。

注1:SASL認証のユーザ名とパスワードは,Mac OS X Serverではワークグループマネージャに登録したユーザ名とパスワードになります。すなわちメール受信用のユーザ名とパスワードと同一になります。
注2:認証時のKerberosとCRAM-MD5はログインを暗号化して行い,LoginとPLAINは平文で行います。クライアント側はアップルの「Mail」はCRAM-MD5で認証を行い,マイクロソフトの「Outlook」「Outlook Express」はLoginで認証を行います。

【追記】
Postfixのドキュメントから設定可能なパラメータを調べるために,Mac OS X Serverで稼動しているPostfixのバージョン番号を知る必要がありますが,「サーバ管理」ツールではこれは表示されません。
Postfixのバージョン番号を知るには,コマンドライン上で
postconf mail_version
と入力することでバージョン番号を知ることができます。
ちなみにMac OS X Server 10.4.11のPostfixのバージョンは2.1.5でした。

■関連情報
Postfix 2.1日本語ドキュメント: [基本設定][設定パラメータ]
Appleサポート:Mac OS X Server 10.4送信メールを設定する
Apple Server 製品ドキュメント:メールサービスの管理[PDF]

■関連書籍をAmazonで検索:[Postfix][Mac OS X Server]
Postfix詳解―MTAの理解とメールサーバの構築・運用

■Apple Store:アップルサーバ3点セット:[Mac OS X Server][Xserve][Apple Remote Desktop]

にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ ネットワーク・SEへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
Mac ソフトのことなら act2.comMac ソフトのことなら act2.com宿・ホテル予約ならじゃらんnet

| | トラックバック (0)

Mac OS X Server:Webサーバ設定の問題

【問題】
Mac OS X Server 10.4.x(Tiger Server)にて,OS付属のWebサーバApache)を稼動させ,Webクライアントでサーバにアクセスすると,一部のコンテンツが表示されないことがあります。また,ApacheのKeepAlive機能(「サーバ管理」ツールでの表記は「持続的な接続を許可する」)をONにすると,Webクライアントからアクセスしたときに,すべてのコンテンツの表示が完了するのに時間がかかることがあります。

【原因】
Mac OS X ServerのWebサーバには「パフォーマンスキャッシュ」という機能が組み込まれており,デフォルトでONになっています。
このパフォーマンスキャッシュは,通常Webサーバが使用するポート番号80でリクエストを受け付けます。Webサーバ(Apache)は,パフォーマンスキャッシュがONのときはポート番号16080でリクエストを受け付けます。
外部からのリクエストはまずポート番号80が受け取り,パフォーマンスキャッシュでコンテンツがキャッシュされていなければポート番号16080へリダイレクトされます。そのためポート番号16080が外部からアクセスできないとキャッシュにないコンテンツを表示させることができません。

また,パフォーマンスキャッシュがONのときにKeepAlive機能もONだった場合,KeepAliveで設定されたタイムアウト時間がリダイレクト時のウエイトタイムになってしまうようです。そのためたとえばタイムアウト時間が15秒だった場合,Webページ上の画像が1枚表示されるごとに15秒待たされる,ということになってしまいます。

【対策】
パフォーマンスキャッシュをOFFにすれば,とりあえず問題は解消します。パフォーマンスキャッシュをONにしたまま問題が出ないようにするには以下のようにします。

  1. ルータなどの設定により,ポート番号16080を外部からアクセスできるようにする。
    →ポート番号16080にアクセスできるかどうかは,HPのURLがたとえば「http://www.example.co.jp/」だった場合,この URLを「http://www.example.co.jp:16080/」と変更してWebブラウザからHPにアクセスできるかどうかで確認することができます。
  2. KeepAlive機能(「持続的な接続を許可する」)をOFFにする。
    パフォーマンスキャッシュとKeepAliveの両方をONにすると,上記のような問題が生じるので,「自分達のサイトにとってどちらの機能がより恩恵があるか」を検討し,どちらをONにするかを決定することになるかと思います。
■関連情報
Appleサポート:Webパフォーマンスキャッシュについて学習する
Appleサポート:Webサービスがデフォルトでポート80と16080を使用する
Appleサポート:Appleソフトウエアで使用するTCP/UDPポート
Appleサポート:Mac OS X Server 10.4Web サイトを管理する
Apple Server 製品ドキュメント:Webテクノロジーの管理[PDF]

■関連書籍をAmazonで検索:[Apache][Mac OS X Server]
ハイパフォーマンスWebサイト ―高速サイトを実現する14のルール

■Apple Store:アップルサーバ3点セット:[Mac OS X Server][Xserve][Apple Remote Desktop]

にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ ネットワーク・SEへ にほんブログ村 IT技術ブログ Webサイト構築へ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
Mac ソフトのことなら act2.com

| | トラックバック (0)

Mac OS X Server:DNS設定の問題

【問題】
Mac OS X Server 10.4.x(Tiger Server)にて,設定ツール「サーバ管理」でDNSサーバBIND)の設定を行ったときに以下のような問題がありました。
注:この問題がLeopard Serverで改修されているかどうかは未確認です。

●設定が勝手に書き換わる問題
「サーバ管理」は,DNS設定を変更したあとで「保存」ボタンで変更内容を反映するときに,設定ファイルの内容やファイル名をチェックして,エラーと判断した箇所を書き換えているようです。
この機能は,間違った設定のままDNSを運用してしまわないための機能だと思われますが,一部正しいはずの設定をエラーと判定するケースがあるようです。
ここでは現在こちらで把握している不備の内容についてリストアップしたいと思います。

1)逆引き設定をクラスC未満にできない
クラスC未満の逆引き設定を行い,「サーバ管理」の「保存」ボタンを押すと,逆引き設定がクラスCに拡張して保存されてしまいます。

2)SPF設定が消える
SPFの設定は,ドメイン名に対してTXTレコードを割り当てて,そこに記述しますが,ドメイン名へのTXTレコードの割り当ては,「サーバ管理」は設定の誤りと解釈するようで,SPFの設定を行ったあとで「サーバ管理」の「保存」ボタンを押すと,SPFの設定をファイルから削除して保存されてしまいます。

●view設定がエラーになる問題
「view」はBIND9から追加された機能で,Tiger ServerのBINDも9.xなので当然使えるのですが,設定ファイルをviewを使って書き換えてBINDを起動し直すと,「サーバ管理」のステータス表示でDNSがエラー状態になってしまいます。

【対処方法】
設定が勝手に書き換わる問題については,「サーバ管理」による設定の更新を行わないようにするしかないと思います。「サーバ管理」による設定は,最初のドメイン設定ファイルの生成までにして,その後の設定はテキストエディタで直接設定ファイルを編集して「サーバ管理」からは設定は行わないようにします。「サーバ管理」は設定ファイルの書き換えを行ったあとのDNSの起動・停止でのみ利用します。
view機能の障害については,viewを使わないか,エラー表示に目を瞑るしかないようです。

■関連情報
Appleサポート:Mac OS X Server 10.4DNS サービスの概要
Apple Server 製品ドキュメント:ネットワークサービスの管理[PDF]
JPRS:DNS関連技術情報
OPENTECHPRESS:SPF解説

■関連書籍をAmazonで検索:[BIND/DNS][Mac OS X Server]
DNS & BINDクックブック―ネームサーバ管理者のためのレシピ集

■Apple Store:アップルサーバ3点セット:[Mac OS X Server][Xserve][Apple Remote Desktop]

にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ ネットワーク・SEへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。


オフィスの収納家具Mac ソフトのことなら act2.comシマンテックストア

| | トラックバック (0)

Java:CSVパーサを作る(その3) - RFC4180対応 後編

←CSVパーサを作る(その2) - RFC4180対応 前編

前回の記事で未実装だった「レコードの確定」と「レコードのフィールドへの分割」を行うメソッドについて実装を行い,CSVパーサを完成させます。

【実装の考察】
●レコードの確定
レコード確定では,入力テキストデータに対して,ダブルクォーテーション(二重引用符)のペアをヒントに各レコードの末尾を確定して,レコードの切り分けを行います。処理手順は以下のようになります。

  1. BufferedReaderのreadLineメソッドを使ってテキストを1行分(現在の位置から改行が現れるまで,またはファイルの終了まで)取り出して,行の先頭からダブルクォーテーションを探す。見つからなければその1行を1レコードとして確定する。(readLineメソッドは「CR」「LF」「CRLF」を改行と認識するので,「CRLF以外の改行も考慮する」仕様の要求を満たしています。)
  2. ダブルクォーテーションが見つかった場合,ペアになる後ろのダブルクォーテーションを探す。後ろのダブルクォーテーションが見つかったらその位置から後続のダブルクォーテーションのペアを探す。この手順を行の終わりまで繰り返す。ダブルクォーテーションペアの外側で行が終了していれば,その行を1レコードとして確定する。
  3. ペアの後ろのダブルクォーテーションが見つからずにダブルクォーテーションペアの内側で改行に達したら,その改行を文字列フィールドに含まれる改行と見なしてBufferedReaderのreadLineメソッドより次の行を取り出して前の行と連結し,ペアの後ろのダブルクォーテーションを探すところから処理から継続する。これをダブルクォーテーションペアの外側で改行が見つかるまで繰り返す。後ろのダブルクォーテーションが見つからずにファイルの末尾に達したときは,ファイルの末尾にダブルクォーテーションを付加して行の末尾とする。この連結した行を1レコードとして確定する。
以上の処理を行うコードは次のようになります。(2009年6月22日改訂)
//------------------------------------------------------------------
/**
 * BufferedReaderから1レコード分のテキストを取り出す。
 * @param reader 行データを取り出すBufferedReader。
 * @return 1レコード分のテキスト。
 * @throws IOException 入出力エラー
 */
private String buildRecord (
  BufferedReader reader)
  throws IOException
{
  String  result = reader.readLine();
  int    pos;
  if (result != null && 0 < result.length() &&
    0 <= (pos = result.indexOf("\"")))
  {
    boolean  inString = true;
    String   rawline = result;
    String   newline = null;
    StringBuffer buff = new StringBuffer(1024);
    while (true) {
      while (0 <= (pos = rawline.indexOf("\"", ++pos))) {
        inString = !inString;
      }
      if (inString && (newline = reader.readLine()) != null) {
        buff.append(rawline);
        buff.append("\n");
        pos = -1;
        rawline = newline;
        continue;
      }
      else {
        if (inString || 0 < buff.length()) {
          buff.append(rawline);
          if (inString) {
            buff.append("\"");
          }
          result = buff.toString();
        }
        break;
      }
    }
  }
  return result;
}
注意)上記コードでは,フィールド内の改行をLF("\n")に決め打ちしていますが,実際にはCSVパーサの出力結果を受け取るプログラム(データベースなど)が要求する改行コードを挿入する必要があります。
使用すべき改行コードの選択は,プログラムが稼動するプラットフォームの改行コードを取得する
「String returnStr = System.getProperty("line.separator");」
が知られていますが,実行環境によってはCSVパーサとCSVパーサの出力結果を受け取るプログラムが異なるOSで稼動する場合もありうるので,正解は常に一つとは限りません。
そのため,厳密に処理するには出力する改行コードをプロパティファイルで指定できるようにするなどの工夫が必要です。

●レコードのフィールドへの分割
フィールド分割では,レコードに切り分けたテキストに対して,最初にレコード全体をカンマで分割し,分割した個々の文字列にダブルクォーテーションをヒントに必要な連結やエスケープ処理を行って,個々のフィールドを確定します。処理手順は以下のようになります。
  1. レコード全体をStringクラスのsplitメソッドを使ってカンマで分割し,分割した個々の文字列データを順に先頭からダブルクォーテーションを探す。見つからなければその文字列は1フィールドとして確定する。
  2. ダブルクォーテーションが見つかったら,次のダブルクォーテーションを探す。次のダブルクォーテーションの直後にダブルクォーテーションがあれば,エスケープされたダブルクォーテーションとして処理し,そうでなければフィールドの終わりと見なす。
  3. フィールドで後ろのダブルクォーテーションが見つからない場合,フィールドに含まれるカンマでsplitメソッドが分割したものと見なして,フィールドの後ろに(splitメソッドが削除した)カンマと次のフィールドを連結する。
  4. フィールドの開始と終了のダブルクォーテーションは削除する。
以上の処理を行うコードは次のようになります。(2009年6月22日改訂)
//------------------------------------------------------------------
/**
 * 1レコード分のテキストを分割してフィールドの配列にする。
 * @param src 1レコード分のテキストデータ。
 * @param dest フィールドの配列の出力先。
 */
private void splitRecord (
  String    src,
  LinkedList dest)
{
  String[]  columns = src.split(",");
  int     maxlen = columns.length;
  int     startPos, endPos, columnlen;
  StringBuffer buff = new StringBuffer(1024);
  String   column;
  boolean  isInString, isEscaped;

  for (int index = 0; index < maxlen; index++) {
    column = columns[index];
    if ((endPos = column.indexOf("\"")) < 0) {
      dest.addLast(column);
    }
    else {
      isInString = (endPos == 0);
      isEscaped = false;
      columnlen = column.length();
      buff.setLength(0);
      startPos = (isInString)? 1: 0;
      while (startPos < columnlen) {
        if (0 <= (endPos = column.indexOf("\"", startPos))) {
          buff.append((startPos < endPos)?
                column.substring(startPos, endPos): isEscaped? "\"": "");
          isEscaped = !isEscaped;
          isInString = !isInString;
          startPos = ++endPos;
        }
        else {
          buff.append(column.substring(startPos));
          if (isInString && index < maxlen - 1) {
            column = columns[++index];
            columnlen = column.length();
            buff.append(",");
            startPos = 0;
          }
          else {
            break;
          }
        }
      }
      dest.addLast(buff.toString());
    }
  }
}


以上で「RFC4180対応のCSVパーサ」はひととおりできあがったかと思います。

■関連書籍をAmazonで検索:[Java]
ソースコードリーディングから学ぶ Javaの設計と実装



にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ プログラム・プログラマへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
▼CSVパーサを作る[その1][その2][その3]
宿・ホテル予約ならじゃらんnet

| | トラックバック (0)

Java:CSVパーサを作る(その2) - RFC4180対応 前編

←CSVパーサを作る(その1) - 簡易パーサ

CSVの仕様について調べてみると「K3フォーマット」と「RFC4180日本語訳)」が見つかります。K3フォーマットとCSVの違いについては「桐ver.8活用ガイド」というドキュメントの「読み込み K3フォーマット」と「読み込み CSV」という項目で記述されています。
ここでは前回の記事で作った簡易CSVパーサを発展させて,今回と次回の2回に分けてRFC4180で規定されたCSVが読み込めるCSVパーサを実装してみたいと思います。

【RFC4180で規定されたCSVの仕様】

  1. レコードの区切りは改行である。改行コードはCRLFだが他の値も考慮すべきである。
  2. ファイルの末尾に改行はあってもなくても良い。
  3. ファイルの先頭行にヘッダ行があっても良い。ヘッダ行の有無はMIMEタイプ「text/csv」のパラメータ「header」で示す。header パラメータの値は「present」(存在する),または「absent」(存在しない)のどちらかである。「header」パラメータを使わない場合はヘッダの有無を自ら決める。
  4. 各行にはカンマ「,」で区切ったフィールド列が必ずある(つまり空行はない)。フィールド数はすべての行で同じ。フィールド中の空白は無視しない。行末のカンマは行の終わりを表さない(その後ろにNULLフィールドがもう1つある)。
  5. 各フィールドはダブルクォーテーション(二重引用符)「"」で囲んでも良いし囲まなくても良い。
  6. 改行,ダブルクォーテーション,カンマを含むフィールドはダブルクォーテーションで囲む。
  7. フィールドデータにダブルクォーテーションを含む場合,ダブルクォーテーションを2つ続けることでエスケープする。
  8. 文字コードは一般にUS-ASCIIを使う,他の文字コードの使用を明示するにはMIMEタイプ「text/csv」のパラメータ「charset」で指定する。
【参考:K3フォーマットとRFC4180の違い】
  1. フィールドをダブルクォーテーションで,
    • RFC4180:「囲んでも囲まなくてもよい」(数値型を囲んでもよい)
    • K3フォーマット:「文字列型データは囲む,数値型データは囲まない」(数値型,文字型以外は想定なし)
  2. 行の先頭が「#」だった場合,
    • RFC4180:規定なし。よって通常のレコードとして解釈する。
    • K3フォーマット:その行をレコードとして解釈せずに以下のように解釈する:
      • 行頭が「#」の場合:制御コマンド記述行
      • 行頭が「##」の場合:コメント行
      • 行頭が「###」の場合:データ終了。この行以降のデータは取り込まない。

【仕様の考察】
CSVパーサのように外部からデータを取り込んでこれを処理するプログラムの場合,受け取ったデータがRFC4180の仕様に沿っていた場合に正常に処理できることはもちろん,仕様とは異なるデータを受け取った場合にどのように処理するかを決める必要があります。CSVパーサの実装を検討する上で考慮すべき事項として以下のようなものが考えられます。

  1. 画像データやPDFなど,CSVではないデータを受け取った場合にどうするか。
  2. RFC4180の仕様から若干外れたCSVデータ,たとえば次のようなデータを受け取った場合にこれをどのように処理するか:
    • CRLF以外の改行コードを受け取ったらどうするか。複数の種類の改行コードが混在していたらどうするか。
    • 仕様では「空行はない」とされているが,空行があった場合はどうするか。
    • 仕様では「フィールド数はすべての行で同じ」となっているが,フィールド数にばらつきがあった場合はどうするか。
    • 仕様で「改行,ダブルクォーテーション,カンマを含むフィールドはダブルクォーテーションで囲む」とあるが,囲まれていない場合はどうするか。フィールド中の改行とカンマについてはダブルクォーテーションなしでは判定は不可能だが,ダブルクォーテーションについてはエスケープされているかどうかで判定できる可能性がある。
    • ダブルクォーテーションで囲まれていないフィールドの途中にエスケープされていないダブルクォーテーションが出現した場合はどのように解釈するか。
    • 行頭が「#」の場合どうするか。RFC4180では行頭が「#」の場合については決められていないが,K3フォーマットではこの行はレコードとして解釈せずにコメント行または制御行として扱う。
    • 文字コードはどうするか。RFC4180ではUS-ASCIIがデフォルトだが,これだと日本語が扱えないのでデフォルトを変えるべきではないか。
これらを考慮する場合に重要なのが「リーズナブル」という考え方です。これは具体的には以下のような考え方です。
  1. なるべく少ないコードでより多くの要求を満たす実装方法を選択する。
  2. 重要ではない小さな要求のために大量のコードが必要になる実装方法は避ける。
  3. 何かに対応するために大量のコードが必要になったら「この実装方法には何か根本的な問題があるのではないか?」と考える。
  4. 実現不可能なもの,実現が困難なものはどこかで諦める。その場合はユーザが納得できる代替案を探す。
以上を踏まえて,CSVパーサを実現するためにRFC4180に対して以下のように追加修正を行います。
  1. CSVパーサの実装において,CSV以外のデータを受け取った場合の処理については特に考慮しません。この問題は,CSVパーサが呼ばれる以前に,たとえばファイル名の拡張子でエラー判定するなどして解決するものとします。
  2. フィールドに含まれる空白のうち,フィールドの先頭と末尾の空白は無視(削除)できることにします。これは特に数値を数値データとして取り込む場合に必要です。
  3. MIMEタイプのパラメータは読み込んだCSVデータの中で判断できないため,パーサでは対応しません。
  4. 行頭に「#」がある行はコメント行とします。ヘッダ行はこのコメント行で実現するものとします。対応すべき制御コマンドもないので「#」1つでもコメント行とします。
  5. フィールド内にエスケープされたダブルクォーテーションが含まれる場合の動作は次のようにします。
    • エスケープされたダブルクォーテーションの前後にデータがある場合は,フィールド全体を囲むダブルクォーテーションはあってもなくても良い。
    • フィールド内のデータがエスケープされたダブルクォーテーションだけの場合,全体を囲むダブルクォーテーションが必須。(つまり連続する4つのダブルクォーテーションが必要。)
    • 逆にフィールド内のデータがダブルクォーテーションの1ペアだけだった場合,パーサはこれをダブルクォーテーションで囲んだNULLフィールドとして扱う。
  6. レコードの区切りは改行(またはファイル末尾)ですが,ダブルクォーテーションで囲んだフィールドに改行が入っていることがあるため,改行がレコードの区切りとは限りません。
  7. フィールド数は固定であることを期待しません。また空行もありうるものとします。空行はスキップします。フィールドが多いときは余りは無視します。フィールドが足りないときはNULLフィールドで補うものとします。
  8. ダブルクォーテーションを2つ続けることによりダブルクォーテーションをエスケープする仕様のため,ダブルクォーテーションは1フィールド及び1レコード内で必ず偶数になります。
  9. 文字コードはUS-ASCIIではなく「Windows-31J/MS932」とします。これはExcelで開けるCSVの文字コードに合わせています。
【実装の考察】
処理手順としては,入力したテキストデータを先頭から1レコードづつ切り出し,このレコードをフィールドに分割する,これを入力テキストの最後まで繰り返す,という流れになりますが,処理手順を検討した結果,以下の流れで処理を行うことにします。
  1. レコードの確定
    入力テキストを1行づつ取り出し,ダブルクォーテーションのペアをトレースしてレコードの末尾を確定し,1レコードを確定する。
    • 取り出した1行分の入力テキストに含まれるダブルクォーテーションの数がゼロまたは偶数ならば,その1行を1レコードとして確定する。
    • 取り出した1行分の入力テキストに含まれるダブルクォーテーションの数が奇数ならば,ダブルクォーテーションで囲まれたフィールドの途中と見なして次の1行分の入力テキストを取り出し前の行と連結する。これをダブルクォーテーションが偶数になるまで(または入力テキスト末尾になるまで)繰り返す。
  2. フィールドの分割
    レコードに対して,最初にレコード全体をカンマで分割した後で,ダブルクォーテーションをヒントに
    • 文字列フィールドのカンマによる分割の再結合
    • 文字列フィールドを囲むダブルクォーテーションの削除
    • ダブルクォーテーションが2つ連続した場合のエスケープ処理
    を行う。
  3. 1と2を入力テキスト末尾まで繰り返す。
以上のような処理になりますが,ソースコードを簡潔にするために上記「レコードの確定」と「レコードのフィールドへの分割」を別メソッドにして実装することにします。

このメソッドを実装する前に,前回の記事で作った「簡易CSVパーサ」を,上記メソッドを呼び出すように書き換えたコードを先に作ってしまいます。

【簡易パーサの修正】
「レコードの確定」「レコードのフィールドへの分割」を行うメソッドを以下のように規定します。
/**
 * BufferedReaderから1レコード分のテキストを取り出す。
 * @param reader 行データを取り出すBufferedReader。
 * @return 1レコード分のテキスト。
 * @throws IOException 入出力エラー
 */
private String buildRecord (BufferedReader reader);

/**
 * レコードデータsrcを分割してフィールドの配列にする。
 * @param src レコードデータ。
 * @param dest レコードデータからフィールドの配列を取り出してリストに加える。
 */
private void splitRecord (String src, LinkedList dest);

このメソッドを使って前回の記事の「簡易CSVパーサプログラム」を書き換えると次のようになります。
注:行頭が「#」の行はコメント行と見なしてスキップするコードも追加しています。
//------------------------------------------------------------------
/**
 * CSVファイルの読み込み。
 * @param stream 入力ストリーム。FileInputStream,ByteArrayInputStreamなど。
 */
public void read (
  InputStream stream)
{
  LinkedList     columns = new LinkedList();
  InputStreamReader reader = null;
  BufferedReader   buff = null;
  try {
    reader = new InputStreamReader(stream, "MS932");
    buff = new BufferedReader(reader);
    String record;
    int   lineNum = 0;

    while ((record = buildRecord(buff)) != null) {
      lineNum++;
      if (record.length() <= 0)
        continue;
      if (record.startsWith("#"))
        continue;
      splitRecord(record, columns);
      if (0 < columns.size()) {
        readColumns(columns, lineNum);
        columns.clear();
      }
    }
  }
  catch (Exception ex) {
    ex.printStackTrace();
  }
  finally {
    columns.clear();
    try {
      if (buff != null) {
        buff.close();
      }
      if (reader != null) {
        reader.close();
      }
      stream.close();
    }
    catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

//------------------------------------------------------------------
/**
 * 読み込んだ1レコード分のデータをDBに取り込む。
 * 実際の処理は派生クラスで実装。
 * @param columns 1レコードをフィールドに分割した文字列リスト。
 * @param lineNum CSVの行番号(エラーが発生したときの行番号記録用)。
 */
abstract protected void readColumns (LinkedList columns, int lineNum);


今回はここまで。
次回は,「レコードの確定」と「レコードのフィールドへの分割」を行う「buildRecord」と「splitRecord」両メソッドの実装を行い,CSVパーサを完成させます。

CSVパーサを作る(その3) - RFC4180対応 後編→

■関連情報
CSVの仕様:RFC4180日本語訳)(Wikipedia
桐ver.8活用ガイド(K3フォーマットとCSVの違いについての解説あり)
Microsoftコードページ932(Wikipedia
WebObjects:CSVレスポンスの実装

■関連書籍をAmazonで検索:[Java]
Effective Java 第2版 (The Java Series)



にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ プログラム・プログラマへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
▼CSVパーサを作る[その1][その2][その3]
SOHO家具オンラインショップ Garageデル株式会社コンピューターミュージックモニター

| | トラックバック (0)

Java:CSVパーサを作る(その1) - 簡易パーサ

データベースExcelなどでデータをインポート・エクスポートする際に使用されるファイルフォーマットとしてCSVがあります。
CSVを単にカンマでフィールドを区切り,改行でレコードを区切ったテキストデータと見るならば,JavaでCSVファイルをインポートしてデータベースに読み込ませるなどの処理を行うCSVパーサを作るには,BufferedReaderクラスのreadLineメソッドでレコードを取り出し,Stringクラスのsplitメソッドでフィールドに分割すれば,それでCSVパーサができあがります。
この場合の具体的なプログラムを以下に例示します。

■処理の概要
このCSVパーサでは,以下の処理をファイルの最後まで繰り返し実行します。

  1. 読み込んだCSVデータから1行を1レコードとして取り出します。
  2. 1レコードをフィールドに分割してStringクラスインスタンスのリストを作ります。
  3. 1レコード分のフィールドのリストをレコード処理メソッド(「readColumns」)に渡します。
CSVパーサクラス及びreadColumnsメソッドはabstractとし,派生クラスでreadColumnsメソッドの処理を実装するものとします。派生クラスのreadColumnsメソッドでは,CSVパーサで生成した1レコード分のフィールドのリストをデータベースへ登録する処理などを実装します。
CSVファイルの文字コードは「Windows-31J/MS932」とします。これはExcelで開けるCSVの文字コードに合わせています。

簡易CSVパーサプログラム

//------------------------------------------------------------------
/**
 * CSVファイルの読み込み。
 * @param stream 入力ストリーム。FileInputStream,ByteArrayInputStreamなど。
 */
public void read (
  InputStream stream)
{
  InputStreamReader reader = null;
  BufferedReader   buff = null;
  try {
    reader = new InputStreamReader(stream, "MS932");
    buff = new BufferedReader(reader);
    String  record;
    String[] columns;
    int   lineNum = 0;

    while ((record = buff.readLine()) != null) {
      lineNum++;
      if (record.length() <= 0)
        continue;
      columns = record.split(",");
      if (0 < columns.length) {
        readColumns(columns, lineNum);
      }
    }
  }
  catch (Exception ex) {
    ex.printStackTrace();
  }
  finally {
    try {
      if (buff != null) {
        buff.close();
      }
      if (reader != null) {
        reader.close();
      }
      stream.close();
    }
    catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

//------------------------------------------------------------------
/**
 * 読み込んだ1レコード分のデータをDBに取り込む。
 * 実際の処理は派生クラスで実装。
 * @param columns 1レコードをフィールドに分割した文字列リスト。
 * @param lineNum CSVの行番号(エラーが発生したときの行番号記録用)。
 */
abstract protected void readColumns (String[] columns, int lineNum);


次回は,この簡易パーサを拡張して「RFC4180」に対応したCSVパーサへの拡張を試みます。

CSVパーサを作る(その2) - RFC4180対応 前編→

■関連情報
CSVの仕様:RFC4180日本語訳)(Wikipedia
Microsoftコードページ932(Wikipedia
WebObjects:CSVレスポンスの実装

■関連書籍をAmazonで検索:[Java]
Java謎+落とし穴徹底解明



にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ プログラム・プログラマへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
▼CSVパーサを作る[その1][その2][その3]
ブックオフオンラインSony Style(ソニースタイル)ioPLAZA【アイ・オー・データ直販サイト】

| | トラックバック (0)

WebObjects:CSVレスポンスの実装

Webアプリケーションにおいて,データベースの内容をCSVデータにしてユーザにダウンロードさせたい場合があるかと思います。ここではWebObjectsの環境で作成するWebアプリでの実装例として,ボタンクリックに対するWebブラウザへのレスポンスをCSVデータとすることで,WebブラウザでCSVデータをダウンロードさせる実装例を示します。

【処理の概要】
WebObjectsのアクションメソッドの戻り値としてコンポーネントクラスのインスタンスを返すと,WebObjectsがそのクラスのappendToResponseメソッドを呼び出してWebブラウザへのレスポンスを作成します。この仕組みを使ってCSVデータを返すために,CSVデータを返すためのコンポーネントクラスを作り,メンバ変数にCSVデータセットしてappendToResponseメソッドが呼ばれればCSVレスポンスが作られるようにします。

【実装手順】
1)コンポーネントの作成
CSVデータを返すコンポーネントを作成します。
これは通常のHTMLを返すコンポーネントの新規作成と同じで,Xcodeの「新規ファイル」ダイアログから「WebObjects」→「Component」を選んで「次へ」ボタンで進み,ターゲットを「Application Server」にしてファイル名にコンポーネント名を入力して完了ボタンで作成を実行します。
ここでは仮に,コンポーネント名を「CSVExport」と指定することにします。

2)CSVデータを保持するメンバ変数と初期化メソッドの作成
CSVExport.javaにて,CSVデータとファイル名を保持するメンバ変数と,これに値を設定する初期化メソッドを実装します。

private String mFileName;
private String mCSVData;

public void init (
  String fileName,
  String csvData)
{
  mFileName = fileName;
  mCSVData = csvData;
}

3)appendToResponseのオーバーライド
CSVExport.javaにて「appendToResponse」メソッドを定義すると,Application.javaやSession.javaで定義したappendToResponseメソッドがオーバーライドされます。CSVデータを返す場合のappendToResponseメソッドは以下のようになります。このメソッドでは,出力するCSVファイルをExcelで開くことができるように,文字コードを「Windows-31J/MS932」に指定しています。
public void appendToResponse (
  WOResponse res,
  WOContext cont)
{
  res.setContextEncoding("MS932");
  super.appendToResponse(res, cont);
  if (mFileName != null && mCSVData != null) {
    res.setHeader("public", "Cache-Control");
    res.setHeader("public", "Pragma");
    res.setHeader("text/csv;charset=Windows-31J", "Content-Type");
    res.setHeader("attachment;filename=\""+mFileName+".csv\"", "Content-Disposition");
    res.setHeader(new Long(mCSVData.length()).toString(), "Content-Length");
    res.setContent(mCSVData);
    res.setStatus(res.HTTP_STATUS_OK);
  }
}

上記コードのうち,
res.setHeader("public", "Cache-Control");
res.setHeader("public", "Pragma");
IEのダウンロード問題に対処したものです。
以上でCSVExportコンポーネントの実装は完了です。

4)アクションメソッドからの呼び出し
Webアプリ上でボタンを押すと,CSVExportコンポーネントによりCSVファイルがダウンロードされるようにします。
WebアプリのCSVダウンロードボタンを配置したページのコンポーネントクラスにおいて,ボタンを押したときに呼ばれるアクションメソッドを用意して,その戻り値としてCSVExportクラスインスタンスを返すようにします。アクションメソッドは以下のようになります。
public CSVExport exportAction ()
{
  CSVExport nextPage = null;
  String fileName = buildFileName(); // ファイル名生成.
  String csvData = buildCSVData();  // CSVデータ生成.
  if (csvData != null && 0 < csvData.length()) {
    nextPage = (CSVExport)pageWithName("CSVExport");
    nextPage.init(fileName, csvData);
  }
  return nextPage;
}
このアクションメソッドをWebページのWOSubmitButtonなどにバインドすることにより,ユーザのボタン押下でCSVデータをWebブラウザからダウンロードすることができます。

5)CSVデータ生成メソッドの実装の概要
CSVデータの作成についてはJavaの「StringBuffer」クラスで組み立てるのが一般的かと思います。
CSVのデータフォーマットの仕様についてはRFC4180日本語訳)を参照してください。
この実装方法で巨大なCSVファイルを扱う場合,最後にtoStringメソッドでStringインスタンスを生成するときに,文字列データに必要なメモリサイズが倍になってしまうので,メモリ不足に注意しましょう。WebObjectsのメモリ不足対策はこちら
private String buildCSVData ()
{
  StringBuffer buff = new StringBuffer();
  buff.append("カラム1-1");
  buff.append(",");    // カンマ挿入.
  buff.append("\"");    // ダブルクォーテーション(二重引用符)挿入.
  buff.append("カラム1-2");
  buff.append("\"");
  buff.append("\r\n");   // 改行(CRLF)挿入.
  buff.append("カラム2-1");
  buff.append(",");
  buff.append("\"");
  buff.append("カラム2-2");
  buff.append("\"");
  buff.append("\r\n");

  return buff.toString();
}

※CSVの「数値/数値」をExcelで日付にしないために
ここで実装したしたCSVレスポンスのデータをユーザがダウンロードしてExcelで開いて閲覧・編集する場合,フィールドデータの形式が「数値/数値」または「数値-数値」で,数値が日付として解釈できる範囲内の場合,Excelはこれを勝手に日付データとして取り込んでしまいます。
これに対する対処法として,CSVレスポンスデータを組み立てるときにフィールドデータの先頭に半角スペースを入れる,というのが最も手っ取り早い対処法かと思います。先頭に半角スペースを入れた「 数値/数値」のデータをExcelに読み込ませると,Excelはこのデータの先頭の半角スペースを削除して日付ではなく文字列としてデータを取り込みます。
参考:Excel で文字列または数値が意図しない表示形式に変換される(Microsoftサポートオンライン)

■関連情報
CSVの仕様:RFC4180日本語訳
Java:Shift_JISのエイリアスの変更について
Microsoftサポートオンライン:IEのダウンロードの問題
Java:CSVパーサを作る[その1][その2][その3]
WebObjects:[日本語技術マニュアル][開発と運用について][API Reference(javadoc)][ADC Tools][サポート][製品紹介]

■関連書籍をAmazonで検索:[WebObjects 和書 洋書]
コーディングの掟 現場でよく見る不可解なJavaコードを一掃せよ!

■Apple Store:Windows/Solaris版[WebObjects 5.2]

にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ プログラム・プログラマへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
Mac ソフトのことなら act2.comMac ソフトのことなら act2.comMac ソフトのことなら act2.com

| | トラックバック (0)

Java:MySQLに金額を格納する

データベースで金額を格納するのに適したデータ型として,MySQLでは「DECIMAL」型があります。FLOATやDOUBLEのような浮動小数点数の型では,小数点以下の値は近似値となりますが,DECIMALでは小数点以下の値も含めて正確に保存するために,MySQL5.0.3以前では数値を文字列に,5.0.3以降ではバイナリデータに変換して保存しています。「DEC」「NUMERIC」「FIXED」はDECIMALの別名です。
DECIMALフィールドの宣言は「DECIMAL(M,D)」です。Mはフィールド全体の桁数,DはMのうち小数点以下の桁数(スケール)を表します。DECIMALの最大桁数(DECIMAL(M,D)のMの値)は,MySQL5.0.3より前は1~254桁まで,5.0.6からは1~65桁までです。また,小数点以下の桁数(DECIMAL(M,D)のDの値)は0~30桁までです。(出典1)(出典2

MySQLにJavaプログラムからJDBCアダプタ(MySQL Connector/J)経由でアクセスする場合,データベースのDECIMAL型のフィールドは,JDBCによりJavaの「BigDecimalクラスインスタンスに置き換えられます。
BigDecimalクラスでは,BigDecimalどうしでの四則演算のほか,小数点以下の桁数操作や丸め処理を行うことができます。BigDecimalで指定できる丸め処理は以下になります。

パラメータ名称説明
ROUND_UP切り上げ計算結果に端数があれば,端数を切り捨てて最小桁を+1(負の値のときは-1)します。
ROUND_DOWN切り捨て計算結果に端数があれば,端数を単に切り捨てます。
ROUND_CEILING正の無限大計算結果が正の値のときは切り上げ,負の値のときは切り捨てを行います。その結果,誤差は必ず正の方向に現れます。この指定は負の方向に誤差が出てはならない場合に指定します。
ROUND_FLOOR負の無限大計算結果が正の値のときは切り捨て,負の値のときは切り上げを行います。その結果,誤差は必ず負の方向に現れます。この指定は正の方向に誤差が出てはならない場合に指定します。
ROUND_HALF_UP四捨五入計算結果の端数が0.5より小さいときは切り捨て,0.5より大きいときは切り上げ,0.5のときは切り上げを行います。
注:連続して計算を行う場合には,四捨五入のみを指定して計算すると,計算結果の累積誤差が大きくなる欠点があります。そのため五捨六入と交互またはランダムに切り替えて誤差を抑えるなどの工夫が必要です。
ROUND_HALF_DOWN五捨六入計算結果の端数が0.5より小さいときは切り捨て,0.5より大きいときは切り上げ,0.5のときは切り捨てを行います。
ROUND_HALF_EVEN最近接偶数への丸め計算結果の端数が0.5より小さいときは切り捨て,0.5より大きいときは切り上げ,0.5のときは結果が偶数になるように切り上げまたは切り捨てを行います。
具体的には「0.5→0(切捨),1.5→2(切上),2.5→2(切捨),3.5→4(切上),4.5→4(切捨),5.5→6(切上)…」といった値になります。
連続して計算を行う場合に,累積誤差が出にくい丸め処理です。
ROUND_UNNECESSARY丸め無し計算結果で端数が生じる場合はArithmeticException例外をスローします。

●BigDecimalでの数値の比較
BigDecimal同士で数値を比較する場合,equalsメソッドでは数値としては同じでもスケールが違う値,たとえば「1」「1.0」「1.00」は別の値として判定されてしまいます。
これらを同じ値として比較判定する場合はcompareToメソッドを使います。

●BigDecimalでのゼロ値の判定
BigDecimalに設定された値がゼロかどうかを判定する場合,予めゼロ値を設定したBigDecimalとcompareToメソッドで比較判定する方法のほかに,BigDecimalのsignumメソッドが0を返すかどうかでも判定できます。signumメソッドは値が負の場合は-1,ゼロの場合は0,正の場合は1を返します。
予めゼロ値が設定されているBigDecimalを使いたい場合は,Java1.5以降で「BigDecimal.ZERO」が定義されています。

■関連情報
MySQL:5.0.3以降のDECIMAL データ タイプの変更(MySQL5.1:22章:精密計算
MySQLの数値型:[4.1][5.1]
MySQL Connector/J:JDBCで値を受け渡すときのJavaの型:[5.0][5.1]
端数処理:(Wikipedia

■関連書籍をAmazonで検索:[MySQL][Java][JDBC]
データベース・リファクタリング



にほんブログ村 IT技術ブログへ にほんブログ村 IT技術ブログ プログラム・プログラマへ 人気ブログランキングへ ←この記事が役に立ったという方はクリックお願いします。
ioPLAZA【アイ・オー・データ直販サイト】デポのオフィスチェアー特集(大)

| | トラックバック (0)

«JavaMail:メール送信でOP25B対応