2019/SEP/19
毎回、同じようにつまづいて、同じような感じで対応してます。
なので、いいかげん自分に判りやすいようにまとめておきます。
日本語対応といっても、ソースコード中に日本語を書くつもりはありません。
データはYAML形式のファイルで用意。
そして、YAML形式データファイル中の文字列を、UTF-8で日本語に置き換えようとして、 毎回「うぅっ」とうなってます。
Python2 では日本語を含む文字列は .encode('utf-8')して UTF-8 で保持。
Python3 では日本語の有無に関係なく、素直にstrのままでよし。
Python2 では素直に sys.stdin/sys.stdout を read/write。
Python3 では sys.stdin.buffer , sys.stdout.buffer を使う。
Python2 の世界には .buffer は存在しないので注意。
Python2 では素直に 'r', 'w'モードでopen()。
Python3 では 'rb', 'wb' を使う。
Python2 でも 'rb', 'wb' は使えるので、'rb', 'wb'にしておけば良いかも。
.encode('utf-8') で UTF-8 にして send()
recv() した UTF-8、Python2 なら、そのまま .format(), .join() できる。
recv() した UTF-8、Python3 なら .decode('utf-8') で str に。
UTF-8のテキストやファイルは、Python2, 3ともに yaml.load()できる。
ただし、Python3でファイルのオープンは'rb'モードで。
yaml.load()した結果のデータは、Python2では、日本語を含む文字列はunicode型、それ以外はstr型。
yaml.load()した結果のデータは、Python3では、全てstr型 (実体はunicode)。
yaml.dump()のオプションにはallow_unicode=Trueを指定する。
yaml.dump()に渡すデータは、Python2では、日本語を含む文字列はuniocde型、それ以外はstr型にしておくと、 !!python/xxx の記述が出ない。
dump()結果のYAMLデータはUTF-8エンコーディング。
yaml.dump()に渡すデータは、Python3では、全てstr型 (実体unicode) で良い。
dump()結果のYAMLデータはunicode。(dump()のオプション指定特になければ) .encode('utf-8')してからファイルに'wb'で書き込むべし。
自分のプログラムでよく使う、 ありがちな処理だけ対応できれば良いので...
まずはこのようなプログラムから
p2.py
#!/usr/bin/env python2
import sys
import yaml
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False)
sys.stdout.write(s)
# EOF
データ
dat.yaml
foo: hello
bar: world
実行してみます。
$ ./p2.py < dat.yaml > res.yaml $ cat res.yaml bar: world foo: hello fuga: foo=hello bar=world guha: hello(^_^)world(^_^)foo=hello bar=world(^_^)hello world hoge: hello world
ではここで、データファイルに日本語をば。
dat_u8.yaml
foo: こんちには
bar: 世界
エンコーディングはUTF-8
$ nkf -g dat_u8.yaml UTF-8 $ hd dat_u8.yaml 00000000 66 6f 6f 3a 20 e3 81 93 e3 82 93 e3 81 a1 e3 81 |foo: ...........| 00000010 ab e3 81 af 0a 62 61 72 3a 20 e4 b8 96 e7 95 8c |.....bar: ......| 00000020 0a |.| 00000021
実行してみます。
$ ./p2.py < dat_u8.yaml > res_u8.yaml Traceback (most recent call last): File "./p2.py", line 12, in <module> d['fuga'] = 'foo={} bar={}'.format(foo, bar) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
文字列の.format()による展開というか変換で、 fooやbarが保持してる値の中に128以上のものがあると、おっしゃる。
そらありますわな。
文字列のtypeを表示してみます。
p22.py
#!/usr/bin/env python2
import sys
import yaml
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
foo = d.get('foo')
bar = d.get('bar')
sys.stderr.write( '{}\n'.format( type(foo) ) )
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False)
sys.stdout.write(s)
# EOF
$ diff -u p2.py p22.py --- p2.py 2019-09-18 23:14:28.431519000 +0900 +++ p22.py 2019-09-18 23:25:38.204708000 +0900 @@ -8,6 +8,7 @@ d = yaml.load(s) foo = d.get('foo') bar = d.get('bar') + sys.stderr.write( '{}\n'.format( type(foo) ) ) d['hoge'] = foo + ' ' + bar d['fuga'] = 'foo={} bar={}'.format(foo, bar) d['guha'] = '(^_^)'.join( d.values() )
$ ./p22.py < dat_u8.yaml <type 'unicode'> Traceback (most recent call last): File "./p22.py", line 13, in <module> d['fuga'] = 'foo={} bar={}'.format(foo, bar) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
strでなくてunicode
データファイルはUTF-8。
標準入力経由で読み込んで、yaml.load()でデータに仕立てられるとunicode。
unicodeからUTF-8にエンコードしなおしてみます。
p23.py
#!/usr/bin/env python2
import sys
import yaml
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
d['foo'] = d.get('foo').encode('utf-8')
d['bar'] = d.get('bar').encode('utf-8')
foo = d.get('foo')
bar = d.get('bar')
sys.stderr.write( '{}\n'.format( type(foo) ) )
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False)
sys.stdout.write(s)
# EOF
$ diff -u p22.py p23.py --- p22.py 2019-09-18 23:25:38.204708000 +0900 +++ p23.py 2019-09-18 23:31:48.975535000 +0900 @@ -6,6 +6,10 @@ if __name__ == "__main__": s = sys.stdin.read() d = yaml.load(s) + + d['foo'] = d.get('foo').encode('utf-8') + d['bar'] = d.get('bar').encode('utf-8') + foo = d.get('foo') bar = d.get('bar') sys.stderr.write( '{}\n'.format( type(foo) ) )
$ ./p23.py < dat_u8.yaml <type 'str'> bar: !!python/str "\u4E16\u754C" foo: !!python/str "\u3053\u3093\u3061\u306B\u306F" fuga: !!python/str "foo=\u3053\u3093\u3061\u306B\u306F bar=\u4E16\u754C" guha: !!python/str "\u3053\u3093\u3061\u306B\u306F(^_^)\u4E16\u754C(^_^)foo=\u3053\ \u3093\u3061\u306B\u306F bar=\u4E16\u754C(^_^)\u3053\u3093\u3061\u306B\u306F \u4E16\ \u754C" hoge: !!python/str "\u3053\u3093\u3061\u306B\u306F \u4E16\u754C"
エラーは出なくなりました。
UTF-8にエンコードするとtypeの表示はstrに。
unicodeだと、.format() でエラーでしたが、strなら問題なさそうです。
yaml.dump()によるYAML形式のデータには !!python/str の文字が。
yaml.dump()する前に、デコードしてunicodeに戻してみます。
p24.py
#!/usr/bin/env python2
import sys
import yaml
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
d['foo'] = d.get('foo').encode('utf-8')
d['bar'] = d.get('bar').encode('utf-8')
foo = d.get('foo')
bar = d.get('bar')
sys.stderr.write( '{}\n'.format( type(foo) ) )
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) )
s = yaml.dump(d, default_flow_style=False)
sys.stdout.write(s)
# EOF
$ diff -u p23.py p24.py --- p23.py 2019-09-18 23:31:48.975535000 +0900 +++ p24.py 2019-09-18 23:33:19.082966000 +0900 @@ -16,6 +16,9 @@ d['hoge'] = foo + ' ' + bar d['fuga'] = 'foo={} bar={}'.format(foo, bar) d['guha'] = '(^_^)'.join( d.values() ) + + d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) ) + s = yaml.dump(d, default_flow_style=False) sys.stdout.write(s) # EOF
$ ./p24.py < dat_u8.yaml <type 'str'> bar: "\u4E16\u754C" foo: "\u3053\u3093\u3061\u306B\u306F" fuga: "foo=\u3053\u3093\u3061\u306B\u306F bar=\u4E16\u754C" guha: "\u3053\u3093\u3061\u306B\u306F(^_^)\u4E16\u754C(^_^)foo=\u3053\u3093\u3061\u306B\ \u306F bar=\u4E16\u754C(^_^)\u3053\u3093\u3061\u306B\u306F \u4E16\u754C" hoge: "\u3053\u3093\u3061\u306B\u306F \u4E16\u754C"
!!python/str の文字は消えました。
文字列のデータは \uxxxx の形式です。
yaml.dump()のオプションを調べると、allow_unicode とうそれらしいものが。
p25.py
#!/usr/bin/env python2
import sys
import yaml
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
d['foo'] = d.get('foo').encode('utf-8')
d['bar'] = d.get('bar').encode('utf-8')
foo = d.get('foo')
bar = d.get('bar')
sys.stderr.write( '{}\n'.format( type(foo) ) )
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p24.py p25.py --- p24.py 2019-09-18 23:33:19.082966000 +0900 +++ p25.py 2019-09-18 23:39:00.230364000 +0900 @@ -19,6 +19,6 @@ d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) ) - s = yaml.dump(d, default_flow_style=False) + s = yaml.dump(d, default_flow_style=False, allow_unicode=True) sys.stdout.write(s) # EOF
$ ./p25.py < dat_u8.yaml <type 'str'> bar: 世界 foo: こんちには fuga: foo=こんちには bar=世界 guha: こんちには(^_^)世界(^_^)foo=こんちには bar=世界(^_^)こんちには 世界 hoge: こんちには 世界 $ ./p25.py < dat_u8.yaml > res_u8.yaml <type 'str'> $ nkf -g res_u8.yaml UTF-8
お望みの形式で出力できました。
エンコーディングは確かにUTF-8です。
日本語を含まない最初のデータで確認を。
$ ./p25.py < res_u8.yaml <type 'str'> Traceback (most recent call last): File "./p25.py", line 18, in <module> d['guha'] = '(^_^)'.join( d.values() ) UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)
おっと、これはプログラム側の問題。
出力側のように map() で一括変換しておきます。
p26.py
#!/usr/bin/env python2
import sys
import yaml
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
foo = d.get('foo')
bar = d.get('bar')
sys.stderr.write( '{}\n'.format( type(foo) ) )
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p25.py p26.py --- p25.py 2019-09-18 23:39:00.230364000 +0900 +++ p26.py 2019-09-19 00:08:30.805695000 +0900 @@ -7,8 +7,7 @@ s = sys.stdin.read() d = yaml.load(s) - d['foo'] = d.get('foo').encode('utf-8') - d['bar'] = d.get('bar').encode('utf-8') + d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) ) foo = d.get('foo') bar = d.get('bar')
$ ./p26.py < res_u8.yaml <type 'str'> bar: 世界 foo: こんちには fuga: foo=こんちには bar=世界 guha: こんちには 世界(^_^)こんちには(^_^)世界(^_^)foo=こんちには bar=世界(^_^)こんちには(^_^)世界(^_^)foo=こんちには bar=世界(^_^)こんちには 世界 hoge: こんちには 世界
日本語は問題なく。
元のアルファベットだけのデータで
$ ./p26.py < dat.yaml <type 'str'> bar: !!python/unicode 'world' foo: !!python/unicode 'hello' fuga: !!python/unicode 'foo=hello bar=world' guha: !!python/unicode 'hello(^_^)world(^_^)foo=hello bar=world(^_^)hello world' hoge: !!python/unicode 'hello world'
!!python/uniocde がついてます。
出力前の変換でtypeがどうなってるか確認してみます。
p27.py
#!/usr/bin/env python2
import sys
import yaml
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
foo = d.get('foo')
bar = d.get('bar')
sys.stderr.write( '{}\n'.format( type(foo) ) )
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
sys.stderr.write( '{}\n'.format( type( d.get('foo') ) ) )
d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) )
sys.stderr.write( '{}\n'.format( type( d.get('foo') ) ) )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p26.py p27.py --- p26.py 2019-09-19 00:08:30.805695000 +0900 +++ p27.py 2019-09-19 00:23:48.656686000 +0900 @@ -16,7 +16,9 @@ d['fuga'] = 'foo={} bar={}'.format(foo, bar) d['guha'] = '(^_^)'.join( d.values() ) + sys.stderr.write( '{}\n'.format( type( d.get('foo') ) ) ) d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) ) + sys.stderr.write( '{}\n'.format( type( d.get('foo') ) ) ) s = yaml.dump(d, default_flow_style=False, allow_unicode=True) sys.stdout.write(s)
$ ./p27.py < dat_u8.yaml <type 'str'> <type 'str'> <type 'unicode'> bar: 世界 foo: こんちには fuga: foo=こんちには bar=世界 guha: こんちには(^_^)世界(^_^)foo=こんちには bar=世界(^_^)こんちには 世界 hoge: こんちには 世界
$ ./p27.py < dat.yaml <type 'str'> <type 'str'> <type 'unicode'> bar: !!python/unicode 'world' foo: !!python/unicode 'hello' fuga: !!python/unicode 'foo=hello bar=world' guha: !!python/unicode 'hello(^_^)world(^_^)foo=hello bar=world(^_^)hello world' hoge: !!python/unicode 'hello world'
データが日本語だろうと、そうでなかろうと、typeの表示は同じです。
typeは同じでも、日本語か、そうでないかで、yaml.dump() の結果に !!python/unicode がついたり、つかなかったり。
もうちょっと詳細にtypeを見てみます。
p28.py
#!/usr/bin/env python2
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
show_type(d)
d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
show_type(d)
d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) )
show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p27.py p28.py --- p27.py 2019-09-19 00:23:48.656686000 +0900 +++ p28.py 2019-09-19 00:18:05.427970000 +0900 @@ -3,22 +3,28 @@ import sys import yaml +def show_type(d): + sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) ) + sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) ) + if __name__ == "__main__": s = sys.stdin.read() d = yaml.load(s) + show_type(d) d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) ) + show_type(d) foo = d.get('foo') bar = d.get('bar') - sys.stderr.write( '{}\n'.format( type(foo) ) ) + d['hoge'] = foo + ' ' + bar d['fuga'] = 'foo={} bar={}'.format(foo, bar) d['guha'] = '(^_^)'.join( d.values() ) - sys.stderr.write( '{}\n'.format( type( d.get('foo') ) ) ) + show_type(d) d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) ) - sys.stderr.write( '{}\n'.format( type( d.get('foo') ) ) ) + show_type(d) s = yaml.dump(d, default_flow_style=False, allow_unicode=True) sys.stdout.write(s)
データは混合っぽく。
dat_mix.yaml
foo: こんちには
bar: world
$ nkf -g dat_mix.yaml UTF-8
実行してみます。
$ ./p28.py < dat_mix.yaml foo <type 'unicode'>, bar <type 'str'> foo <type 'str'>, bar <type 'str'> foo <type 'str'>, bar <type 'str'> foo <type 'unicode'>, bar <type 'unicode'> bar: !!python/unicode 'world' foo: こんちには fuga: foo=こんちには bar=world guha: こんちには(^_^)world(^_^)foo=こんちには bar=world(^_^)こんちには world hoge: こんちには world
yaml.load()結果の逆として、yaml.dump()を考えてみます。
日本語を含む文字列は unicode で、 そうでないものは str として用意しておけば、 !!python/xxx を含まない、お望みのYAMLデータになるのでは?
p29.py
#!/usr/bin/env python2
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
show_type(d)
d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
show_type(d)
d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p28.py p29.py --- p28.py 2019-09-19 00:18:05.427970000 +0900 +++ p29.py 2019-09-19 00:36:54.802610000 +0900 @@ -7,6 +7,10 @@ sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) ) sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) ) +need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) ) + +my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s + if __name__ == "__main__": s = sys.stdin.read() d = yaml.load(s) @@ -23,7 +27,7 @@ d['guha'] = '(^_^)'.join( d.values() ) show_type(d) - d = dict( map( lambda kv: ( kv[0], kv[1].decode('utf-8') ), d.items() ) ) + d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) ) show_type(d) s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
値が128以上のデータを含む場合だけ、デコードするようにしてみました。
$ ./p29.py < dat_mix.yaml foo <type 'unicode'>, bar <type 'str'> foo <type 'str'>, bar <type 'str'> foo <type 'str'>, bar <type 'str'> foo <type 'unicode'>, bar <type 'str'> bar: world foo: こんちには fuga: foo=こんちには bar=world guha: こんちには(^_^)world(^_^)foo=こんちには bar=world(^_^)こんちには world hoge: こんちには world
お望みの動作となりました。
何も考えてない文字列を扱うPython2プログラムでの、 単純な日本語データ対応のまとめ
一般には、文字列処理は unicode で扱うべきとされているはずです。
文字列の長さをlen(s)で求めるときの扱いの問題や、エンコード情報を保持してないとか。
ですが自分的には、.format(), .join() が使えるかどうかが大きいので、 .encode('utf-8') で str になった状態の方がありがたいです。
さてPython3。
とりあえず、そのままPython3で試してみます。
p3.py
#!/usr/bin/env python3
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
show_type(d)
d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
show_type(d)
d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p29.py p3.py --- p29.py 2019-09-19 00:36:54.802610000 +0900 +++ p3.py 2019-09-19 00:42:34.875823000 +0900 @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import sys import yaml
先のPython2での最終形 p29.py のままです。
まずは日本語を含まないデータから。
$ ./p3.py < dat.yaml foo <class 'str'>, bar <class 'str'> foo <class 'bytes'>, bar <class 'bytes'> Traceback (most recent call last): File "./p3.py", line 25, in <module> d['hoge'] = foo + ' ' + bar TypeError: can't concat bytes to str
bytes と str を連結できないですよと。
日本語を含まない文字列でも.encode('utf-8')でエンコードすると、bytes型に。
ソース中の1文字の空白' 'は str型なので、連結できないぞと。
日本語を含まない状態なので、一旦エンコードなしで。
p32.py
#!/usr/bin/env python3
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
if __name__ == "__main__":
s = sys.stdin.read()
d = yaml.load(s)
show_type(d)
#d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
show_type(d)
d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p3.py p32.py --- p3.py 2019-09-19 00:42:34.875823000 +0900 +++ p32.py 2019-09-19 00:44:56.708300000 +0900 @@ -16,7 +16,7 @@ d = yaml.load(s) show_type(d) - d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) ) + #d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) ) show_type(d) foo = d.get('foo')
$ ./p32.py < dat.yaml foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> bar: world foo: hello fuga: foo=hello bar=world guha: foo=hello bar=world(^_^)world(^_^)hello(^_^)hello world hoge: hello world $
問題なし。
日本語のデータにしてみると。
$ ./p32.py < dat_u8.yaml Traceback (most recent call last): File "./p32.py", line 16, in <module> d = yaml.load(s) File "/usr/lib/python3/dist-packages/yaml/__init__.py", line 70, in load loader = Loader(stream) File "/usr/lib/python3/dist-packages/yaml/loader.py", line 34, in __init__ Reader.__init__(self, stream) File "/usr/lib/python3/dist-packages/yaml/reader.py", line 74, in __init__ self.check_printable(stream) File "/usr/lib/python3/dist-packages/yaml/reader.py", line 144, in check_printable 'unicode', "special characters are not allowed") yaml.reader.ReaderError: unacceptable character #xdce3: special characters are not allowed in "<unicode string>", position 5
そもそも yaml.load() の段階でエラー。
これ知ってます。
Python3 の sys.stdin はテキストだけでバイナリはダメ。
sys.stdin.buffer を使うべし。
p33.py
#!/usr/bin/env python3
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
if __name__ == "__main__":
#s = sys.stdin.read()
s = sys.stdin.buffer.read()
d = yaml.load(s)
show_type(d)
#d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
show_type(d)
d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p32.py p33.py --- p32.py 2019-09-19 00:44:56.708300000 +0900 +++ p33.py 2019-09-19 00:46:46.103044000 +0900 @@ -12,7 +12,8 @@ my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s if __name__ == "__main__": - s = sys.stdin.read() + #s = sys.stdin.read() + s = sys.stdin.buffer.read() d = yaml.load(s) show_type(d)
$ ./p33.py < dat_u8.yaml foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> Traceback (most recent call last): File "./p33.py", line 31, in <module> d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) ) File "./p33.py", line 31, in <lambda> d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) ) File "./p33.py", line 12, in <lambda> my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s AttributeError: 'str' object has no attribute 'decode'
Python3のstrは、Python2のunicode。なのでtype表示は全てstr。
yaml.load()は無事成功して、文字列のゴニョゴニョもOK。
yaml.dump()前のデコードでエラー。
そもそもエンコードしてないから、デコード済のsは.decode()を持ってないと。 ごもっとも。
ということはPython3では、エンコードもデコードも不要かな。
p34.py
#!/usr/bin/env python3
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
if __name__ == "__main__":
#s = sys.stdin.read()
s = sys.stdin.buffer.read()
d = yaml.load(s)
show_type(d)
#d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
#show_type(d)
#d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
#show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
sys.stdout.write(s)
# EOF
$ diff -u p33.py p34.py --- p33.py 2019-09-19 00:46:46.103044000 +0900 +++ p34.py 2019-09-19 00:48:39.927561000 +0900 @@ -27,9 +27,9 @@ d['fuga'] = 'foo={} bar={}'.format(foo, bar) d['guha'] = '(^_^)'.join( d.values() ) - show_type(d) - d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) ) - show_type(d) + #show_type(d) + #d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) ) + #show_type(d) s = yaml.dump(d, default_flow_style=False, allow_unicode=True) sys.stdout.write(s)
$ ./p34.py < dat_u8.yaml foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> Traceback (most recent call last): File "./p34.py", line 35, in <module> sys.stdout.write(s) UnicodeEncodeError: 'ascii' codec can't encode characters in position 5-6: ordinal not in range(128)
Python3のstrこと、unicodeのままで無事にyaml.dump()できて。 yaml.dump()結果はUTF-8であろうか?
ああ。標準出力もバイナリの出力はだめ。
p35.py
#!/usr/bin/env python3
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
if __name__ == "__main__":
#s = sys.stdin.read()
s = sys.stdin.buffer.read()
d = yaml.load(s)
show_type(d)
#d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
#show_type(d)
#d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
#show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
#sys.stdout.write(s)
sys.stdout.buffer.write(s)
# EOF
$ diff -u p34.py p35.py --- p34.py 2019-09-19 00:48:39.927561000 +0900 +++ p35.py 2019-09-19 00:49:26.196358000 +0900 @@ -32,5 +32,6 @@ #show_type(d) s = yaml.dump(d, default_flow_style=False, allow_unicode=True) - sys.stdout.write(s) + #sys.stdout.write(s) + sys.stdout.buffer.write(s) # EOF
$ ./p35.py < dat_u8.yaml foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> Traceback (most recent call last): File "./p35.py", line 36, in <module> sys.stdout.buffer.write(s) TypeError: a bytes-like object is required, not 'str'
となると、yaml.dump()結果はUTF-8じゃなくてunicodeかな。
yaml.dump()のオプションもあるような気がするけど、 とりあえず明示的にUTF-8にエンコードしてみます。
p36.py
#!/usr/bin/env python3
import sys
import yaml
def show_type(d):
sys.stderr.write( 'foo {}, '.format( type( d.get('foo') ) ) )
sys.stderr.write( 'bar {}\n'.format( type( d.get('bar') ) ) )
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
if __name__ == "__main__":
#s = sys.stdin.read()
s = sys.stdin.buffer.read()
d = yaml.load(s)
show_type(d)
#d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
show_type(d)
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
#show_type(d)
#d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
#show_type(d)
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
#sys.stdout.write(s)
s = s.encode('utf-8')
sys.stdout.buffer.write(s)
# EOF
$ diff -u p35.py p36.py --- p35.py 2019-09-19 00:49:26.196358000 +0900 +++ p36.py 2019-09-19 00:51:37.067715000 +0900 @@ -33,5 +33,6 @@ s = yaml.dump(d, default_flow_style=False, allow_unicode=True) #sys.stdout.write(s) + s = s.encode('utf-8') sys.stdout.buffer.write(s) # EOF
$ ./p36.py < dat_u8.yaml foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> bar: 世界 foo: こんちには fuga: foo=こんちには bar=世界 guha: こんちには 世界(^_^)こんちには(^_^)世界(^_^)foo=こんちには bar=世界 hoge: こんちには 世界
これでようやく。お望みの結果。
混合やアルファベットだけでも確認。
$ ./p36.py < dat_mix.yaml | nkf -j foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> bar: world foo: こんちには fuga: foo=こんちには bar=world guha: こんちには world(^_^)world(^_^)こんちには(^_^)foo=こんちには bar=world hoge: こんちには world
$ ./p36.py < dat.yaml foo <class 'str'>, bar <class 'str'> foo <class 'str'>, bar <class 'str'> bar: world foo: hello fuga: foo=hello bar=world guha: hello(^_^)foo=hello bar=world(^_^)world(^_^)hello world hoge: hello world
OK。
何も考えてない文字列を扱うPython3プログラムでの、 単純な日本語データ対応のまとめ
ファイルをオープンしてデータをリードしたり、 TCPソケットでsend(), recv()でデータをやりとりする場合についてです。
プロセス間をソケットでデータのやりとり。よくやるパターンです。
p200.py
#!/usr/bin/env python2
import sys
import yaml
import socket
import threading
def func(port, f_out):
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(('', port))
ss.listen(5)
(fd, adr) = ss.accept()
s = fd.recv(1024)
f_out.write(s)
f_out.close()
fd.close()
ss.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0]))
sys.exit(1)
f = open(sys.argv[1], 'r')
d = yaml.load(f)
f.close()
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
port = 11233
f_out = open(sys.argv[2], 'w')
th = threading.Thread( target=func, args=(port, f_out) )
th.daemon = True
th.start()
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd.connect(('localhost', port))
fd.sendall(s)
th.join()
# EOF
$ ./p200.py dat.yaml res.yaml $ cat res.yaml bar: world foo: hello fuga: foo=hello bar=world guha: hello(^_^)world(^_^)foo=hello bar=world(^_^)hello world hoge: hello world
問題なし。
では日本語のデータで。
$ ./p200.py dat_u8.yaml res_u8.yaml Traceback (most recent call last): File "./p200.py", line 32, in <module> d['fuga'] = 'foo={} bar={}'.format(foo, bar) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
まぁそうですね。Python2でのsys.stdinのときと同じ。
Python2の標準入出力版の最終形の対策を考慮して。
p202.py
#!/usr/bin/env python2
import sys
import yaml
import socket
import threading
need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) )
my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s
def func(port, f_out):
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(('', port))
ss.listen(5)
(fd, adr) = ss.accept()
s = fd.recv(1024)
f_out.write(s)
f_out.close()
fd.close()
ss.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0]))
sys.exit(1)
f = open(sys.argv[1], 'r')
d = yaml.load(f)
f.close()
d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) )
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
port = 11233
f_out = open(sys.argv[2], 'w')
th = threading.Thread( target=func, args=(port, f_out) )
th.daemon = True
th.start()
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd.connect(('localhost', port))
fd.sendall(s)
th.join()
# EOF
$ diff -u p200.py p202.py --- p200.py 2019-09-19 00:01:16.435258000 +0900 +++ p202.py 2019-09-19 00:39:59.999188000 +0900 @@ -5,6 +5,10 @@ import socket import threading +need_decode = lambda s: list( filter( lambda c: ord(c) >= 128, s ) ) + +my_decode = lambda s: s.decode('utf-8') if need_decode(s) else s + def func(port, f_out): ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -26,12 +30,16 @@ d = yaml.load(f) f.close() + d = dict( map( lambda kv: ( kv[0], kv[1].encode('utf-8') ), d.items() ) ) + foo = d.get('foo') bar = d.get('bar') d['hoge'] = foo + ' ' + bar d['fuga'] = 'foo={} bar={}'.format(foo, bar) d['guha'] = '(^_^)'.join( d.values() ) + d = dict( map( lambda kv: ( kv[0], my_decode(kv[1]) ), d.items() ) ) + s = yaml.dump(d, default_flow_style=False, allow_unicode=True) port = 11233
実行。
$ ./p202.py dat_u8.yaml res_u8.yaml $ cat res_u8.yaml bar: 世界 foo: こんちには fuga: foo=こんちには bar=世界 guha: こんちには(^_^)世界(^_^)foo=こんちには bar=世界(^_^)こんちには 世界 hoge: こんちには 世界
$ ./p202.py dat.yaml res.yaml $ cat res.yaml bar: world foo: hello fuga: foo=hello bar=world guha: hello(^_^)world(^_^)foo=hello bar=world(^_^)hello world hoge: hello world
$ ./p202.py dat_mix.yaml res_mix.yaml $ cat res_mix.yaml bar: world foo: こんちには fuga: foo=こんちには bar=world guha: こんちには(^_^)world(^_^)foo=こんちには bar=world(^_^)こんちには world hoge: こんちには world
Python2では、ファイルやソケットの場合でも、標準入出力の場合と同じ対応でよし。
まずはそのままでPython3で実行してみます。
p300.py
#!/usr/bin/env python3
import sys
import yaml
import socket
import threading
def func(port, f_out):
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(('', port))
ss.listen(5)
(fd, adr) = ss.accept()
s = fd.recv(1024)
f_out.write(s)
f_out.close()
fd.close()
ss.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0]))
sys.exit(1)
f = open(sys.argv[1], 'r')
d = yaml.load(f)
f.close()
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
port = 11233
f_out = open(sys.argv[2], 'w')
th = threading.Thread( target=func, args=(port, f_out) )
th.daemon = True
th.start()
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd.connect(('localhost', port))
fd.sendall(s)
th.join()
# EOF
$ diff -u p200.py p300.py --- p200.py 2019-09-19 00:01:16.435258000 +0900 +++ p300.py 2019-09-19 00:54:02.517980000 +0900 @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import sys import yaml
$ ./p300.py dat.yaml res.yaml Traceback (most recent call last): File "./p300.py", line 45, in <module> fd.sendall(s) TypeError: a bytes-like object is required, not 'str'
Python3のソケットのsend()はstrだめ。bytes。
という事で.encode('utf-8')で。
p302.py
#!/usr/bin/env python3
import sys
import yaml
import socket
import threading
def func(port, f_out):
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(('', port))
ss.listen(5)
(fd, adr) = ss.accept()
s = fd.recv(1024)
f_out.write(s)
f_out.close()
fd.close()
ss.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0]))
sys.exit(1)
f = open(sys.argv[1], 'r')
d = yaml.load(f)
f.close()
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
port = 11233
f_out = open(sys.argv[2], 'w')
th = threading.Thread( target=func, args=(port, f_out) )
th.daemon = True
th.start()
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd.connect(('localhost', port))
s = s.encode('utf-8')
fd.sendall(s)
th.join()
# EOF
--- p300.py 2019-09-19 00:54:02.517980000 +0900 +++ p302.py 2019-09-19 00:56:21.246919000 +0900 @@ -42,6 +42,7 @@ fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) fd.connect(('localhost', port)) + s = s.encode('utf-8') fd.sendall(s) th.join() # EOF
$ ./p302.py dat.yaml res.yaml Exception in thread Thread-1: Traceback (most recent call last): File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner self.run() File "/usr/lib/python3.5/threading.py", line 862, in run self._target(*self._args, **self._kwargs) File "./p302.py", line 15, in func f_out.write(s) TypeError: write() argument must be str, not bytes
ソケットのsend()もrecv()もエラーなしですが、 ファイルのwrite()でエラー。
recv()でUTF-8のbytes受け取って、そのままファイルに書けない。
ということは decode() で unicode にしてから write()
p303.py
#!/usr/bin/env python3
import sys
import yaml
import socket
import threading
def func(port, f_out):
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(('', port))
ss.listen(5)
(fd, adr) = ss.accept()
s = fd.recv(1024)
s = s.decode('utf-8')
f_out.write(s)
f_out.close()
fd.close()
ss.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0]))
sys.exit(1)
f = open(sys.argv[1], 'r')
d = yaml.load(f)
f.close()
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
port = 11233
f_out = open(sys.argv[2], 'w')
th = threading.Thread( target=func, args=(port, f_out) )
th.daemon = True
th.start()
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd.connect(('localhost', port))
s = s.encode('utf-8')
fd.sendall(s)
th.join()
# EOF
$ diff -u p302.py p303.py --- p302.py 2019-09-19 00:56:21.246919000 +0900 +++ p303.py 2019-09-19 00:57:33.111471000 +0900 @@ -12,6 +12,7 @@ ss.listen(5) (fd, adr) = ss.accept() s = fd.recv(1024) + s = s.decode('utf-8') f_out.write(s) f_out.close() fd.close()
$ ./p303.py dat.yaml res.yaml $ cat res.yaml bar: world foo: hello fuga: foo=hello bar=world guha: hello(^_^)hello world(^_^)world(^_^)foo=hello bar=world hoge: hello world
Python3で日本語なし版でお望みの結果。
では日本語で。
$ ./p303.py dat_u8.yaml res_u8.yaml Traceback (most recent call last): File "./p303.py", line 27, in <module> d = yaml.load(f) File "/usr/lib/python3/dist-packages/yaml/__init__.py", line 70, in load loader = Loader(stream) File "/usr/lib/python3/dist-packages/yaml/loader.py", line 34, in __init__ Reader.__init__(self, stream) File "/usr/lib/python3/dist-packages/yaml/reader.py", line 85, in __init__ self.determine_encoding() File "/usr/lib/python3/dist-packages/yaml/reader.py", line 124, in determine_encoding self.update_raw() File "/usr/lib/python3/dist-packages/yaml/reader.py", line 178, in update_raw data = self.stream.read(size) File "/usr/lib/python3.5/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 5: ordinal not in range(128)
これ、知ってます。
標準入力では sys.stdin.buffer でバイナリでした。
ファイル・オープンのモードを'r'でなくて'rb'で。
ついでに出力側も'w'でなくて'wb'で。
p304.py
#!/usr/bin/env python3
import sys
import yaml
import socket
import threading
def func(port, f_out):
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(('', port))
ss.listen(5)
(fd, adr) = ss.accept()
s = fd.recv(1024)
s = s.decode('utf-8')
f_out.write(s)
f_out.close()
fd.close()
ss.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0]))
sys.exit(1)
f = open(sys.argv[1], 'rb')
d = yaml.load(f)
f.close()
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
port = 11233
f_out = open(sys.argv[2], 'wb')
th = threading.Thread( target=func, args=(port, f_out) )
th.daemon = True
th.start()
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd.connect(('localhost', port))
s = s.encode('utf-8')
fd.sendall(s)
th.join()
# EOF
$ diff -u p303.py p304.py --- p303.py 2019-09-19 00:57:33.111471000 +0900 +++ p304.py 2019-09-19 00:59:10.391487000 +0900 @@ -23,7 +23,7 @@ sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0])) sys.exit(1) - f = open(sys.argv[1], 'r') + f = open(sys.argv[1], 'rb') d = yaml.load(f) f.close() @@ -36,7 +36,7 @@ s = yaml.dump(d, default_flow_style=False, allow_unicode=True) port = 11233 - f_out = open(sys.argv[2], 'w') + f_out = open(sys.argv[2], 'wb') th = threading.Thread( target=func, args=(port, f_out) ) th.daemon = True th.start()
$ ./p304.py dat_u8.yaml res_u8.yaml Exception in thread Thread-1: Traceback (most recent call last): File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner self.run() File "/usr/lib/python3.5/threading.py", line 862, in run self._target(*self._args, **self._kwargs) File "./p304.py", line 16, in func f_out.write(s) TypeError: a bytes-like object is required, not 'str'
なー。
'w'モードならstrで書き込みなので、.decode()してたけど。
'wb'モードならbytesにしないとダメ。
ということで、やっぱり.decode()なしで。
p305.py
#!/usr/bin/env python3
import sys
import yaml
import socket
import threading
def func(port, f_out):
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(('', port))
ss.listen(5)
(fd, adr) = ss.accept()
s = fd.recv(1024)
#s = s.decode('utf-8')
f_out.write(s)
f_out.close()
fd.close()
ss.close()
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.stderr.write('Usage: {} input.yaml output.yaml\n'.format(sys.argv[0]))
sys.exit(1)
f = open(sys.argv[1], 'rb')
d = yaml.load(f)
f.close()
foo = d.get('foo')
bar = d.get('bar')
d['hoge'] = foo + ' ' + bar
d['fuga'] = 'foo={} bar={}'.format(foo, bar)
d['guha'] = '(^_^)'.join( d.values() )
s = yaml.dump(d, default_flow_style=False, allow_unicode=True)
port = 11233
f_out = open(sys.argv[2], 'wb')
th = threading.Thread( target=func, args=(port, f_out) )
th.daemon = True
th.start()
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd.connect(('localhost', port))
s = s.encode('utf-8')
fd.sendall(s)
th.join()
# EOF
$ diff -u p304.py p305.py --- p304.py 2019-09-19 00:59:10.391487000 +0900 +++ p305.py 2019-09-19 01:03:01.256765000 +0900 @@ -12,7 +12,7 @@ ss.listen(5) (fd, adr) = ss.accept() s = fd.recv(1024) - s = s.decode('utf-8') + #s = s.decode('utf-8') f_out.write(s) f_out.close() fd.close()
$ ./p305.py dat_u8.yaml res_u8.yaml $ cat res_u8.yaml bar: 世界 foo: こんちには fuga: foo=こんちには bar=世界 guha: 世界(^_^)foo=こんちには bar=世界(^_^)こんちには(^_^)こんちには 世界 hoge: こんちには 世界
$ ./p305.py dat.yaml res.yaml $ cat res.yaml bar: world foo: hello fuga: foo=hello bar=world guha: foo=hello bar=world(^_^)hello(^_^)hello world(^_^)world hoge: hello world
$ ./p305.py dat_mix.yaml res_mix.yaml $ cat res_mix.yaml bar: world foo: こんちには fuga: foo=こんちには bar=world guha: こんちには(^_^)こんちには world(^_^)foo=こんちには bar=world(^_^)world hoge: こんちには world
お望みの結果。
Python3でファイルやソケットの場合の対応