Python 中的 Unicode - 解析 JSON
Unicode in Python - parsing JSON
我写了这个小代码来获取 JSON 文件并将它们的内容导入 consul 键值存储 - 我很高兴递归完全按照我的预期工作,但是当源 .json 文件包含非 ASCII:
#!/usr/bin/python
import sys
import json
filename = str(sys.argv[1])
fh = open(filename)
def printDict (d, path):
for key in d:
if isinstance(d[key], dict):
printDict(d[key], path + str(key) + "/")
else:
print 'curl -X PUT http://localhost:8500/v1/kv/' + filename + path + key + ' -d "' + str(d[key]) + '"'
return
j = json.load(fh)
printDict(j, "/")
磁盘上的 JSON 文件失败示例:
{
"FacetConfig" : {
"facet:price-lf-p" : {
"prefixParts" : "£"
}
}
}
当我按原样 运行 代码时,我遇到了一个讨厌的异常,因为那个简单的 str() 无法将英国货币英镑符号转换为 7 位 ASCII:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa3' in position 0: ordinal not in range(128)
我怎样才能解决这个问题,而又不至于把一开始的小而优雅的代码搞得一团糟? :)
只需从 str(d[key])
中删除 str
。也就是说,
print ('curl -X PUT http://localhost:8500/v1/kv/' + filename +
path + key + ' -d "' + str(d[key]) + '"')
变为:
print ('curl -X PUT http://localhost:8500/v1/kv/' + filename +
path + key + ' -d "' + d[key] + '"')
这里的问题是Python2中的str
类型基本上仅限于ASCII字符。 type(d[key])
是 unicode
,所以你不能把它转换成 str
...不过没关系,我们还是可以打印它。
而不是使用 str()
, 显式编码 unicode 值。由于您将值用作 URL 元素,因此您必须编码为密钥 UTF-8,然后 URL-quote ;该值只需要编码为 UTF-8。
import urllib
print ('curl -X PUT http://localhost:8500/v1/kv/' + filename + path +
urllib.quote(key.encode('utf8')) + ' -d "' +
unicode(d[key]).encode('utf8') + '"')
您可以在此处使用字符串格式来提高可读性:
print 'curl -X PUT http://localhost:8500/v1/kv/{}{}{} -d "{}"'.format(
filename, path, urllib.quote(key.encode('utf8')),
unicode(d[key]).encode('utf8'))
如果 d[key]
始终是字符串值,则 unicode()
调用是多余的,但如果您还有数字、布尔值或 None
值,这将确保代码继续工作.
服务器可能需要 Content-Type header;如果您确实发送了一个,或许可以考虑向 header 添加一个 charset=utf8
参数。然而,Consul 似乎将数据视为不透明。
How can I solve this without making too much of a dog's dinner of code
that started out small and elegant?
遗憾的是,还需要几个额外的步骤来防止 decoding/encoding 错误。 python 2.x 有很多地方 隐式 encoding/decoding,即在你背后和未经你许可的情况下。当 python 执行隐式 encoding/decoding 时,它使用 ascii 编解码器,如果存在 utf-8(或任何其他非 ascii)字符,这将导致 encoding/decoding 错误。因此,你必须找到所有 python 隐式 encodings/decodings 的地方,并用显式 encodings/decodings 替换它们——如果你想让你的程序在那些地方处理非 ascii 字符.
至少,任何来自外部源的输入都应该在继续之前解码为 unicode 字符串,这意味着您必须知道输入的编码。但是,如果将 unicode 字符串与常规字符串结合使用,则会出现 encoding/decoding 错误,例如:
#-*- coding: utf-8 -*- #Allows utf-8 characters in your source code
unicode_str = '€'.decode('utf-8')
my_str = '{0}{1}'.format('This is the Euro sign: ', unicode_str)
--output:--
Traceback (most recent call last):
File "1.py", line 3, in <module>
my_str = '{0}{1}'.format('hello', unicode_str)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
因此,您的所有字符串可能都应该解码为 unicode 字符串。然后当你想输出字符串时,你需要对unicode字符串进行编码。
import sys
import json
import codecs
import urllib
def printDict(d, path, filename):
for key, val in d.items(): #key is a unicode string, val is a unicode string or dict
if isinstance(val, dict):
printDict(
val,
u'{0}{1}/'.format(path, key), #format() specifiers require 0,1 for python 2.6
filename
)
else:
key_str = key.encode('utf-8')
val_str = val.encode('utf-8')
url = '{0}{1}{2} -d "{3}"'.format(
filename,
path,
key_str,
val_str
)
print url
url_escaped = urllib.quote(url)
print url_escaped
curl_cmd = 'curl -X PUT'
base_url = 'http://localhost:8500/v1/kv/'
print "{0} {1}{2}".format(curl_cmd, base_url, url_escaped)
filename = sys.argv[1].decode('utf-8')
file_encoding = 'utf-8'
fh = codecs.open(filename, encoding=file_encoding)
my_json = json.load(fh)
fh.close()
print my_json
path = "/"
printDict(my_json, path.decode('utf-8'), filename) #Can the path have non-ascii characters in it?
--output:--
{u'FacetConfig': {u'facet:price-lf-p': {u'prefixParts': u'\xa3'}}}
data.txt/FacetConfig/facet:price-lf-p/prefixParts -d "£"
data.txt/FacetConfig/facet%3Aprice-lf-p/prefixParts%20-d%20%22%C2%A3%22
curl -X PUT http://localhost:8500/v1/kv/data.txt/FacetConfig/facet%3Aprice-lf-p/prefixParts%20-d%20%22%C2%A3%22
我写了这个小代码来获取 JSON 文件并将它们的内容导入 consul 键值存储 - 我很高兴递归完全按照我的预期工作,但是当源 .json 文件包含非 ASCII:
#!/usr/bin/python
import sys
import json
filename = str(sys.argv[1])
fh = open(filename)
def printDict (d, path):
for key in d:
if isinstance(d[key], dict):
printDict(d[key], path + str(key) + "/")
else:
print 'curl -X PUT http://localhost:8500/v1/kv/' + filename + path + key + ' -d "' + str(d[key]) + '"'
return
j = json.load(fh)
printDict(j, "/")
磁盘上的 JSON 文件失败示例:
{
"FacetConfig" : {
"facet:price-lf-p" : {
"prefixParts" : "£"
}
}
}
当我按原样 运行 代码时,我遇到了一个讨厌的异常,因为那个简单的 str() 无法将英国货币英镑符号转换为 7 位 ASCII:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa3' in position 0: ordinal not in range(128)
我怎样才能解决这个问题,而又不至于把一开始的小而优雅的代码搞得一团糟? :)
只需从 str(d[key])
中删除 str
。也就是说,
print ('curl -X PUT http://localhost:8500/v1/kv/' + filename +
path + key + ' -d "' + str(d[key]) + '"')
变为:
print ('curl -X PUT http://localhost:8500/v1/kv/' + filename +
path + key + ' -d "' + d[key] + '"')
这里的问题是Python2中的str
类型基本上仅限于ASCII字符。 type(d[key])
是 unicode
,所以你不能把它转换成 str
...不过没关系,我们还是可以打印它。
而不是使用 str()
, 显式编码 unicode 值。由于您将值用作 URL 元素,因此您必须编码为密钥 UTF-8,然后 URL-quote ;该值只需要编码为 UTF-8。
import urllib
print ('curl -X PUT http://localhost:8500/v1/kv/' + filename + path +
urllib.quote(key.encode('utf8')) + ' -d "' +
unicode(d[key]).encode('utf8') + '"')
您可以在此处使用字符串格式来提高可读性:
print 'curl -X PUT http://localhost:8500/v1/kv/{}{}{} -d "{}"'.format(
filename, path, urllib.quote(key.encode('utf8')),
unicode(d[key]).encode('utf8'))
如果 d[key]
始终是字符串值,则 unicode()
调用是多余的,但如果您还有数字、布尔值或 None
值,这将确保代码继续工作.
服务器可能需要 Content-Type header;如果您确实发送了一个,或许可以考虑向 header 添加一个 charset=utf8
参数。然而,Consul 似乎将数据视为不透明。
How can I solve this without making too much of a dog's dinner of code that started out small and elegant?
遗憾的是,还需要几个额外的步骤来防止 decoding/encoding 错误。 python 2.x 有很多地方 隐式 encoding/decoding,即在你背后和未经你许可的情况下。当 python 执行隐式 encoding/decoding 时,它使用 ascii 编解码器,如果存在 utf-8(或任何其他非 ascii)字符,这将导致 encoding/decoding 错误。因此,你必须找到所有 python 隐式 encodings/decodings 的地方,并用显式 encodings/decodings 替换它们——如果你想让你的程序在那些地方处理非 ascii 字符.
至少,任何来自外部源的输入都应该在继续之前解码为 unicode 字符串,这意味着您必须知道输入的编码。但是,如果将 unicode 字符串与常规字符串结合使用,则会出现 encoding/decoding 错误,例如:
#-*- coding: utf-8 -*- #Allows utf-8 characters in your source code
unicode_str = '€'.decode('utf-8')
my_str = '{0}{1}'.format('This is the Euro sign: ', unicode_str)
--output:--
Traceback (most recent call last):
File "1.py", line 3, in <module>
my_str = '{0}{1}'.format('hello', unicode_str)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
因此,您的所有字符串可能都应该解码为 unicode 字符串。然后当你想输出字符串时,你需要对unicode字符串进行编码。
import sys
import json
import codecs
import urllib
def printDict(d, path, filename):
for key, val in d.items(): #key is a unicode string, val is a unicode string or dict
if isinstance(val, dict):
printDict(
val,
u'{0}{1}/'.format(path, key), #format() specifiers require 0,1 for python 2.6
filename
)
else:
key_str = key.encode('utf-8')
val_str = val.encode('utf-8')
url = '{0}{1}{2} -d "{3}"'.format(
filename,
path,
key_str,
val_str
)
print url
url_escaped = urllib.quote(url)
print url_escaped
curl_cmd = 'curl -X PUT'
base_url = 'http://localhost:8500/v1/kv/'
print "{0} {1}{2}".format(curl_cmd, base_url, url_escaped)
filename = sys.argv[1].decode('utf-8')
file_encoding = 'utf-8'
fh = codecs.open(filename, encoding=file_encoding)
my_json = json.load(fh)
fh.close()
print my_json
path = "/"
printDict(my_json, path.decode('utf-8'), filename) #Can the path have non-ascii characters in it?
--output:--
{u'FacetConfig': {u'facet:price-lf-p': {u'prefixParts': u'\xa3'}}}
data.txt/FacetConfig/facet:price-lf-p/prefixParts -d "£"
data.txt/FacetConfig/facet%3Aprice-lf-p/prefixParts%20-d%20%22%C2%A3%22
curl -X PUT http://localhost:8500/v1/kv/data.txt/FacetConfig/facet%3Aprice-lf-p/prefixParts%20-d%20%22%C2%A3%22