M.Hiroi's Home Page

Algorithms with Python

Prediction by Partial Matching (PPM) [2]

[ PrevPage | Python | NextPage ]

はじめに

前回は Method C のプログラムを作ってファイルを圧縮してみました。今回は前回作成したプログラムを改良してみましょう。最初にエスケープ確率を Method D に変更します。そして、メモリを使い切ったときの対応として「LRU スキーム」を導入します。

●Method D

Method D はとても簡単にプログラムできます。記号を符号化・復号するときに、記号の増分値を 2 倍にすればいいわけです。これは出現頻度表を更新するメソッド update() の引数に同じ記号を渡すだけで実現できます。

それからもう一つ、記号の増分値 INC を 1.5 倍に増やした Method D' という方法を試してみました。この方法は、メソッド update() で引数 c0 の出現頻度を増やすとき、self.inc + self.inc / 2 とするだけで実現できます。エスケープ記号の増分値は self.inc のままです。

それでは、実際に Canterbury Corpus で配布されているテストデータ The Canterbury Corpus を圧縮してみましょう。結果は次にようになりました。

        表 : PPM の評価結果 (節の個数 100000, INC = +4)

                                  Method
ファイル名      サイズ     C        D        D'      LHA     bzip2
--------------------------------------------------------------------
alice29.txt    152,089   42,585   41,671   41,095   59,117   43,202
asyoulik.txt   125,179   39,405   38,646   38,310   52,341   39,569
cp.html         24,603    7,117    7,023    6,998    8,384    7,624
fields.c        11,150    3,008    2,933    2,860    3,170    3,039
grammar.lsp      3,721    1,141    1,127    1,107    1,271    1,283
kennedy.xls  1,029,744  184,738  170,625  169,728  198,342  130,280
lcet10.txt     426,754  106,399  103,788  102,287  159,558  107,706
plrabn12.txt   481,861  143,284  140,254  138,673  210,045  145,577
ptt5           513,216   51,339   50,731   51,309   52,305   49,759
sum             38,240   13,044   12,729   12,641   13,993   12,909
xargs.1          4,227    1,592    1,570    1,561    1,778    1,762
--------------------------------------------------------------------
合計         2,810,784  593,652  571,097  566,569  760,304  542,710

結果を見ると、PPM の圧縮率はエスケープ確率によって大きく左右されることがよくわかります。今回のプログラムでは Method D' の圧縮率が一番高くなりました。特に、テキストファイルとの相性が良いようです。テキストファイルの場合、出現する記号の種類は限られているので、エスケープ確率はそれほど大きくないはずです。記号数の増分値をエスケープ記号よりも増やすことで、エスケープ確率は少し低下するので、テキストファイルの圧縮率が高くなったと思われます。逆に、バイナリファイルでは圧縮率が低下する場合もあります。

今回はテストデータに The Canterbury Corpus だけしか使っていないので、興味のある方はいろいろなデータで試してみてください。

●記号の増分値の変更

ところで、有限文脈モデルの場合、高次モデルで記号の増分値を増やすと、圧縮率が向上する場合があります。PPM の場合でも効果があるかもしれません。実際に試してみましょう。プログラムの修正は簡単です。クラス Trie のメソッド get_freq() で新しい出現頻度表を生成するとき、記号の増分値を次のように設定します。

freq = FreqEsc(INC + 4 * x, LIMIT)

x は次数 (order) - 1 です。この場合、order-2, 3, 4, 5 の増分値は 8, 12, 16, 20 になります。4 を 6 と 8 に増やして試してみたところ、結果は次のようになりました。

        表 : PPM の評価結果 (節の個数 100000, Method D')

ファイル名      サイズ    4 * x    6 * x    8 * x    LHA     bzip2
--------------------------------------------------------------------
alice29.txt    152,089   41,033   41,030   41,027   59,117   43,202
asyoulik.txt   125,179   38,363   38,371   38,376   52,341   39,569
cp.html         24,603    6,986    6,988    6,989    8,384    7,624
fields.c        11,150    2,835    2,834    2,833    3,170    3,039
grammar.lsp      3,721    1,099    1,099    1,099    1,271    1,283
kennedy.xls  1,029,744  149,502  145,031  141,109  198,342  130,280
lcet10.txt     426,754  102,069  102,065  102,074  159,558  107,706
plrabn12.txt   481,861  138,716  138,741  138,765  210,045  145,577
ptt5           513,216   51,735   51,936   52,144   52,305   49,759
sum             38,240   12,567   12,557   12,540   13,993   12,909
xargs.1          4,227    1,557    1,557    1,557    1,778    1,762
--------------------------------------------------------------------
合計         2,810,784  546,480  542,209  538,513  760,304  542,710

高次モデルの増分値を増やすことで、kennedy.xls の圧縮率は大幅に向上しました。その他のファイルでは、圧縮率が少し良くなる場合もありますし、逆に少し悪くなる場合もあります。有限文脈モデルのように、大きな効果はないようです。それでも、kennedy.xls の圧縮率は向上するので、ほかにも効果があるファイルがあるかもしれません。興味のある方はいろいろなデータで試してみてください。

●LRU スキーム

今まで作成したプログラムは、メモリを使い切った時点で有限文脈モデル (トライ) を固定して、あとはそのままのモデルを使ってデータを圧縮しました。ところが、この方法では不都合な場合もあります。

たとえば、tar などのアーカイバで複数のファイルをひとつにまとめた場合、データの種類が途中で変化することがあります。この場合、モデルを固定するとデータの変化についていけないので、圧縮率が低下することが予想されます。そこで、実際に The Canterbury Corpus の 11 ファイルを tar でまとめたファイルを圧縮してみました。結果は次のようになりました。

        表 : canterbury.tar の評価結果

ファイル名    サイズ    圧縮後   符号化    復号
-------------------------------------------------
Canterbury  2,821,120  722,397   37.444   55.701

符号化と復号の単位 : 秒
実行環境 : Python 3.8.10, Ubuntu 20.04 LTS (WSL1), Intel Core i5-6200U 2.30GHz

圧縮率は大きく低下すると思っていたのですが、予想以上に圧縮率が良かったのには驚きました。それでも、データの変化に対応できれば、もう少し圧縮率は良くなるはずです。そこで、メモリを使い切ったときの対応に「LRU スキーム」を導入してみましょう。

LRU スキームは、長い間使われていない (最長時間未使用 : Least Recently Used) 節を取り除くことで、トライに空きスペースを作る操作のことです。PPM に LRU スキームを適用すると、符号化と復号に時間がかかることになりますが、少ないメモリでも高い圧縮率を期待することができます。また、データの局所的な偏在も反映することが可能になります。

LRU スキームは LZT 符号のときに「キュー」を使って実装しました。今回のプログラムも LZT 符号のときに作成したキュー (Queue) を使うことにします。詳しい説明は拙作のページ LZT 符号 をお読みくださいませ。

●プログラムの作成

それではプログラムを作りましょう。次のリストを見てください。

リスト : トライの定義

class Trie:
    def __init__(self):
        self.sym = [None] * TRIE_SIZE
        self.freq = [None] * TRIE_SIZE
        self.parent = [None] * TRIE_SIZE
        self.child = [0] * TRIE_SIZE
        self.ht = {}
        for x in range(256):
            self.sym[x] = x
            self.freq[x] = FreqEsc(INC, LIMIT)  # order-1
        self.num = 256

トライから節を削除する場合、その親節が必要になります。このため、親節を表す配列 parent を追加します。ただし、削除できる節は「葉」だけであり、トライの途中から節を削除することはできません。ハッシュを使ってトライを実装する場合、節が「葉」なのかすぐに判別することができません。そこで、子の個数を配列 child で管理します。たとえば、n に子を追加するときは child[n] を +1 します。n の子を削除するときは child[n] を -1 します。child[n] が 0 の場合、n は葉であることがすぐにわかります。

次は、キューの先頭から葉を探すメソッド search_leaf() と削除するメソッド delete() を作ります。

リスト : 葉の探索と削除

    # 葉を探す
    def search_leaf(self, que):
        for x in que.traverse():
            if self.child[x] == 0: return x
        return None

    # 削除
    def delete(self, n):
        c = self.sym[n]
        p = self.parent[n]
        del self.ht[(p, c)]
        self.child[p] -= 1

search_leaf() の引数 que は LRU スキームで使用するキューです。葉を探す処理は簡単です。キューのメソッド traverse() を使って、節 x を取り出します。child[x] が 0 ならば return で x を返します。見つからない場合は None を返していますが、必ず見つかるはずなので、raise でエラーを送出してもいいでしょう。

delete() の引数 n は削除する節です。まず、parent[n] と sym[n] から親節と記号を求めて、変数 p, c にセットします。そして、ハッシュ ht からキー (p, c) を削除して、child[p] を -1 します。これでトライから節 n を削除することができます。

最後に、出現頻度表を取得するメソッド get_freq() を修正します。

リスト : 出現頻度表の取得

    def get_freq(self, buff, prev_code, que):
        n = prev_code[0]
        buff.append(self.freq[n])       # order-1 をセット
        for x in range(1, MAX_ORDER):
            c = prev_code[x]
            m = self.search(n, c)
            if m is None:
                # Trie に追加する
                m = self.num
                if m >= TRIE_SIZE:
                    m = self.search_leaf(que)
                    self.delete(m)
                    que.delete(m)
                else:
                    self.num += 1
                freq = FreqEsc(INC + 8 * x, LIMIT)
                self.sym[m] = c
                self.parent[m] = n
                self.freq[m] = freq
                self.child[m] = 0
                self.child[n] += 1
                self.ht[(n, c)] = m
                que.insert(m)
            else:
                freq = self.freq[m]
                que.delete(m)
                que.insert(m)
            # 出現頻度表を追加する
            buff.append(freq)
            n = m

引数 que は LRU スキーム用のキューです。新しい節をトライに追加するとき、self.num >= TRIE_SIZE の場合は空きエリアがありません。もっとも使われていない「葉」を search_leaf() で探します。そして、見つけた葉 m をトライとキューから削除します。出現頻度表は新しく生成して freq にセットします。あとは節 m をトライとキューに追加するだけです。

節を見つけた場合、その節をメソッド delete() で双方向リストから削除し、メソッド insert() で双方向リストの最後尾へ移動します。これで節を LRU スキームで管理することができます。

あとはとくに難しいところはないでしょう。詳細は プログラムリスト1 をお読みくださいませ。

●評価結果

それでは、実際にファイルを圧縮してみましょう。最初に The Canterbury Corpus をアーカイバ tar でまとめた canterbury.tar の結果を表に示します。

        表 : canterbury.tar の評価結果

PROG        サイズ    圧縮後   符号化    復号
-----------------------------------------------
PPM       2,821,120  722,397   37.444   55.701
PPM(LRU)  2,821,120  532,448   56.944   58.584


符号化と復号の単位 : 秒
実行環境 : Python 3.8.10, Ubuntu 20.04 LTS (WSL1), Intel Core i5-6200U 2.30GHz

LRU スキームの方が高い圧縮率になりました。LRU スキームは PPM でも大きな効果を発揮していることがわかります。次は The Canterbury Corpus の評価結果を表に示します。

                表 : PPM の評価結果
         (節の個数 100000, LRU スキーム, Method D')

ファイル名      サイズ    PPM      LHA     bzip2    符号化  復号
------------------------------------------------------------------
alice29.txt    152,089   41,027   59,117   43,202   3.402   3.687
asyoulik.txt   125,179   38,376   52,341   39,569   3.069   3.419
cp.html         24,603    6,989    8,384    7,624   0.718   0.779
fields.c        11,150    2,833    3,170    3,039   0.275   0.317
grammar.lsp      3,721    1,099    1,271    1,283   0.115   0.125
kennedy.xls  1,029,744  128,740  198,342  130,280  24.109  25.998
lcet10.txt     426,754  101,808  159,558  107,706   7.901   8.608
plrabn12.txt   481,861  138,771  210,045  145,577   9.282   9.990
ptt5           513,216   52,126   52,305   49,759   7.156   8.030
sum             38,240   12,540   13,993   12,909   1.345   1.407
xargs.1          4,227    1,557    1,778    1,762   0.153   0.177
------------------------------------------------------------------
合計         2,810,784  525,866  760,304  542,710  57.525  62.537


符号化と復号の単位 : 秒
実行環境 : Python 3.8.10, Ubuntu 20.04 LTS (WSL1), Intel Core i5-6200U 2.30GHz

LRU スキームの効果により kennedy.xls の圧縮率は大幅に向上しました。LRU スキームの効果はとても高いことがわかります。そうはいっても、バイナリファイルが苦手であることに変わりはありませんね。また、符号化・復号の処理も大変遅くなりました。LRU スキームを行っているので、その分だけ時間がかかるのは仕方がないでしょう。

それでは、LRU スキームを適用さえすれば、節の個数は少なくても高い圧縮率になるのでしょうか。これはファイルの大きさによって変わると思われます。そこで、The Canterbury Corpus (11 ファイル) を tar でまとめたファイル Canterbury と The Large Corpus を圧縮してみました。実行結果は次のようになりました。

      表 : PPM の評価結果 (LRU スキーム, Method D')

                                     節の個数
ファイル名      サイズ      50,000    100,000    150,000
---------------------------------------------------------
bible.txt     4,047,392    805,297    793,452    792,699
Canterbury    2,821,120    535,665    532,448    533,234
E.coli        4,638,690  1,129,469  1,129,469  1,129,469
world192.txt  2,473,400    502,988    474,209    464,949
---------------------------------------------------------
合計         13,980,602  2,973,419  2,929,578  2,920,351

大きなテキストファイル (bible.txt, world192.txt) の場合、節の個数を増やすと圧縮率は向上しました。ところが Canterbury のように、節の個数を増やしても圧縮率が向上しない場合もあります。また、E.coli は記号の種類が少ないデータで、使用する節の個数はとても少なく、PPM では高い圧縮率になります。PPM と相性の良いデータといえるでしょう。

PPM の場合、ある程度のメモリを確保できれば、LRU スキームを適用することにより、大きなファイルでも高い圧縮率を達成することができるようです。それにしても、基本的な PPM だけでここまで高い圧縮率を達成できるとは驚きました。PPM の改良版である PPM* を使った圧縮ツールが、極めて高い圧縮率をたたき出しているのも納得できます。

今回のプログラムは、テキストファイルであれば bzip2 を越える圧縮率を達成していますが、ブロックソートも負けてはいません。ブロックソートの圧縮率はバッファサイズで大きく変化します。そこで、拙作のページ ブロックソート [2] で作成した「ブロックソート 0-1-2 coding 混合法」でファイルを圧縮してみました。結果は次のようになりました。

        表 : ブロックソートの評価結果
    (bsrc = BlockSorting + MTF-2 + 0-1-2 coding 混合法)

                               PPM
  ファイル名      サイズ    (150,000)    bzip2      bsrc
  ---------------------------------------------------------
  bible.txt     4,047,392    792,699    845,623    760,899
  Canterbury    2,821,120    533,234    570,856    519,253
  e.coli        4,638,690  1,129,469  1,251,004  1,158,098
  world192.txt  2,473,400    464,969    489,583    413,097
  ---------------------------------------------------------
  合計         13,980,602  2,920,351  3,157,066  2,851,347

e.coli の圧縮率は PPM にかないませんが、それ以外のファイルは PPM よりも高い圧縮率になりました。ブロックソート法も優秀な圧縮アルゴリズムであることがよくわかります。ただし、バッファサイズを大きくするとブロックソートに時間がかかるようになります。それでも、今回のテストでは PPM よりも高速です。

なお、実行時間の結果は M.Hiroi のコーディング、実行したマシン、プログラミング言語などの環境に大きく依存しています。また、これらの環境だけではなく、データの種類によっても実行時間はかなり左右されます。興味のある方は、いろいろなデータをご自分の環境で試してみてください。

●参考文献, URL

  1. 儘田真吾, 『PPM系列の符号化法について』, (リンク切れ)
  2. 広井誠, 『PPM によるファイルの圧縮 (前編)』, Interface 2005 年 3 月号, CQ出版社
  3. 広井誠, 『PPM によるファイルの圧縮 (後編)』, Interface 2005 年 4 月号, CQ出版社

●プログラムリスト1

#
# ppmfreq.py : PPM 用の出現頻度表
#
#              Copyright (C) 2007-2022 Makoto Hiroi
#
from rangecoder import *

# 定数
GR = 16
ESC = 256
CHAR_SIZE = 257

# ESC 記号付き出現頻度表
class FreqEsc:
    def __init__(self, inc, limit):
        self.sym = []
        self.inc = inc
        self.limit = limit
        self.count = [0] * CHAR_SIZE
        self.count[ESC] = 1
        self.count_group = [0] * (CHAR_SIZE // GR + 1)
        self.count_group[ESC // GR] = 1
        self.sum_ = 1

    # 出現頻度表の更新
    def update(self, c0, c1 = None):
        a = self.inc + self.inc // 2  # Method D'
        self.count[c0] += a
        self.count_group[c0 // GR] += a
        self.sum_ += a
        if c1 is not None:
            self.count[c1] += self.inc
            self.count_group[c1 // GR] += self.inc
            self.sum_ += self.inc
        if self.sum_ >= self.limit:
            n = 0
            for x in range(len(self.count_group)):
                self.count_group[x] = 0
            for x in range(len(self.count)):
                if self.count[x] > 0:
                    self.count[x] = (self.count[x] >> 1) | 1
                    self.count_group[x // GR] += self.count[x]
                    n += self.count[x]
            self.sum_ = n

    # 符号化
    def encode(self, rc, c, exclusion):
        # 記号の累積度数を求める
        def cumul(c):
            n = 0
            for x in range(c // GR): n += count_group[x]
            for x in range((c // GR)*GR, c):
                if x not in exclusion: n += self.count[x]
            return n

        # exclusion の処理
        count_group = self.count_group[:]
        sum_ = self.sum_
        for x in exclusion:
            count_group[x // GR] -= self.count[x]
            sum_ -= self.count[x]
        #
        temp = rc.range_ // sum_
        rc.low += cumul(c) * temp
        rc.range_ = self.count[c] * temp
        rc.encode_normalize()

    # 復号
    def decode(self, rc, exclusion):
        # 記号の探索
        def search_code(value):
            n = 0
            for x in range(len(count_group)):
                if value < n + count_group[x]: break
                n += count_group[x]
            for c in range(x*GR, len(self.count)):
                if c not in exclusion:
                    if value < n + self.count[c]: break
                    n += self.count[c]
            return c, n

        # exclusion の処理
        count_group = self.count_group[:]
        sum_ = self.sum_
        for x in exclusion:
            count_group[x // GR] -= self.count[x]
            sum_ -= self.count[x]
        #
        temp = rc.range_ // sum_
        c, num = search_code(rc.low // temp)
        rc.low -= temp * num
        rc.range_ = temp * self.count[c]
        rc.decode_normalize()
        return c


# order-(-1) 用の出現頻度表
class Freq(FreqEsc):
    def __init__(self, inc, limit):
        self.inc = inc
        self.limit = limit
        self.count = [1] * (CHAR_SIZE - 1)
        self.count_group = [GR] * ((CHAR_SIZE - 1) // GR)
        self.sum_ = CHAR_SIZE - 1
#
# ppm_lru.py : PPM のテスト
#
#              Copyright (C) 2007-2022 Makoto Hiroi
#
import argparse, os.path, time
from rangecoder import *
from ppmfreq import *

# 定数
MAX_ORDER = 5
INC = 4
LIMIT = 0x4000
TRIE_SIZE = 150000
HEAD = 0

# 4 バイトの正整数値を出力
def write_number(num, fout):
    fout.putc((num >> 24) & 0xff)
    fout.putc((num >> 16) & 0xff)
    fout.putc((num >> 8) & 0xff)
    fout.putc(num & 0xff)

# 4 バイトの正整数値を入力
def read_number(fin):
    num = 0
    for _ in range(4):
        num = (num << 8) + fin.getc()
    return num

##### 双方向リストによるキュー #####

class Queue:
    def __init__(self):
        # 0 - 255 はキューに登録しない
        self.prev = [None] * TRIE_SIZE
        self.next_ = [None] * TRIE_SIZE
        self.prev[HEAD] = HEAD
        self.next_[HEAD] = HEAD

    # 最後尾に追加
    def insert(self, x):
        last = self.prev[HEAD]
        self.prev[x] = last
        self.next_[x] = HEAD
        self.next_[last] = x
        self.prev[HEAD] = x

    # 削除
    def delete(self, x):
        p = self.prev[x]
        q = self.next_[x]
        self.next_[p] = q
        self.prev[q] = p

    # 巡回
    def traverse(self):
        n = self.next_[HEAD]
        while n != HEAD:
            yield n
            n = self.next_[n]


##### Trie #####

class Trie:
    def __init__(self):
        self.sym = [None] * TRIE_SIZE
        self.freq = [None] * TRIE_SIZE
        self.parent = [None] * TRIE_SIZE
        self.child = [0] * TRIE_SIZE
        self.ht = {}
        for x in range(256):
            self.sym[x] = x
            self.freq[x] = FreqEsc(INC, LIMIT)  # order-1
        self.num = 256

    # 子の探索
    def search(self, n, c):
        key = (n, c)
        if key in self.ht: return self.ht[key]
        return None

    # 葉を探す
    def search_leaf(self, que):
        for x in que.traverse():
            if self.child[x] == 0: return x
        return None

    # 削除
    def delete(self, n):
        c = self.sym[n]
        p = self.parent[n]
        del self.ht[(p, c)]
        self.child[p] -= 1

    # 出現頻度表の取得
    def get_freq(self, buff, prev_code, que):
        n = prev_code[0]
        buff.append(self.freq[n])       # order-1 をセット
        for x in range(1, MAX_ORDER):
            c = prev_code[x]
            m = self.search(n, c)
            if m is None:
                # Trie に追加する
                m = self.num
                if m >= TRIE_SIZE:
                    m = self.search_leaf(que)
                    self.delete(m)
                    que.delete(m)
                else:
                    self.num += 1
                freq = FreqEsc(INC + 8 * x, LIMIT)
                self.sym[m] = c
                self.parent[m] = n
                self.freq[m] = freq
                self.child[m] = 0
                self.child[n] += 1
                self.ht[(n, c)] = m
                que.insert(m)
            else:
                freq = self.freq[m]
                que.delete(m)
                que.insert(m)
            # 出現頻度表を追加する
            buff.append(freq)
            n = m

##### 出現頻度表 #####
trie = Trie()                  # order-1 - MAX_ORDER
freq0 = FreqEsc(INC, LIMIT)    # order-0
freq_1 = Freq(INC, LIMIT)      # order-(-1)

# ファイルの読み込み
def read_file(fin):
    while True:
        c = fin.getc()
        if c is None: break
        yield c

# PPM による符号化
def encode(fin, fout):
    rc = RangeCoder(fout, ENCODE)
    que = Queue()
    prev_code = [0] * MAX_ORDER
    exclusion = set()
    for x in read_file(fin):
        exclusion.clear()
        buff = [freq_1, freq0]
        trie.get_freq(buff, prev_code, que)
        for i in range(MAX_ORDER + 1, -1, -1):
            freq = buff[i]
            if freq.count[x] == 0:
                freq.encode(rc, ESC, exclusion)
                freq.update(x, ESC)
                exclusion.update(freq.sym)
                freq.sym.append(x)
            else:
                freq.encode(rc, x, exclusion)
                freq.update(x, x)
                break
        #
        prev_code.pop()
        prev_code.insert(0, x)
    rc.finish()

# PPM による復号
def decode(fin, fout, size):
    rc = RangeCoder(fin, DECODE)
    que = Queue()
    prev_code = [0] * MAX_ORDER
    exclusion = set()
    for _ in range(size):
        exclusion.clear()
        buff = [freq_1, freq0]
        trie.get_freq(buff, prev_code, que)
        for i in range(MAX_ORDER + 1, -1, -1):
            freq = buff[i]
            c = freq.decode(rc, exclusion)
            if c == ESC:
                exclusion.update(freq.sym)
            else:
                for x in range(MAX_ORDER + 1, i, -1):
                    buff[x].update(c, ESC)
                    buff[x].sym.append(c)
                freq.update(c, c)
                break
        #
        fout.putc(c)
        prev_code.pop()
        prev_code.insert(0, c)

# 符号化
def encode_file(name1, name2):
    size = os.path.getsize(name1)
    with ByteIO(name1, ROPEN) as fin, ByteIO(name2, WOPEN) as fout:
        write_number(size, fout)
        if size > 0: encode(fin, fout)

# 復号
def decode_file(name1, name2):
    with ByteIO(name1, ROPEN) as fin, ByteIO(name2, WOPEN) as fout:
        size = read_number(fin)
        if size > 0: decode(fin, fout, size)

# オプション解析
parser = argparse.ArgumentParser(description='PPM によるファイルの圧縮')
parser.add_argument('name1', help='入力ファイル')
parser.add_argument('name2', help='出力ファイル')
parser.add_argument('-e', '--encode', action='store_true', help='符号化')
parser.add_argument('-d', '--decode', action='store_true', help='復号')
args = parser.parse_args()

# 実行
s = time.time()
if args.encode and not args.decode:
    encode_file(args.name1, args.name2)
elif args.decode:
    decode_file(args.name1, args.name2)
else:
    print('option error')
e = time.time()
print('{:.3f}'.format(e - s))

初版 2007 年 9 月 2 日
改訂 2022 年 10 月 2 日

Copyright (C) 2007-2022 Makoto Hiroi
All rights reserved.

[ PrevPage | Python | NextPage ]