获取魔术线/shebang中指定的编码(来自模块内)
Posted
技术标签:
【中文标题】获取魔术线/shebang中指定的编码(来自模块内)【英文标题】:get encoding specified in magic line / shebang (from within module) 【发布时间】:2016-07-14 12:40:11 【问题描述】:如果我在“魔术线”或 python 模块的 shebang 中指定字符编码(如 PEP 263 建议的那样)
# -*- coding: utf-8 -*-
我可以从该模块中检索此编码吗?
(使用 Python 2.7.9 在 Windows 7 x64 上工作)
我尝试(没有成功)检索默认编码或 shebang
# -*- coding: utf-8 -*-
import sys
from shebang import shebang
print "sys.getdefaultencoding():", sys.getdefaultencoding()
print "shebang:", shebang( __file__.rstrip("oc"))
将产生:
sys.getdefaultencoding(): ascii
shebang:无
(与 iso-8859-1 相同)
【问题讨论】:
编译到pyc
后编码信息很可能丢失。您可能需要直接解析py
文件。
请注意,sys.getdefaultencoding()
与 Python 源代码的解码方式无关。
【参考方案1】:
我会在 Python 2 中借用 Python 3 tokenize.detect_encoding()
function,稍作调整以符合 Python 2 的期望。我已更改函数签名以接受文件名,并删除了包含到目前为止读取的行;您的用例不需要这些:
import re
from codecs import lookup, BOM_UTF8
cookie_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)')
blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)')
def _get_normal_name(orig_enc):
"""Imitates get_normal_name in tokenizer.c."""
# Only care about the first 12 characters.
enc = orig_enc[:12].lower().replace("_", "-")
if enc == "utf-8" or enc.startswith("utf-8-"):
return "utf-8"
if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \
enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")):
return "iso-8859-1"
return orig_enc
def detect_encoding(filename):
bom_found = False
encoding = None
default = 'ascii'
def find_cookie(line):
match = cookie_re.match(line)
if not match:
return None
encoding = _get_normal_name(match.group(1))
try:
codec = lookup(encoding)
except LookupError:
# This behaviour mimics the Python interpreter
raise SyntaxError(
"unknown encoding for !r: ".format(
filename, encoding))
if bom_found:
if encoding != 'utf-8':
# This behaviour mimics the Python interpreter
raise SyntaxError(
'encoding problem for !r: utf-8'.format(filename))
encoding += '-sig'
return encoding
with open(filename, 'rb') as fileobj:
first = next(fileobj, '')
if first.startswith(BOM_UTF8):
bom_found = True
first = first[3:]
default = 'utf-8-sig'
if not first:
return default
encoding = find_cookie(first)
if encoding:
return encoding
if not blank_re.match(first):
return default
second = next(fileobj, '')
if not second:
return default
return find_cookie(second) or default
和原来的函数一样,上面的函数从源文件中读取两行max,如果cookie中的编码无效或者不是UTF-8,则会引发SyntaxError
异常,而存在 UTF-8 BOM。
演示:
>>> import tempfile
>>> def test(contents):
... with tempfile.NamedTemporaryFile() as f:
... f.write(contents)
... f.flush()
... return detect_encoding(f.name)
...
>>> test('# -*- coding: utf-8 -*-\n')
'utf-8'
>>> test('#!/bin/env python\n# -*- coding: latin-1 -*-\n')
'iso-8859-1'
>>> test('import this\n')
'ascii'
>>> import codecs
>>> test(codecs.BOM_UTF8 + 'import this\n')
'utf-8-sig'
>>> test(codecs.BOM_UTF8 + '# encoding: latin-1\n')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in test
File "<string>", line 37, in detect_encoding
File "<string>", line 24, in find_cookie
SyntaxError: encoding problem for '/var/folders/w0/nl1bwj6163j2pvxswf84xcsjh2pc5g/T/tmpxsqH8L': utf-8
>>> test('# encoding: foobarbaz\n')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in test
File "<string>", line 37, in detect_encoding
File "<string>", line 18, in find_cookie
SyntaxError: unknown encoding for '/var/folders/w0/nl1bwj6163j2pvxswf84xcsjh2pc5g/T/tmpHiHdG3': foobarbaz
【讨论】:
很棒的代码,虽然在 Windows 上它给了我一个IOError: [Errno 13] Permission denied: 'c:\\users\\maggyero\\appdata\\local\\temp\\tmp6qsaxo'
(在 Linux 上它工作正常)。我有一个相关的问题here,我认为你是这里最有资格回答的人之一。
@Maggyero:这是 Windows 上 NamedTemporaryFile
class 的限制:在命名的临时文件仍处于打开状态时,该名称是否可用于第二次打开文件,因人而异平台(它可以在 Unix 上如此使用;它不能在 Windows NT 或更高版本上)。以上是关于获取魔术线/shebang中指定的编码(来自模块内)的主要内容,如果未能解决你的问题,请参考以下文章
{WinError 2}在使用python编码时,系统找不到Sublime文本中指定的文件
项目App.config中指定的ConnectionStrings不在ConfigurationManager.ConnectionStrings中