銀月の符号

Python 使い見習いの日記・雑記

Python レシピの増量、ひと段落

http://lightson.dip.jp/zope/ZWiki/FrontPage/recentchanges 。25 個以上追加して満足。

067:文字列を最大n桁に切り詰める

East Asian Width 使用版。

# coding: utf-8
from unicodedata import east_asian_width

_east_asian_width = {
        'Na': 1,
        'N': 1,
        'W': 2,
        'H': 1,
        'A': 2,
        }
def take_ncols(s, n):
    length = 0
    result = []
    for c in s:
        length += _east_asian_width[east_asian_width(c)]
        if length > n:
            break
        result.append(c)
    return u''.join(result)

def test():
    print take_ncols(u'a'* 30, 20)
    print take_ncols(u'あ'* 30, 20)
    print take_ncols(u'a'* 15 + u'あ' * 15, 20)

if __name__ == '__main__':
    test()

123:2つのハッシュの両方にあるキー/一方にしかないキーを見つける

集合型の出番。

>>> dict1 = {'a': 1, 'b': 2}
>>> dict2 = {'a': 3, 'c': 4}
>>> set(dict1) & set(dict2)
set(['a'])
>>> set(dict1) ^ set(dict2)
set(['c', 'b'])

065:文字列を最大nバイトに切り詰める

放置してきた一品。とうとう手をつけた。出来はしたものの、このレシピには出番はあるのだろうか? あと速度も心配。

# coding: utf-8

def _shift_jis(c):
    if c <= '\x7f' or '\xa1' <= c <= '\xdf':
        return 1
    elif '\x81' <= c <= '\x9f' or '\xe0' <= c <= '\xfc':
        return 2
    raise ValueError(repr(c))

def _euc_jp(c):
    if c <= '\x7f':
        return 1
    elif c == '\x8e' or '\xa1' <= c <= '\xfe':
        return 2
    elif c == '\x8f':
        return 3
    raise ValueError(repr(c))

def _utf_8(c):
    if c <= '\x7f':
        return 1
    elif '\xc0' <= c <= '\xdf':
        return 2
    elif '\xe0' <= c <= '\xef':
        return 3
    elif '\xf0' <= c <= '\xf7':
        return 4
    elif '\xf8' <= c <= '\xfb':
        return 5
    elif '\xfc' <= c <= '\xfd':
        return 6
    raise ValueError(repr(c))

_code = {
        'shift_jis': _shift_jis,
        'euc_jp': _euc_jp,
        'utf_8': _utf_8,
        }

def take_nbytes1(str_, n, code):
    u"""
    文字列 str_ を最大 n バイトに切り詰める

    str_: 8 ビット文字列
    n:    最大のバイト数。これより長い場合、切り詰める
    code: str_ の文字コード ['shift_jis', 'euc_jp', 'utf_8']
    """
    length = 0
    buf = []
    f_code = _code[code]
    i = iter(str_)
    try:
        while True:
            c = i.next()
            width = f_code(c)
            length += width
            if length > n:
                break
            buf.append(c)
            for _ in range(width - 1):
                buf.append(i.next())
    except StopIteration:
        pass
    return ''.join(buf)


def _test():
    def _write(f, code):
        s = u'あいうえおかきくけこさしすせそたちつてと'.encode(code)
        for i in range(1, 40):
            f.write(take_nbytes1(s, i, code))
            f.write('\n')
    f_sjis = open('_sjis.txt', 'w')
    try:
        _write(f_sjis, 'shift_jis')
    finally:
        f_sjis.close()
    f_euc = open('_euc.txt', 'w')
    try:
        _write(f_euc, 'euc_jp')
    finally:
        f_euc.close()
    f_utf8 = open('_utf8.txt', 'w')
    try:
        _write(f_utf8, 'utf_8')
    finally:
        f_utf8.close()

if __name__ == '__main__':
    _test()

2009/2/9 修正、 Shift JIS のコードに過ちあり。 2 バイト文字先頭の条件のひとつめ、 '\x81' <= c <= '\x9f' が '\x81' <= c <= '\x91' となってしまっていたので修正しました。以下、テストコードです。

import split_065
import unittest

class TestCode(unittest.TestCase):
    def testshift_jis(self):
        for i in xrange(0xffff):
            try:
                c = unichr(i).encode('shift_jis')
                self.assertEqual(len(c), split_065._shift_jis(c[0]))
            except UnicodeError:
                pass

    def testeuc_jp(self):
        for i in xrange(0xffff):
            try:
                c = unichr(i).encode('euc_jp')
                self.assertEqual(len(c), split_065._euc_jp(c[0]))
            except UnicodeError:
                pass

    def testutf_8(self):
        for i in xrange(0xffff):
            c = unichr(i).encode('utf_8')
            length = len(c)
            self.assertEqual(len(c), split_065._utf_8(c[0]))

if __name__ == '__main__':
    unittest.main()