Pythonでフォーマットして文字列出力

目次

フォーマットして文字列出力

PHP sprintf

<?php
$str = sprintf('今日は%s、明日は%s', '晴れ', '曇り');
echo $str;
// 今日は晴れ、明日は曇り

$str = sprintf('今日は%1$s、明日も%1$s', '晴れ');
echo $str;
// 今日は晴れ、明日も晴れ

$str = sprintf('%.2f', 1 / 3);
echo $str;
// 0.33

$str = sprintf('%04d', 12);
echo $str;
// 0012

$str = sprintf('%1$04d, %1$08d', 12);
echo $str;
// 0012, 00000012

直接出力する場合は printf を使用できます。

Python %演算子 2.x 3.x

# -*- coding: utf-8 -*-
str = u'今日は%s、明日は%s' % (u'晴れ', u'曇り')
print(str)
# 今日は晴れ、明日は曇り

str = u'今日は%(weather)s、明日も%(weather)s' % {'weather': u'晴れ'}
print(str)
# 今日は晴れ、明日も晴れ

str = '%.2f' % (1.0 / 3.0)
print(str)
# 0.33

str = '%04d' % 12
print(str)
# 0012

str = '%(i)04d, %(i)08d' % {'i': 12}
print(str)
# 0012, 00000012

Python 3系ならマルチバイト文字でUnicodeを明示するu''は必要ありません。

Pythonで末尾の改行だけ取り除く

ファイルから読み込んだ時など、末尾にある改行だけ取り除きたい場合があります。
末尾の1文字だけ取り除くと改行以外の文字が取り除かれてしまう恐れがありますね。
この場合Pythonではどのように行うのか調べてみました。

目次

末尾の改行だけ取り除く

いずれもstringとスペースは残り、字数9となり、末尾の改行(\r or \n)だけが取り除かれます。
\r\nWindows\nLinux\rは旧Mac

PHP rtrim

<?php
echo strlen(rtrim("string   \r\n\r\n", "\r\n"));

echo strlen(rtrim("string   \n\n", "\r\n"));

echo strlen(rtrim("string   \r\r", "\r\n"));

Python rstrip 2.x 3.x

print(len('string   \r\n\r\n'.rstrip('\r\n')))

print(len('string   \r\r'.rstrip('\r\n')))

print(len('string   \n\n'.rstrip('\r\n')))

Pythonは文字列をスライス表記で操作できるので次のようなことが可能ですが、末尾が文字列でない場合も取り除いてしまうため注意が必要です。

s = 'string   \n'
print(s[:-1])
# string   

s = 'string'
print(s[:-1])
# strin

こちらを参考にしました。

PHPで言うところのltrim, trim, rtrimが、
Pythonではlstrip, strip, rstripで利用できるようです。

Pythonで複数の値を複数の変数に同時に代入する

PHPでは見慣れない感じでしたのでメモとして残します。

左辺に複数の変数、右辺に同数の値を記載して代入

a, b = 1, 2

print(a)
# 1

print(b)
# 2

これはタプルのパックとシーケンスのアンパックを組み合わせたものとのことです。
http://docs.python.jp/2/tutorial/datastructures.html#tuples-and-sequences
http://docs.python.jp/3/tutorial/datastructures.html#tuples-and-sequences

PHPで似たようなことをしようとすると、

<?php
list($a, $b) = [1, 2];

echo $a;
// 1

echo $b;
// 2

※複数の値を配列として返すfunctionからの返り値を受け取る場合などに利用できるかと思われます。

配列の途中の値をまとめて置き換え

スライスで行えます。

v = range(1, 10)
print(v)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

v[1:6] = ['a'] * 5
print(v)
# [1, 'a', 'a', 'a', 'a', 'a', 7, 8, 9]

v[1:6]:の左側のoffset以上:の右側のoffset未満と覚えることにします。

PHPで行うと、

<?php
$v = range(1, 9);
print_r($v);
/*
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
    [6] => 7
    [7] => 8
    [8] => 9
)
*/

array_splice($v, 1, 5, array_fill(0, 5, 'a'));
print_r($v);
/*
Array
(
    [0] => 1
    [1] => a
    [2] => a
    [3] => a
    [4] => a
    [5] => a
    [6] => 7
    [7] => 8
    [8] => 9
)
*/

array_spliceの第1引数で指定する配列はリファレンス渡しになるので渡した配列そのものが書き換えられます。

Pythonで文字列を1文字ずつ分割する

シングルバイト文字の場合こちらのサイトを参考に解決しましたが、

マルチバイト文字ではどうなるのか疑問が生じたので方法を調べてみました。

目次

文字列を1文字ずつ分割する

PHP preg_split

普段はPHPを利用していますので、参考までにPHPではpreg_splitで行えることを記載します。

シングルバイト文字

<?php
$chars = preg_split('//', 'abcdef', -1, PREG_SPLIT_NO_EMPTY);
print_r($chars);
/*
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => d
    [4] => e
    [5] => f
)
*/

マルチバイト文字(UTF-8)

<?php
$chars = preg_split('//u', 'あいうえお', -1, PREG_SPLIT_NO_EMPTY);
print_r($chars);
/*
Array
(
    [0] => あ
    [1] => い
    [2] => う
    [3] => え
    [4] => お
)
*/

Python

2系も3系もlistを用いることで文字列を分割できる模様。
2系と3系でマルチバイト文字の取り扱いが少々異なるようです。

Python 2.x list

シングルバイト文字

chars = list('abcdef')
print chars
# ['a', 'b', 'c', 'd', 'e', 'f']

マルチバイト文字(UTF-8)

Python 2系では明示的にUnicode文字をあらわすu''としないと1byteずつ分割される模様。

  • list('あいうえお')
# -*- coding: utf-8 -*-
chars = list('あいうえお')
print chars
# ['\xe3', '\x81', '\x82', '\xe3', '\x81', '\x84', '\xe3', '\x81', '\x86', '\xe3', '\x81', '\x88', '\xe3', '\x81', '\x8a']
  • list(u'あいうえお')
# -*- coding: utf-8 -*-
chars = list(u'あいうえお')
chars = [char.encode('utf-8') for char in chars]
print str(chars).decode('string-escape')
# ['あ', 'い', 'う', 'え', 'お']

for c in chars:
    print c
# あ
# い
# う
# え
# お

Python 3.x list

シングルバイト文字

chars = list('abcdef')
print(chars)
# ['a', 'b', 'c', 'd', 'e', 'f']

マルチバイト文字(UTF-8)

Windows7コマンドプロンプトで素直にprintしたところ期待するUTF-8ではなくShift_JISに変換されてしまいました。

  • これを避けるため、当初UTF-8のbytes型に変換した上sysモジュール経由で出力。
# -*- coding: utf-8 -*-
import sys

chars = list('あいうえお')
chars = [bytes(char, 'utf-8') for char in chars]
sys.stdout.buffer.writelines(chars)
# あいうえお

for char in chars:
    sys.stdout.buffer.write(char)
    sys.stdout.write('\n')
    sys.stdout.flush()
# あ
# い
# う
# え
# お
  • しかし、この現象はencodingの設定により起こるとのこと。
    当初の初期値。
print(sys.getdefaultencoding())
# utf-8
print(sys.stdout.encoding) # 出力がutf-8ではない
# cp932
  • 次の記事を参考に適切な文字コードsys.stdout.encodingに設定したところprintでもできました。

# -*- coding: utf-8 -*-
import sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

chars = list('あいうえお')
print(chars)
# ['あ', 'い', 'う', 'え', 'お']

for char in chars:
    print(char)
# あ
# い
# う
# え
# お

マルチバイト文字はPython 3系の方が扱いやすいようです。

PHPerがPython 2.7でタブ区切りファイル(TSV)を読み込んで処理した際のメモ

普段プログラミング言語PHPを主に使用していますが、Python 2.7を使用する機会があったためメモを残します。

コマンドラインで実行する形式で行ったため、コマンドライン引数、引数に指定されたファイルの存在確認、ファイルを開く、読み取るなどの関数や処理をPHPなら~、Python 2.7なら~という形式でメモしています。

目次

コマンドライン引数

PHPPython 2.7も始めの要素にはスクリプトパスが入っている。

PHP $argv

if ($argc < 2) {
    exit('引数が足りません。');
}
print_r($argv);

Python 2.7 sys.argv

import sys
if len(sys.argv) < 2:
    sys.exit('引数が足りません。')
print sys.argv

ファイルの存在確認

PHP file_exists

if (!file_exists($argv[1])) {
    exit(sprintf('%s does not exist', $argv[1]));
}

Python 2.7 os.path.exists

import sys
import os.path
if not os.path.exists(sys.argv[1]):
    sys.exit('%s does not exist' % sys.argv[1])

ファイルを開く

PHP fopenfile_get_contents を使うと思います。

  • fopen
$fh = fopen($filename, 'r');
fclose($fh);
  • エラーハンドリングもする
$fh = fopen($filename, 'r');
if (!$fh) {
    exit($filename . 'を開けませんでした。');
}
fclose($fh);
  • file_get_contents
$string = file_get_contents($filename);

Python 2.7 open を使えますが、ファイルの読み込みなら with 構文をあわせて利用すると、ファイルの閉じ忘れがなくなり便利そうです。

  • with 構文を利用する open
with open(filename, 'r') as fh:
  • with 構文を利用しない場合は使い終わった後に閉じます。
fh = open(filename, 'r')
fh.close()
  • エラーハンドリングもする
import sys
fh = None
try:
    fh = open(filename, 'r')
except IOError as e:
    sys.exit('%sを開けませんでした。' % filename)
fh.close()

文字列を分割する

タブで分割する場合。
PHP\t をタブとして用いる場合ダブルクォーテーションの必要がありますが、Pythonではシングルクォーテーションとダブルクォーテーションの区別がないようです。

PHP explode

$words = explode("\t", $row);

Python 2.7 split

words = row.split('\t')

配列のスライス

PHP array_slice

$row = ['a', 'b', 'c'];
$words = array_slice($row, 0, -1);
print_r($words);
/*
Array
(
    [0] => a
    [1] => b
)
*/

Python 2.7 スライス表記というものがあるようです。

row = ['a', 'b', 'c']
words = row[:-1]
print words
# ['a', 'b']

ファイルに書き込む

PHP fwrite

fwrite($fh, '文字列');

Python 2.7 write, writelines

fh.write('文字列');
words = ['文', '字', '列']
fh.writelines(words);

異なる型の値を含む配列/リストをjoinして出力

PHPだと型を気にせずに implode で連結することが出来るところ、Python 2.7だと TypeError: sequence item 0: expected string, int found というエラーが発生して動作しなかったときの対処。

例えば id(int), name(string), created(string) というような配列/リストがあったとき、

PHP implode

$row = [123, 'name', date('Y-m-d H:i:s')];
print_r(implode("\t", $row));
// 123 name 2016-09-27 00:45:12

Python 2.7 join

import datetime
row = [123, 'name', datetime.datetime.now()]
print '\t'.join(row)
# TypeError: sequence item 0: expected string, int found

Python 2.7でTypeError発生。
調べたところstringにconvertすると良い模様。
http://stackoverflow.com/questions/5618878/how-to-convert-list-to-string

import datetime
row = [123, 'name', datetime.datetime.now()]
print '\t'.join(str(col) for col in row)
# 123 name 2016-09-27 00:45:12.804000

コマンドライン引数に指定のTSVファイルを読み込み何かしら処理する例

PHP fgetcsv

if ($argc != 2) {
    exit('1つ目の引数にはTSVファイルを指定してください。');
}

if (!file_exists($argv[1])) {
    exit(sprintf('%s does not exist', $argv[1]));
}

if (($csvfile = fopen($argv[1], 'r')) !== false) {
    while (($columns = fgetcsv($csvfile, 0, "\t")) !== false) {
        // 何かしら処理
    }
    fclose($csvfile);
}

Python 2.7 csvモジュール

# coding: utf-8
import sys
if len(sys.argv) != 2:
    sys.exit('1つ目の引数にはTSVファイルを指定してください。')

import os.path
if not os.path.exists(sys.argv[1]):
    sys.exit('%s does not exist' % sys.argv[1])

import csv
with open(sys.argv[1], 'r') as csvfile:
    reader = csv.reader(csvfile, delimiter='\t')
    for columns in reader:
        # 何かしら処理