配列/リストの中身を一度にすべて表示する PHP, Python

PHPを用いて次のようなコードで配列をループ処理せずに一度に全て表示する方法を調べてみました。

<?php
$chars = preg_split('//u', '配列の中身', -1, PREG_SPLIT_NO_EMPTY);
foreach ($chars as $char) {
    echo $char, '<br>', PHP_EOL;
}

/*
配<br>
列<br>
の<br>
中<br>
身<br>

*/

PHP: implode()を用いると意図している感じに表示できます。

<?php
$eol = '<br>' . PHP_EOL;
$chars = preg_split('//u', '配列の中身', -1, PREG_SPLIT_NO_EMPTY);
echo implode($eol, $chars), $eol;

/*
配<br>
列<br>
の<br>
中<br>
身<br>

*/

PHP: vprintf()を用いても意図している感じに表示できるようです。

<?php
$eol = '<br>' . PHP_EOL;
$chars = preg_split('//u', '配列の中身', -1, PREG_SPLIT_NO_EMPTY);
vprintf(str_repeat('%s' . $eol, count($chars)), $chars);

/*
配<br>
列<br>
の<br>
中<br>
身<br>

*/
  • vprintf()の第1引数に用いるフォーマット%s<br>PHP_EOLを配列$charsと同じ数count($chars)だけstr_repeat()で用意しています。
<?php
$chars = preg_split('//u', '配列の中身', -1, PREG_SPLIT_NO_EMPTY);
print_r($chars);

/*
Array
(
    [0] => 配
    [1] => 列
    [2] => の
    [3] => 中
    [4] => 身
)
*/

Python: *でリストをアンパックしてprintに渡すと意図しているように表示できます。

# -*- coding: utf-8 -*-
from __future__ import print_function
eol = '<br>\n'
chars = list(u'配列の中身')
print(*chars, sep=eol, end=eol)

'''
配<br>
列<br>
の<br>
中<br>
身<br>

'''
  • sepで区切り文字を<br>\nに変えています。
    より前に表示されている<br>\nです。
  • endで文末の文字を<br>\nに変えています。
    の後に表示されている<br>\nです。

*を付け忘れるとリストのまま表示されてしまいます。

*charsのように*を付ける事でリストがアンパックされるのでcharsのまま渡してしまうと意図しているようにはならないので注意が必要です。

[u'\u914d', u'\u5217', u'\u306e', u'\u4e2d', u'\u8eab']<br>
['配', '列', 'の', '中', '身']<br>

PythonでSQLite3を試す

Python2.7と3.5で動作するサンプルコードです。

SQLiteとは?

Oracle, MySQL, PostgreSQL, MariaDB などよりも軽量に動作し、アプリに組み込むなどして使われているようです。経験上デスクトップで確認したい場合や一時的に利用したい場合などに使うこともありました。SQLで操作します。メモリ管理も任せられるのでデータが多い場合、自前でテキストファイルで操作するよりもSQLiteを選択すると良い場合があります。

Python2 と Python3 で動作するサンプルコード

  • sqlite3モジュール 2.x 3.x を利用します。
# -*- coding: utf-8 -*-
import datetime, os, random, sqlite3, sys

if __name__ != '__main__':
    sys.exit()

db_file = 'example.db'

base_dir = os.path.dirname(__file__)
if not base_dir:
    base_dir = '.'

db_path = base_dir + os.path.sep + db_file
# db_pathが存在しなければTrue, 新規作成
db_is_new = not os.path.exists(db_path)

# 新規作成時にINSERTするデータ
def new_date():
    for i in range(10):
        yield (i + 1, int(random.random() * 100), datetime.datetime.now())

# db_pathが無ければスクリプトと同じディレクトリ内に自動で作成
conn = sqlite3.connect(db_path)

# db_pathが新たに作られた時
if db_is_new:
    # テーブル作成
    conn.execute('''
CREATE TABLE example(
    id INTEGER,
    num INTEGER,
    date TEXT
)
    ''')

    try:
        # with文終了時はcommit
        with conn:
            # データ投入
            conn.executemany('''
INSERT INTO example VALUES(?, ?, ?)
            ''', new_date())
    except sqlite3.IntegrityError:
        # 例外発生時はrollback
        pass

# カラム名で参照できるようにsqlite3.Rowを使用
conn.row_factory = sqlite3.Row

# SELECTしたデータを1行ずつ出力
for row in conn.execute('SELECT * FROM example'):
    r = ['%s = %s' % (k, row[k]) for k in row.keys()]
    print(', '.join(r))

conn.close()

importで呼ばれた場合は終了します。

  • このスクリプトは直接実行する前提ですのでimportで呼ばれた場合は終了します。
if __name__ != '__main__':
    sys.exit()

dbファイルはスクリプトと同じディレクトリ内にあるものとします。

db_file = 'example.db'

base_dir = os.path.dirname(__file__)
if not base_dir:
    base_dir = '.'

db_path = base_dir + os.path.sep + db_file

dbファイルが無い場合は新規に作成されます。

  • dbファイルの存在確認でまだファイルが無い場合、新たに作るものと判断しテーブルの作成などに利用します。
# db_pathが存在しなければTrue, 新規作成
db_is_new = not os.path.exists(db_path)

sqlite3.connect()で取得するConnectionオブジェクトをコンテキストマネージャーとして利用しています。

    try:
        # with文終了時はcommit
        with conn:
            pass
    except sqlite3.IntegrityError:
        # 例外発生時はrollback
        pass

conn.executemany()で複数データをまとめてINSERTしています。

# 新規作成時にINSERTするデータ
def new_date():
    for i in range(10):
        yield (i + 1, int(random.random() * 100), datetime.datetime.now())
            # データ投入
            conn.executemany('''
INSERT INTO example VALUES(?, ?, ?)
            ''', new_date())
  • サンプルコードでは第2引数にジェネレータを渡しています。
    イテレータやタプルのリストを渡してまとめてデータ登録します。

TOHO シネマイレージ カード作成後、6本目の映画を見終わりました

TOHO シネマイレージ カードを作成してから6本目の映画を見終わりました。

次の1本は無料で見られるらしい。

TOHO シネマイレージ カードを作成してから見た映画の備忘録。

1. 貞子 vs 伽椰子

  • ホラー映画を時々見たくなり、たまたま上映していた貞子 vs 伽椰子
    個人的には、それぞれのホラー映画を別々に見た方が怖いような気がしました。

2. Independence Day: Resurgence

  • 前作のIndependence Dayは1996年に公開されていたそうです。『えっ、20年も前!?そんなに前だったっけ』という驚きです。

3. 君の名は。

  • 何度も見たくなりました。(個人の感想)
    実在する場所が登場したり、見たことのあるような景色を楽しむこともできるかもしれません。
    Blu-rayで出たらおそらく買います。

4. Self/less

5. BFG

  • 戸締りはしっかりしよう。

6. GANTZ:O

  • 映画の始まりから見覚えのある感じの街並み、
    アクションシーンに釘付けになりました。
    3Dモデリングの作りこみ、モーションなど随所に手が込んでいる印象を持ちました。
    あくまでも個人の感想ではありますが、Blu-rayで出たらおそらく買います。

  • 原作を読みたくなりました。

  • 3DCG製作元のメイキング記事も興味深いです。

PythonでURLをパースする

Python2.7と3.5で動作するサンプルコードです。

Python2 と Python3 で動作するサンプルコード

  • URLのパースにurlparseを利用します。
  • params, queryのパースにはparse_qsを利用します。
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys

# print(sys.version_info)
## 2.7: sys.version_info(major=2, minor=7, micro=11, releaselevel='final', serial=0)
## 3.5: sys.version_info(major=3, minor=5, micro=1, releaselevel='final', serial=0)

if sys.version_info.major <= 2:
    # Python 2.7
    from urlparse import urlparse, parse_qs
else:
    # Python 3.5
    from urllib.parse import urlparse, parse_qs

parse = urlparse('https://www.example.com/file;p1=1;p1=2?encoding=UTF-8&q1=1&q1=2&q1=3&q2=#q=example')
print(parse)
# 2.7: ParseResult(scheme='https', netloc='www.example.com', path='/file', params='p1=1;p1=2', query='encoding=UTF-8&q1=1&q1=2&q1=3&q2=', fragment='q=example')
# 3.5: ParseResult(scheme='https', netloc='www.example.com', path='/file', params='p1=1;p1=2', query='encoding=UTF-8&q1=1&q1=2&q1=3&q2=', fragment='q=example')

p = parse_qs(parse.params)
print(p)
# 2.7: {'p1': ['1', '2']}
# 3.5: {'p1': ['1', '2']}

# 値がカラのパラメータは無視される
q = parse_qs(parse.query)
print(q)
# 2.7: {'q1': ['1', '2', '3'], 'encoding': ['UTF-8']}
# 3.5: {'q1': ['1', '2', '3'], 'encoding': ['UTF-8']}

# parse_qs()の第2引数をTrueとすることで値がカラのパラメータも結果に含まれる
q2 = parse_qs(parse.query, True) 
print(q2)
# 2.7: {'q1': ['1', '2', '3'], 'q2': [''], 'encoding': ['UTF-8']}
# 3.5: {'q2': [''], 'q1': ['1', '2', '3'], 'encoding': ['UTF-8']}

for key in sorted(q.keys()):
    # 区切り文字`sep`の初期値は' '(半角スペース)
    # 文末`end`の初期値は'\n'
    print(key, '=', q[key], sep='\t', end=', ') 
    # encoding  =   ['UTF-8'], q1   =   ['1', '2', '3'], 

print()

if 'q1' in q: # Python3 に dict.has_key() は無い
    for v in q['q1']:
        print(v, end=', ')
        # 1, 2, 3, 

import文、from文の注意点

  • import文はimport モジュール
  • from文はfrom モジュール import 関数

Python 2.6以降で3のprint()関数を利用できるようにする

Python 2と3で読み込むモジュールを変える

  • urlparse, parse_qs の含まれるモジュールがPython 2と3で異なるので、バージョンによって読み込むモジュールを変えています。
import sys
if sys.version_info.major <= 2:
    # Python 2.7
    from urlparse import urlparse, parse_qs
else:
    # Python 3.5
    from urllib.parse import urlparse, parse_qs

dict.has_key() は廃れた用法

  • Python 3でdict.has_key()を使用するとAttributeErrorが発生します。
AttributeError: 'dict' object has no attribute 'has_key'
  • dictにkeyがあるかどうかはkey in dictでチェックします。
if key in dict:
    pass

d = {'a':12}
print('ok' if 'a' in d else 'ng')
# ok
  • 反対にdictにkeyがない場合のチェックはkey not in dictで行えます。
if key not in dict:
    pass

d = {'a':12}
print('ok' if 'a' not in d else 'ng')
# ng

各関数のドキュメント

urlparse

parse_qs

PHPで画像アップロードに関して注意するphp.iniの設定

ファイルアップロードに関する設定

  • file_uploads 初期値 1
    ファイルアップロードを有効にするかどうか。
  • upload_max_filesize 初期値 "2M"
    アップロードできる1ファイルあたりの最大サイズ。
  • max_file_uploads 初期値 20
    同時にアップロードできるファイルの最大数。

データ処理に関する設定

それぞれの設定を確認

  • php.iniファイルを見る場合、ファイルの場所が不明な際php --iniとすることでiniファイルの場所を確認できます(少なくとも5.6.22では。)
$ php --ini
  • 個々のディレクティブの状況確認は次のコードを実行。
<?php
$settings = ['file_uploads', 'upload_max_filesize', 'max_file_uploads', 'post_max_size'];
foreach ($settings as $name) {
    echo $name, ' = ', ini_get($name), '<br/>', "\n";
}
  • 初期値のままなら画面上に次のように出力されます。
file_uploads = 1
upload_max_filesize = 2M
max_file_uploads = 20
post_max_size = 8M

Pythonでテキストファイルを読み込む

プレーンなテキストファイルをwith構文で読み込む時に試したメモです。
以前挑戦したCSVモジュールを利用してのTSVファイルを読み込む方法はこちら

Windowsコマンドプロンプト上で動かしたため、出力に用意したテキストファイルはShift_JISとしています。

あ
い
う
え
お

目次

1行ずつ読み込む iterator

# -*- coding: utf-8 -*-
from __future__ import print_function

with open('sjis.txt') as f:
    for line in f:
        print(line, end='')
  • Python 2.7 での実行結果
あ
い
う
え
お
  • Python 3.5 での実行結果
あ
い
う
え
お

特別な理由が無ければiteratorを利用して1行ずつ読み込む方法で十分なようですが、iteratorを利用しない方が良い場合もあるようです。

全部読み込む read()

# -*- coding: utf-8 -*-
from __future__ import print_function

with open('sjis.txt') as f:
    print(f.read(), end='')
  • Python 2.7 での実行結果
あ
い
う
え
お
  • Python 3.5 での実行結果
あ
い
う
え
お

数GBにもおよぶログファイルなどはメモリが足りなくなりそうです。

リストとして全部読み込む readlines()

# -*- coding: utf-8 -*-
from __future__ import print_function

with open('sjis.txt') as f:
    print(f.readlines())
  • Python 2.7 での実行結果
['\x82\xa0\n', '\x82\xa2\n', '\x82\xa4\n', '\x82\xa6\n', '\x82\xa8\n']
  • Python 3.5 での実行結果
['あ\n', 'い\n', 'う\n', 'え\n', 'お\n']

1行だけ読み込む readline()

# -*- coding: utf-8 -*-
from __future__ import print_function

with open('sjis.txt') as f:
    print(f.readline(), end='')
  • Python 2.7 での実行結果
  • Python 3.5 での実行結果

いずれの方法も末尾の改行コードまで含まれています。
末尾の改行コードが不要な場合はrstrip()で除去などを行う必要があります。

Pythonで排他ロックしてファイルに書き込む

排他ロックとは?

排他ロックしてファイルを壊れ難くしてみます。

目次

排他ロックしてファイルに書き込む

Pythonではfcntlモジュール(2.x)(3.x)を利用します。

WindowsではサポートされていないのでWindowsでは動作しません。
r+の読み書きモードでは新たにファイルは作られません。あらかじめカラのtest.txtを用意します。

# -*- coding: utf-8 -*-
import fcntl, os

filename = 'test.txt'

# ファイルを読み書きモードで開く
with open(filename, 'r+') as f:
    try:
        # ファイルを排他ロックする
        fcntl.flock(f, fcntl.LOCK_EX)

        # ファイルから読み込む
        data = f.readlines()

        # ファイルを切り詰める
        f.truncate(0)

        # ファイルの位置をリセット
        f.seek(os.SEEK_SET)

        # データを加える
        data.append('hoge\n')

        # ファイルに書き込む
        f.writelines(data)
    except IOError:
        print('flock() failure')

その他ファイル系操作

ファイルを開く

ファイルの存在確認