読者です 読者をやめる 読者になる 読者になる

COPY コマンドで取り込む UTF8 のテキストファイルは BOM 無しなんですね

PostgreSQL で UTF8 で作ったデータベースに COPY コマンドで CSV ファイルからテーブルにデータを取り込もうとしたら、エラー発生。

ERROR: invalid input syntax for type 1列目のカラムの型: "1行1列目の値"
SQLステート:22P02
コンテキスト:COPY テーブル名, line 1, column 1列目のカラム名: "1行1列目の値"

COPY コマンドのマニュアルを見ても何が悪いのか分からず、1時間経過―。

あ…COPY コマンドで取り込むテキストファイルは UTF8 だと BOM 無しなんですね…。あ…、はい…、BOM 無しで保存しましたら難なく取り込めました…。はい…、本当にありがとうございました…。

T4 Template を触ってみた

XMLファイルを Linq to XML で読み込んで、T4 Template を使ってコードを生成してみたので、メモメモ。

まずは適当にコンソールアプリプロジェクトをつくって、入力用のXMLファイル(今回は People.xml)をプロジェクトに追加します。

<?xml version="1.0" encoding="utf-8" ?>
<People>
  <Person name="Isabella" />
  <Person name="Sophia" />
  <Person name="Emma" />
  <Person name="Olivia" />
  <Person name="Ava" />
  <Person name="Emily" />
  <Person name="Abigail" />
  <Person name="Madison" />
  <Person name="Chloe" />
  <Person name="Mia" />
</People>

続いて T4 Template ファイルをプロジェクトに追加します。T4 Template ファイルの追加は拡張子 tt の テキストファイル(今回は Template.tt)を追加すればOKです。

<#@ template language="C#" hostspecific="true" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Xml.dll" #>
<#@ assembly name="System.Xml.Linq.dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
using System;

namespace T4TemplateSample
{
	public class Program
	{
		public static void Main()
		{
<#
var xelm = XElement.Load(this.Host.ResolvePath("People.xml"));
var query = from t in xelm.Elements("Person") select t;
foreach (var t in query)
{
	var name = t.Attribute("name").Value;
#>
			Console.WriteLine("Hello <#= name #>");
<# } #>
		}
	}
}

今回は Linq to XML を使うので、必要な System.Core.dll、System.Xml.dll、System.Xml.Linq.dll を assembly ディレクティブで読み込み、import ディレクティブで System.Linq、System.Xml.Linq 名前空間をインポートします。
T4 Template ファイルを保存すると、それと同名の CS ファイルにテンプレートの実行結果が出力されます(今回は Template.cs)。

using System;

namespace T4TemplateSample
{
	public class Program
	{
		public static void Main()
		{
			Console.WriteLine("Hello Isabella");
			Console.WriteLine("Hello Sophia");
			Console.WriteLine("Hello Emma");
			Console.WriteLine("Hello Olivia");
			Console.WriteLine("Hello Ava");
			Console.WriteLine("Hello Emily");
			Console.WriteLine("Hello Abigail");
			Console.WriteLine("Hello Madison");
			Console.WriteLine("Hello Chloe");
			Console.WriteLine("Hello Mia");
		}
	}
}

以上、すごく普通の内容になりましたけど、仕事ではこれでモデルとかをバリバリ自動生成していきたいなー。

モナドパターン

モナドはメタファーではない: http://eed3si9n.com/ja/monads-are-not-metaphors

「ある物から始めて、その値を使って新たな物を計算するのだ。」

値を格納する型Aを定義して、その型Aに格納した値を使って何かを計算する関数を引数に受けて、計算結果を格納した型Aのインスタンスを返すメソッドを作れば、それはモナドパターンってことでOK?

EPPlus を触ってみた

社内で営業の人が「―Excel を操作するパッケージがあるらしくて、それを使えば―」みたいな電話をしていて、「ああ POI っすね。.NET だと NPOI かな。」と思いながら、その日は自分の仕事をこなしていました。

で、気が向いたので軽く触ってみることにしました。

とりあえず、NPOI を触ってみたんですけど作成できるのは xls ファイルだけ。本家の NPOI は org.apache.poi.xssf パッケージで xlsx ファイルが作れるみたいですけど、NPOI は対応していないみたいですね。xlsx ファイルは ExcelPackage とか EPPlus で作成できるみたいです。で、今回は ExcelPackage より開発が盛ん(?)な EPPlus を触ってみることにしました。

EPPlus: http://epplus.codeplex.com/

バイナリと一緒にサンプルコードがダウンロードできるので、それを参考に大体の機能はすぐ作れそうです。

作るものが何も浮かばなかったので、okazuki さんの NPOI のサンプルコード(http://d.hatena.ne.jp/okazuki/20091128/1259405232)を EPPlus で書き直してみました。

using OfficeOpenXml;
using OfficeOpenXml.Style;
using OfficeOpenXml.Style.XmlAccess;
using System;
using System.Drawing;
using System.Linq;
using System.IO;

namespace EPPlusEdu
{
    class Program
    {
        static void Main(string[] args)
        {
            // output.xlsx の FileInfo を作成する。
            FileInfo file = new FileInfo("output.xlsx");
            if (file.Exists)
            {
                file.Delete();
                file = new FileInfo("output.xlsx");
            }
                
            // Excel 2007 XLSX ファイルパッケージを作成。
            using (var package = new ExcelPackage(file))
            {
                // シートを作成
                var sheet = package.Workbook.Worksheets.Add("何とかレポート");

                // ヘッダーにあたる行を作成
                CreateHeaderRow(sheet);

                // とりあえず10行くらいデータ作成
                foreach (var index in Enumerable.Range(1, 10))
                    CreateRow(sheet, index);

                // 2列目と3列目は、そのままだと幅が足りないので広げる
                sheet.Column(2).Width = 12;
                sheet.Column(3).Width = 16;

                // output.xlsxに保存
                package.Save();
            }
        }

        // ヘッダー行を作成する
        private static void CreateHeaderRow(ExcelWorksheet sheet)
        {
            // 1列目はIDの列
            sheet.Cells[1, 1].Value = "ID";

            // 2列目は名前の列
            sheet.Cells[1, 2].Value = "名前";

            // 3列目は誕生日の列
            sheet.Cells[1, 3].Value = "誕生日";

            // ヘッダーセルにスタイルを適用する
            var headerCells = sheet.Cells[1, 1, 1, 3];

            // 4方に罫線
            headerCells.Style.Border.Top.Style = ExcelBorderStyle.Thin;
            headerCells.Style.Border.Left.Style = ExcelBorderStyle.Thin;
            headerCells.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
            headerCells.Style.Border.Right.Style = ExcelBorderStyle.Thin;
            // 薄いグリーンの背景色で塗りつぶす
            headerCells.Style.Fill.PatternType = ExcelFillStyle.Solid;
            headerCells.Style.Fill.BackgroundColor.SetColor(Color.LightGreen);
            // テキストはセンタリング
            headerCells.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
            // 太字
            headerCells.Style.Font.Bold = true;
        }

        private static Random r = new Random();

        // index行目のデータを作る
        private static void CreateRow(ExcelWorksheet sheet, int index)
        {
            // id列を作る
            sheet.Cells[index + 1, 1].Value = index;
            // 名前も適当に入れて
            sheet.Cells[index + 1, 2].Value = "田中 太郎" + index;
            // 誕生日も適当に
            sheet.Cells[index + 1, 3].Value = DateTime.Now.AddYears(r.Next(10));
            // 日付用yyyy年mm月dd日のフォーマットで誕生日は表示するようにする
            sheet.Cells[index + 1, 3].Style.Numberformat.Format = "yyyy年mm月dd日";
            
            // 全ての列に4方に罫線のあるスタイルを作って適用する
            var cells = sheet.Cells[index + 1, 1, index + 1, 3];
            cells.Style.Border.Top.Style = ExcelBorderStyle.Thin;
            cells.Style.Border.Left.Style = ExcelBorderStyle.Thin;
            cells.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
            cells.Style.Border.Right.Style = ExcelBorderStyle.Thin;
        }
    }
}

出来上がったファイルを Excel で開いたのがこれ。

目新しいことは何もないんですけど、とりあえずこれで今後 Excel 出力の話がでても自信を持って答えられるようになったので、個人的には満足かな。

new PosExplorer() で TypeInitializationException

今後の仕事で使うかなーと思って POS for .NET をサンプルコードを見ながらコーディングしてたら PosExplorer のインスタンス生成で TypeInitializationException の例外が。

PosExplorer posExplorer = new PosExplorer();

えーここでー?使い方間違ってる?これだけだよねー?

InnerException を見てみると、

「このメソッドが明示的に使用する CAS ポリシーは、.NET Framework では使用されなくなっています。互換性のために CAS ポリシーを有効にするには、NetFx40_LegacySecurityPolicy 構成スイッチを使用してください。詳細については、http://go.microsoft.com/fwlink/?LinkID=155570 を参照してください。」

とのこと。ほほう。

.NET 4 から CAS は廃止されたようで、POS for .NET は CAS が有効になっていないとダメみたいです。
で、リンク先の記述のようにアプリケーション構成ファイルに次の設定を追加して無事解消。

<configuration>
   <runtime>
      <NetFx40_LegacySecurityPolicy enabled="true"/>
   </runtime>
</configuration>