Recently in Python Category

0 は False

| No Comments | No TrackBacks

Python で久しぶりにどはまりした話題。

いつものように Python でデータを処理して出力するプログラムを書いていたときのこと。順調に動いていたんだけど、なぜか時々思ったような動きをしていない。

そのときのコードは以下のような感じ。

dict_data = { 適当な辞書データ }
if dict_data.get(key, None):
     print u'値があったときの処理'
else:
     print u'値が無かったときの処理'

てな感じで辞書に希望キーがあった場合は値を処理して、無かった場合はエラーにならない感じに処理を書いていたのですが…なぜかあるはずのキーの値がないと言われる。

これ、おそらく Python 熟練者にはあまりにもばかばかしいバグっぽい感じですね。私も気づいたときは頭を抱えましたよ。

で、正解は…真値テストが 0 の時は偽なんですね。当然です。はい。だから、値が 0 の場合は、当然無かったときの処理が動くわけで…

えらい初歩的なことにはまってしまったなぁ。

先日、ちょっとした原因で utf-16 のテキストファイルを加工する必要が出てきたので、いつものように Python で書いていたら、なぜか decode('utf-16') でエラーになる。

しかも '\n' やら '\x00' をデコードしようとしてエラーに…てか、utf-16 だから \x00 はあるよね⁉

と、かなり頭の中をはてなマークが駆け巡ったのですが…

このとき、いつものように行単位で処理をしていたんだけどどうやらそれだと utf-16 をうまくデコード出来ないらしい。

どうやら utf-16 のテキストを読むときは codecs.open の方を使わないと駄目なようです。

しかしいまだに初歩的なことではまるなぁ…

どうでもいいけど、内部は Shift-jis で扱って、出力が utf-16 になるっぽい Excal2010 の VBA さんはどうにかならないのかな。

XML はめんどくさい

| No Comments | No TrackBacks

Python で XML を扱うときのお話し。わりとはまったので備忘録をかねて。

Python 2.5 以降であれば XML を扱うのに便利なモジュール ElementTree が内蔵されています。で、これを使って XML を読み込もうと思ったのですが…なぜか期待した通りに動作しない。具体的には findall で何もヒットしない。

で、しばらく悩んでもわからないので iterator で全出力をしてみたら…

from xml.etree import ElementTree
d=ElementTree.parse(open(filename,'rb'))
for i in d.getroot():
  print i.tag

以下のように出力されました。

{http://URL}Header
{http://URL}Body

最初見た瞬間なんじゃこりゃ⁉と叫んだのはわりと内緒です。

で、しばらく悩んで、いろんな人に相談した結果…URL の中身は xmlns の値(ネームスペース)で、どうやら XML というのはそういうものらしい。とのこと。

とはいえ、これじゃ若干めんどくさいのでいったん読み込んで、xmlns の中身を削除して、ElementTree はデータを引数として渡すことは出来ないっぽいので、cStringIO で…というわりと面倒な方法で対処しました。

import re, cStringIO
from xml.etree import ElementTree
xmlns_re = re.compile(r'^(.*?xmlns=")(.*?)(".*)$', re.I|re.S)
fp=open(filename,'rb')
data=fp.read()
fp.close()
data=xmlns_re.sub(r'\1\3', data)
fp=cStringIO.StringIO(data)
d=ElementTree.parse(fp)
fp.close()

もう少しうまいやり方がありそうだけど…XML はめんどくさいな(^^ゞ

urllib2 と gzip

| No Comments | No TrackBacks

結構どはまりした話題。

とある理由から html で書かれたデータを解析する必要が出てきました。

html を解析する場合、実は結構面倒で何か楽出来ないかな~と思って、いろいろ探していたらBeautifulSoupというとっても便利なライブラリを発見したのですよ。

で、これはラッキー!と思って早速使って組んでみたら、とてもうまく動いてくれてかなり満足していたのですが…とある euc-jp で書かれた html ページがなぜか解析失敗する。

どうも追いかけていくと、euc-jp ではなく、windows-1252 で判別されているっぽい。

ちょっと不思議に思ったので、urllib2 で直接データを取得して…と思ったのがすべての間違い。ここからめちゃくちゃはまってしまいました…まぁ、単純に以下のようにhtmlを取得した(つもり)だったのですが…

import urllib2
fp=urllib2.urlopen('http://どこかの.ページ')
data=fp.read()
fp.close()

なぜか取得したデータが化けている!しかも取得サイズが全然違う。もう、頭の中大混乱でした…

結局一日ぐらいたって、わかったのが…Content-Encoding: gzip なデータは urllib2 で受信した場合、自前で展開しなければならないということ。

無駄にえらい時間を使ってしまった…

ちなみに、当初の BeautifulSoup がエンコードを間違えるのは、euc-jp で変換出来ない文字が使われているから、でした。

この問題も結構面倒だよなぁ……

巨大なファイルを post

| No Comments | No TrackBacks

Python で cgi を組んでいるとき、良く悩むのがファイルの post

普通のファイルを受け取るだけで良ければ form に enctype="multipart/form-data" を指定して、cgi.FieldStorage() で受け取ればいいのですが、これだと巨大なファイルを受け取ったとき、どうしてもクライアント側でずっと待たされるので何とかしたい。

まぁ、用は Windows のファイルコピーなんかみたいに進捗状況を表示できればいいのですが、これって Python 云々じゃなく、JavaScript 等のブラウザ側の処理になるんですよね…若干悩みどころです。

何処かにそういったモジュールありそうなんだけどなぁ~なかなか見当たらないものですな。

ちなみに巨大ファイルを受け取るとき、cgi.FieldStorage() オブジェクトの value プロパティで受け取ったりすると、ファイルの内容をそのまま文字列として受け取ってサーバが悲しいことになるのでヘルプにある方法で受け取りましょう。

本番サーバでやってしまうと 4G のスワップファイルが出来たりして大変なことになるよ!(←やったのかよ…)

知り合いの blog で書かれていたタイトルを見てふと思い出した Python でのお話。

Python のファイルオブジェクトには readlines() という便利な命令があります。普段簡単なスクリプトを組むときは使うのですが、readlines() はいったんメモリに読み込むため、大きな行数があるファイルを読むときに使うと使用メモリが一気に増大して悲惨なことになります。

というか、サーバのログファイルを整理するスクリプトを組んでいて気づいたのですね。

で、まぁ、readline() を使えばいいのですが…そのとき組んだのが以下のような感じ。

fp = open(filename, 'rb')
while True:
  l = fp.readline()
  if len(l) < 1:
    break
  l = l.rstrip()
  # 以下なんかの処理

な~んか美しくないんだよなぁ…この書き方(^^ゞ

ふと思ったけど Python 2.5 以降なら with ステートメントを使うのがよかったのかな?

Python でお手軽乱数作成

| No Comments | No TrackBacks

いつの間にか一ヶ月近く更新していなかった...ので、久しぶりにプログラム的な話題を。とは言っても、たいしたことではありませんが(^^ゞ

ウェブ上のいろいろなサービスに登録するとき、パスワードを登録するのですが、毎回パスワードを考えないと駄目だし、記号が必ず入っていないと駄目とか、何故か6文字以上8文字以内とかいう謎の制限があったりして考えるのが面倒だったので python で簡単なのを作ってみました。

import os, string, random
from optparse import OptionParser, make_option

option_list = [
  make_option( '-n', '--num', action='store', type='int', dest='num',
    help=u'使用する桁数', default=8 ),
  make_option( '-u', '--upper', action='store_true', dest='upper',
    help=u'大文字だけを使う', default=False ),
  make_option( '-l', '--lower', action='store_true', dest='lower',
    help=u'小文字だけを使う', default=False ),
  make_option( '-c', '--code', action='store_true', dest='code',
    help=u'記号[' + string.punctuation + u']を使う', default=False ),
  make_option( '-d', '--digits', action='store_true', dest='digits',
    help=u'数字を使う', default=False ),
  make_option( '-a', '--all', action='store_true', dest='all',
    help=u'全部使う', default=False ),
]

def main():
  parser = OptionParser( usage = u'%prog [options]',
    description = u'乱数生成',
    option_list = option_list )

  (options, args) = parser.parse_args()

  s = ''
  if options.upper:
    s = string.ascii_uppercase
  elif options.lower:
    s = string.ascii_lowercase
  else:
    s = string.ascii_letters

  if options.code:
    s += string.punctuation

  if options.digits:
    s += string.digits

  if options.all:
    s = string.ascii_letters + string.punctuation + string.digits

  if len(s) > 1
    s=string.ascii_letters
  l = list(s)
  n = options.num
  if n < 1:
    n = 1
  print u''.join( l[:n] )

if __name__=='__main__':
 main()

とまぁ、かなり適当な乱数で、しかも一度使った文字は必ず出てこないというあれな乱数ですが(^^ゞ一応使えるかな~と

しかし、パスワードに日本語文字列を使える日は来るのかな?

Python で文字コード判別

| No Comments | 1 TrackBack

久々のプログラミングネタ。

Python で文字列の文字コードを判別したくなったんだけど、標準では用意されていないらしい。で、いろいろと調べてみたら、今のところ以下の二種類があるのかな?

  1. UNIVERSAL ENCODING DETECTOR
    chardet と呼ばれている(?)ライブラリ。confidence というパラメータで、確かさを返してくれる。
    Document がちょっと不便?
  2. NkfPython
    日本人なら誰でも知っている(?) nkf の python インターフェイス。文字コード判別だけでなく変換もやってくれる優れもの。

んで、軽く実験してみた。

chardet

Nkf

'\x82\xa0\x82\xa2\x82\xa4\x82\xa6\x82\xa8'
(sjis で'あいうえお')

'windows-1252'

'Shift_JIS'

'\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa'
(euc-jp で'あいうえお')

'EUC-JP'

'EUC-JP'

'\x1b$B$"$$$&$($*\x1b(B'
(iso-2022-jp で'あいうえお')

'ISO-2022-JP'

'ISO-2022-JP'

てな具合に、chardet が sjis だけ判定ミス。たぶん、内部判定の優先順位の問題なんだろうけど、短い文字列だと間違えやすい気がします。>chardet

まぁ、普通に NkfPython を使えばいいのかな。長い文字列だと chardet も精度が上がるので問題ないんだけどね。