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でファイルやソケットの場合の対応