IronPythonを使ってみる

CSVファイルの正しい取り扱いについて 其の弐

1. MyDataGridViewへの追加

以前作成したMyDataTableクラスを修正し、CSVファイルを正しく扱えるようにしました。 先ほどと同じCSVファイルを読み込ませると次のような表示になります。

CSVファイルではデータに改行文字を含める事もできますが、DataGridViewはデータの中の改行コードを無視されてしまいます。

$alt

2. スクリプトについて

スクリプト本体は次のようになりました。

a09_filedialog.ipyファイル

# coding=Shift_JIS
# @author: YasuhiroABE <yasu@yasundial.org>
#
from a09_lib import *
form = MyForm("Hello World!")

## csv filename
filedialog = MyOpenFileDialog()
filename = filedialog.open()

## setup datatable
dt = MyDataTable()
dt.loadCSV(filename)

## setup layout
datagridview = MyDataGridView()
datagridview.DataSource = dt

form.Controls.Add(datagridview)
form.run()

MyDataTableクラスの修正個所は次の通りです。

変更前:loadCSVメソッド処理の抜粋

...
  def loadCSV(self, filename):
    reader = StreamReader(filename)
    cols = []
    for c in reader.ReadLine().split(","):
      self.Columns.Add(c, System.Type.GetType("System.String"))
      cols.append(c)
      pass
    l = reader.ReadLine()
    while(l != None):
      row = self.NewRow()
      counter = 0
      for item in l.split(","):
        row[cols[counter]] = item
        counter += 1
      self.Rows.Add(row)
      l = reader.ReadLine()
      pass
    reader.Close()
    pass
  pass
...

変更後:loadCSVメソッド処理の抜粋

...
  def loadCSV(self, filename):
    parser = MyCSV(filename)
    cols = []
    rownum = 0
    for r in parser.eachRow():
      tablerow = None
      itemnum = 0
      for item in r.eachItem():
        if rownum == 0:
          self.Columns.Add(item, 
                  System.Type.GetType("System.String"))
          cols.append(item)
	else:
          if tablerow is None: tablerow = self.NewRow()
          tablerow[cols[itemnum]] = item
	  pass
        itemnum += 1
	pass
      rownum += 1
      if tablerow is not None:
        self.Rows.Add(tablerow)
      pass
    pass
  pass
...

元々は1行目をヘッダと認識して別に処理を行なっていましたが、作成したMyCSVクラスは効率化のためにyield文を使っているため1行だけの処理を抜き出すのが難しくなっています。

特別なメソッドを準備しようかとも思いましたが、とりあえず呼び出し側でカラム、行の数を数えるカウンターを準備する事で回避しています。

3. MyCSVクラス

今回作成したmycsv.pyライブラリファイルの内容は次の通りです。

# coding=Shift_JIS
# @author: YasuhiroABE <yasu@yasundial.org>
#
import clr
clr.AddReferenceByPartialName("Microsoft.VisualBasic")
import Microsoft
from Microsoft.VisualBasic.FileIO import TextFieldParser
from Microsoft.VisualBasic.FileIO import FieldType
import System

class MyRow:
  def __init__(self, row):
    self.row = row
    pass

  def eachItem(self):
    for item in self.row:
      yield item
      pass
    pass
  def getItem(self):
    return self.row
  pass

class MyCSV:
  def __init__(self, filename):
    self.parser = TextFieldParser(filename,
		           System.Text.Encoding.GetEncoding("Shift_JIS"))
    self.parser.TextFieldType = FieldType.Delimited
    self.setDelimiters(',')
    pass

  def setDelimiters(self, delimiter):
    self.parser.SetDelimiters(delimiter)
    pass

  def eachRow(self):
    array = self.parser.ReadFields()
    while(array):
      row = MyRow(array)
      yield row
      array = self.parser.ReadFields()
      pass
    pass
  pass

行のデータにアクセスする時にはyieldするのが良いとは限らないので、getItem()メソッドを準備しています。

このクラスはCSVファイルの各列のデータは全て文字型だと仮定しています。 これはCSVファイルのエンコーディングを保持する限りは正しい仮定ですが、IronPythonで日本語を正しく扱うためには少し設定が必要です。

4. 日本語を含む文字列の取り扱いについて

coding行と揃っていれば、Shift_JISでもUTF-8でもスクリプトに直接記述した文字をコントロールに表示させる事は可能です。 MyCSVクラスでは.NETの機能を使って、CSVファイルがShift_JISエンコーディングの前提で読み取っています。

単純にファイルの内容を1行づつ表示させるような処理であれば、decodeを使うこともできます。

CSVファイル: a10_readfile.csv

name,age,address
"yasu,裕",20,AizuWakamatsu
abe,25,"Yokohama Aoba-ku"

decodeを使った例:a10_readfile.ipy

f = open("a10_readfile.csv")
for line in f:
  print line.decode("Shift_JIS")

IronPython 1.xの頃の解説を読むとコマンドプロンプトに出力される文字が化けてしまうのを避けるために"sys.setdefaultencoding()"を使っていたようですが、いまはそんな名前のメソッドはsysモジュールにはありません。

ファイルから読み込んだ文字列をコマンドプロンプトやGUIに渡したい場合には、ファイルのエンコーディングと同じエンコーディングをdecodeメソッドに渡せば扱えるようです。 ファイルはUTF-8でもShift-JISでも正しく指定すれば大丈夫なようです。

テキストファイルから読み込んだ文字列をラベルとコマンドプロンプトに出力するスクリプト:a11_readline_decode.utf8.ipy

# encoding: UTF-8
# @author: YasuhiroABE <yasu@yasundial.org>
#
import System
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import *
clr.AddReference("System.Drawing")
from System.Drawing import *

form = Form()
form.Size = Size(240,100)
label = Label()
label.Size = form.Size

f = open("a11.utf8.txt")
for line in f:
  print line.decode('utf8')
  label.Text += line.decode('utf8')

form.Controls.Add(label)
Application.Run(form)

確認のためだけなので横着をしてライブラリは使わずにスクリプトを組み立てました。

4-1. スクリプトの実行結果

$alt

スクリプトを変更し、"all.sjis.txt"を読み込みline.decode('Shift_JIS')で変換しても同じ結果が得られます。


Created: 2010-03-13, Last modified: 2010-03-19

2009,2010 © Yasuhiro ABE <yasu@yasundial.org>

Valid XHTML + RDFa RDFa it (RDF/XML)!

正当なCSSです!

Creative Commons License www.yasundial.org by Yasuhiro ABE is licensed under a Creative Commons Attribution 2.1 Japan License. Permissions beyond the scope of this license may be available at http://www.yasundial.org/info/license.html.