Python書式:全角文字の幅問題
いやー、久しぶりにPythonやってみましたよ。
プログラミングたのしいね〜!
今回、print()関数で文字を揃えようと思って、"%-15s"みたいな書式を指定したわけですよ!!
そうしたら、なんと!
全角と半角が混在していると、見た目が崩れてしまう問題がはっせい!!
全角文字幅が1文字扱いのようです!!!
「全角文字も揃えたい」ですよね〜
<<追記>>
完成のつもり版はこっちです。
member.pymember = [ { "name":"あいうえ", "weight":90, "height":177 }, { "name":"Super 真理雄", "weight":250, "height":188 }, { "name":"Ninja", "weight":70, "height":160 }, { "name":"Mr.エックス", "weight":66, "height":167 }, ] for m in member: print("%-15s : %5dKg %5dcm" % (m["name"], m["weight"], m["height"]))
全角文字揃えが崩れちゃうあいうえ : 90Kg 177cm Super 真理雄 : 250Kg 188cm Ninja : 70Kg 160cm Mr.エックス : 66Kg 167cm
困りました!てなわけで、世の中の人に聞いてみると!!
note.nkmk.meさんが教えてくれていました!感謝ですな★
なんと!unicodedata.east_asian_width()を使うと素敵になれるようです!
ただ、この関数は全角・半角というのを直接教えてくれるわけではないのです。
いろいろ複雑な事情があるようです・・・
まあ、そこは良いとして、とにかく "F" "W" "A" なら全角、それ以外なら半角ということで判断しましょう!!
よって、全角文字なら幅をもう1文字分必要とするように引き算してやればOKですな!
member.pyfrom unicodedata import east_asian_width from functools import reduce member = [ { "name":"あいうえ", "weight":90, "height":177 }, { "name":"Super 真理雄", "weight":250, "height":188 }, { "name":"Ninja", "weight":70, "height":160 }, { "name":"Mr.エックス", "weight":66, "height":167 }, ] for m in member: name_w = reduce(lambda a, c: east_asian_width(c) in "FWA" and a-1 or a, m["name"], 15) print("%-*s : %5dKg %5dcm" % (name_w, m["name"], m["weight"], m["height"]))
すばらしい!そろってる!あいうえ : 90Kg 177cm Super 真理雄 : 250Kg 188cm Ninja : 70Kg 160cm Mr.エックス : 66Kg 167cm
じゃじゃ〜ん!!
素敵になりました!!!
forループとか書くとコードが長くなるので、reduce()を使いました。
これは、functoolsモジュールに入ってます。(python2の頃は組み込み関数だったらしい)
reduce()の行はちょっとゴチャッとしてますが、一行でかけるので、コードの流れがスマートになって良いと思います。(自画自賛)
ポイントは、書式幅も引数(タプル)で指定してやることですぞ!
"%-*s"の"*"が肝です!!
あっ!今気づいたけど、サンプルコードの身長と体重の書式を整数にしてしまった・・・
まあいいや。なおすのめんどい・・・・
もうちょっとすすめて、ラムダ式で関数にしてみた。
member.pyfrom unicodedata import east_asian_width from functools import reduce member = [ { "name":"あいうえ", "weight":90, "height":177 }, { "name":"Super 真理雄", "weight":250, "height":188 }, { "name":"Ninja", "weight":70, "height":160 }, { "name":"Mr.エックス", "weight":66, "height":167 }, ] withw = lambda s,w:(reduce(lambda a, c: east_asian_width(c) in "FWA" and a-1 or a, s, w), s) for m in member: print("%-*s : %5dKg %5dcm" % (*withw(m["name"], 15) , m["weight"], m["height"]))
このほうが使いやすいな!
ポイントは、このwithw()関数 がタプル ( 文字幅 , 文字列 ) を返すので、*で展開してやるのだ!
う〜ん。まんぞく!
めでたしめでたし!!
<追記>
いやいや、よく考えたら、幅指定を使わなくても、文字幅分をスペースで埋めとけば良かったんじゃね?
アホでした。
というわけで。。。
member.pyfrom unicodedata import east_asian_width from functools import reduce member = [ { "name":"あいうえ", "weight":90, "height":177 }, { "name":"Super 真理雄", "weight":250, "height":188 }, { "name":"Ninja", "weight":70, "height":160 }, { "name":"Mr.エックス", "weight":66, "height":167 }, ] padstrl = lambda s,w: reduce(lambda a, c: c+a[:east_asian_width(c) in "FWA" and -2 or -1], s[::-1], " "*w) padstrr = lambda s,w: reduce(lambda a, c: a[east_asian_width(c) in "FWA" and 2 or 1:]+c, s, " "*w) for m in member: print(f'{padstrl(m["name"], 15)} : {m["weight"]:5}Kg {m["height"]:5}cm') print("-"*50) for m in member: print(f'{padstrr(m["name"], 15)} : {m["weight"]:5}Kg {m["height"]:5}cm')
前のwithw()関数の場合はPythonの書式の方で左寄せと右寄せを指定できたけど、
今回のラムダ式で左寄せと右寄せを一つの式にするのは、
俺の頭では短くかけないので、2つに分けた!
<追記>→なんとか書けたよ!
ちなみに、f"" 書式のこともすっかり忘れてました(汗)
コメント
コメントを投稿