follow us in feedly

2017年10月

Windows版Python3でCSVをSQLiteに保存する

前回のWindows版PHPでのCSVパース問題を解決するためPythonを採用しました。
今回は、そのWindows + PythonでCSVを取り込む方法をご紹介します。
詳細な解説はほとんどなく、ほぼソースコードのみです。

環境

  • Windows 7
  • Python 3.6.3

ソースコード

CSVを読み込んでデータベースに保存します。
仕事で作ったものなので改変しています。

# -*- coding: utf-8 -*-

import os
import io
import sys
import csv
import sqlite3

# 標準出力の文字コードをUTF-8にする
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# ディレクトリを移動
os.chdir(r'C:\Users\77dogs\Sample')

# データベースに接続
conn = sqlite3.connect('db\contents.sqlite3')
c = conn.cursor()

# contentsテーブルのカラムを取得
sql = 'SELECT * FROM contents LIMIT 1'
c.execute(sql)
columns = []
for row in c.description:
    columns.append(row[0])

sql = 'INSERT INTO contents (%s) VALUES (%s)' \
        % (','.join(columns), ('?,' * len(columns)).rstrip(','))

# CSVの読み込み & 保存
with open('csv\data.csv', encoding='utf-8') as csvfile:
    reader = csv.reader(csvfile, delimiter=',', quotechar='"')
    next(reader)  # ヘッダ読み捨て

    for row in reader:
        c.execute(sql, tuple(row))  # SQLを実行
        conn.commit()  # commit()は必須

conn.close()

標準出力の文字コードを変更する

MSYS2にprint()で出力したときに文字化けしたのでその対策。
sys.stdoutのデフォルトの文字コードはCP932なのでUTF-8に変更。
ちなみに、上記ソースコードにはprint()はありません。

print(sys.stdout.encoding)  # cp932
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
print(sys.stdout.encoding)  # utf-8

raw文字列によってエスケープシーケンスを無効にする

エスケープシーケンス(\n, \t など)として解釈してほしくない場合の対策。
下記の場合は、「\U」があるのでraw文字列でないとエラーになる。

# SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
os.chdir('C:\Users\77dogs\Sample')

# raw文字列(r'文字列')だとエスケープシーケンスを無効化
os.chdir(r'C:\Users\77dogs\Sample')

まとめ

Windows + Python3を使って無事にCSVの取り込むができました。
情報もたくさんあるので、多少のつまづきはあったものの簡単に実装できました。
ファイルパスにバックスラッシュを使う場合はエスケープシーケンスに注意です。
はじめてのPython成果物ですが参考になれば幸いです。

参考

sys.stdout のエンコードを変更する in Python3.0
raw文字列 – 文字列 – Python入門
エスケープシーケンス – 文字列 – Python入門
Pythonでsqlite

Windows版PHPでCSVが正しくパースされない問題に悩んだ

仕事でちょっとした多言語サイトを開発することになったわけですが
掲載情報をCSVで取り込むときに正しくパースされない問題に悩まされました。
ということで、解決(?)するまでの経緯を書いていきます。

環境

Windows7
PHP 7.1.8
SQLite 3.20.0

Windows7 + PHPでマルチバイト文字のCSVのパースができなかった

今回、開発するサイトは多言語のサイトです。
日本語に加えて英語、韓国語、中国語(繁体字・簡体字)も扱います。

各言語のCSVをPHPのSplFileObjectで読み込もうとするも
日本語、韓国語、中国語といったマルチバイト文字の言語が適切にパースされませんでした。
文字コードはすべてUTF-8です。

文字エンコードをしてみる

UTF-8のまま読みたかったが仕方がないので、下記のとおりにエンコードすることに。
すると、いい感じにパースされたので、「解決!」と思ったのだけど。。。

  • 日本語→CP932
  • 韓国語→CP949
  • 繁体字→CP950
  • 簡体字→CP936

エンコードできない文字がある

いい感じだったのは日本語、韓国語まで。
繁体字になってエンコードできない文字(?になってる)があることが判明。
簡体字も繁体字ほどではないにしろ同様で、よくよくみると韓国語もまれにあるという状況でした。

setlocaleも試してみる

文字化けするのなら、UTF-8でなんとかするしかないかと考えて
setlocale()をするもパース位置が変わる程度で解決には至らず。

ちなみに、Windowsでのsetlocale()はセットの仕方に注意。

# これだとセットできない
setlocale(LC_ALL, 'ja_JP'):

# これだとセットできる
setlocale(LC_ALL, 'Japanese_Japan');
setlocale(LC_ALL, 'Japanese_Japan.932');
setlocale(LC_ALL, 'ja-JP');

他の環境での動きを見てみる

以下の3つの環境でどうなるかを見てみた。

  • CendOS6 + PHP5.3
  • Windows7 + Python3
  • Windows PowerShell

結果としては、上記の環境ではUTF-8のまま問題なくパースできました。
ということで、Windows版PHPがダメらしいということがわかったのです。

Windows7 + Python3でやることにした

時間も限られているので、PHPでの解決は諦めることにして
Windows7 + Python3でCSVを取り込むことにしました。

理由としては、

  • 今回は諸事情によりWindows環境でやるしかないこと
  • PowerShellでもいいのだけど、Pythonを使ってみたかったからです。

ここにはPythonでの詳細は書きませんが無事に取り込めました。

まとめ

Windows7 + PHPでの開発で遭遇したCSVのパース問題をご紹介しました。

  • CSVがマルチバイト文字の言語だと正しくパースされなかった
  • 各言語をUTF-8からエンコードするとパースはされたが一部文字化けした
  • setlocale()するも期待した結果は得られなかった

結局、Windows版PHPがダメだと判断して、Pythonを使うことで解決しました。
Windows + PHPでの開発をお考えの方はご注意ください。

Excel VBAで選択中セルのURLをQRコード表示する

約3年ぶりの更新ですね。
今回もどこまで続けられるのかは誰にもわかりません。

1発目は、はじめて使ったExcel VBAについて。
仕事の中で「こんなの作れない?」とお願いされたのがきっかけです。

作ったのは、選択中セルにあるURLをQRコードに変換して表示するもの。
ExcelのURL一覧をスマートフォンで確認していくのに使うようです。
バージョンはExcel 2013です。
もともとは仕事用なので業務に特化したところは変えてます。

ソースコード

ソースコードはこんな感じです。
ユーザーフォーム (UrlForm.frm)は使ってみたかっただけです。
これくらいならInputBoxを使ったほうが楽だと思う。

MakeQRCodeModule.bas

Option Explicit

Sub MakeQRcode()
  On Error GoTo ErrorMsg

  Dim url As String
  Dim objIE As Object

  '選択中のセルからURLを取得
  url = ActiveCell.Value

  'URLが取得できない場合はフォーム入力(1回のみ)
  If url = Empty Then
    UrlForm.Tag = "Cancel"
    UrlForm.Show

    'OKボタン以外(キャンセルボタン、閉じるボタン)は終了
    If Not UrlForm.Tag = "OK" Then
      Exit Sub
    End If

    'テキストボックスにURLが入力されなかったら終了
    If UrlForm.UrlTextBox.Value = Empty Then
      MsgBox ("URLが取得できません")
      Exit Sub
    End If

    'テキストボックスからURLを取得
    url = UrlForm.UrlTextBox.Value
  End If

  'Google Chart APIのURLを作成
  url = "http://chart.apis.google.com/chart?cht=qr&chs=150x150&chl=" & Application.WorksheetFunction.EncodeURL(url)

  'Internet ExplorerでGoogle Chart APIからQRコードを取得して表示
  Set objIE = CreateObject("InternetExplorer.Application")
  With objIE
    .Visible = True
    .Top = 100
    .Left = 500
    .Width = 500
    .Height = 500
    .Navigate (url)
  End With

  Do While objIE.Busy = True Or objIE.ReadyState <> 4
    DoEvents
  Loop

  Exit Sub

ErrorMsg:
  MsgBox ("Error:" & Err.Description)
End Sub

UrlForm.frm

Private Sub okButton_Click()
  UrlForm.Tag = "OK"
  UrlForm.Hide
End Sub

Private Sub cancelButton_Click()
  UrlForm.Hide
End Sub

Private Sub UrlForm_Activate()
  UrlForm.UrlTextBox.SetFocus
End Sub

ポイント

選択中の結合セルの値を取得

結合されたセルから取得する必要があったので
調べていくつか出てきた方法を試したけど取得できず。
結局、下記のようなセルの値を取得する方法でできました。

ActiveCell.Value

Excel 2013だからできたのかもしれないです。
上記の全ソースコードにはないけど業務用バージョンでは使ってるので紹介。

ユーザーフォームのどのボタンをクリックしたか判別する

キャンセルボタンをクリックしたときにエラー表示なしで終了するために実装。

まず、UserFormのTagプロパティに情報を入れる。

UrlForm.Tag = "OK"

そうすると、モジュール側で判定できるようになります。

If Not UrlForm.Tag = "OK" Then
  Exit Sub
End If

この方法が調べたなかで一番シンプルでした。
Tagプロパティに入れた情報は終了しても残るので、フォームを起動するごとに初期化してます。

ユーザーフォームのフォーカス

テキストボックスにフォーカスさせるならSetFocusメソッドを使う。
ユーザーフォーム表示時にセットする場合は、Activateイベント内でメソッドを呼ぶ。
Initializeイベント内で呼び出してもできませんでした。

まとめ

今回は、選択中セルにあるURLをQRコードに変換して表示するExcel VBAを作りました。
いくつか苦労したポイントを紹介していますが、QRコードを表示したいだけなら
Google Chart APIのURLを作成してIEを起動すればいいので、もっと簡単にできると思います。
初Excel VBAということもあってよろしくない実装もあるかもですが、参考になれば幸いです。

参考

Excel VBA 入門講座
Excel VBA入門
VBAでIE制御ライブラリの追加 | IE操作の自動化
Excel VBA を学ぶなら moug モーグ | 即効テクニック | Tagプロパティを利用してユーザーフォームからの情報を受け取る