在Python中将域名转换为IDN
我有一长串域名,需要生成一些报告。这个列表里有一些国际化域名(IDN),虽然我知道怎么在命令行里用Python把它们转换:
>>> domain = u"pfarmerü.com"
>>> domain
u'pfarmer\xfc.com'
>>> domain.encode("idna")
'xn--pfarmer-t2a.com'
>>>
但是我在用一个小脚本从文本文件读取数据时遇到了困难。
#!/usr/bin/python
import sys
infile = open(sys.argv[1])
for line in infile:
print line,
domain = unicode(line.strip())
print type(domain)
print "IDN:", domain.encode("idna")
print
我得到的输出是:
$ ./idn.py ./test
pfarmer.com
<type 'unicode'>
IDN: pfarmer.com
pfarmerü.com
Traceback (most recent call last):
File "./idn.py", line 9, in <module>
domain = unicode(line.strip())
UnicodeDecodeError: 'ascii' codec can't decode byte 0xfc in position 7: ordinal not in range(128)
我也尝试过:
#!/usr/bin/python
import sys
import codecs
infile = codecs.open(sys.argv[1], "r", "utf8")
for line in infile:
print line,
domain = line.strip()
print type(domain)
print "IDN:", domain.encode("idna")
print
结果是:
$ ./idn.py ./test
Traceback (most recent call last):
File "./idn.py", line 8, in <module>
for line in infile:
File "/usr/lib/python2.6/codecs.py", line 679, in next
return self.reader.next()
File "/usr/lib/python2.6/codecs.py", line 610, in next
line = self.readline()
File "/usr/lib/python2.6/codecs.py", line 525, in readline
data = self.read(readsize, firstline=True)
File "/usr/lib/python2.6/codecs.py", line 472, in read
newchars, decodedbytes = self.decode(data, self.errors)
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 0-5: unsupported Unicode code range
这是我的测试数据文件:
pfarmer.com
pfarmerü.com
我现在非常清楚自己需要理解Unicode。
谢谢,
彼得
2 个回答
你的第一个例子是可以的,不过有一点需要注意:
domain = unicode(line.strip())
你需要在这里指定一个特定的编码方式:unicode(line.strip(), 'utf-8')
。否则,系统会使用默认的编码方式,而默认的编码是7位ASCII,这样就会出错。你也可以用line.strip().decode('utf-8')
来写,和knitti的例子是一样的,这两种写法的效果是没有区别的。
不过根据错误信息“can't decode byte 0xfc”,我觉得你的test
文件可能没有保存为UTF-8格式。这可能就是为什么第二个例子看起来也没问题,但实际上却失败的原因。
实际上,它可能是ISO-8859-1编码,或者是非常相似的Windows代码页1252。如果这个文件是从西方Windows系统的文本编辑器中生成的,那肯定是后者;而现在Linux系统默认使用的是UTF-8编码。你要么确保将文件保存为UTF-8格式,要么使用编码'cp1252'
来读取这个文件。
你需要知道你的文件是用什么编码保存的。比如说,它可能是 'utf-8'(这不是 Unicode)或者 'iso-8859-1'、'cp1252' 之类的。
然后你可以这样做(假设是 'utf-8'):
infile = open(sys.argv[1])
for line in infile:
print line,
domain = line.strip().decode('utf-8')
print type(domain)
print "IDN:", domain.encode("idna")
print
用 decode
把编码的字符串转换成 Unicode。用 encode
把 Unicode 转换成字符串。如果你尝试对已经编码过的内容再进行编码,Python 会先尝试解码,默认使用的编码是 'ascii',这对于非 ASCII 的值会失败。