如何用递归的方法在python decimal写一个程序把binary转成decimal 不可以用bin什么的

1. 字符编码简介
1.1. ASCII
ASCII(American Standard Code for Information Interchange),是一种单字节的编码。计算机世界里一开始只有英文,而单字节可以表示256个不同的字符,可以表示所有的英文字符和许多的控制符号。不过ASCII只用到了其中的一半(\x80以下),这也是MBCS得以实现的基础。
然而计算机世界里很快就有了其他语言,单字节的ASCII已无法满足需求。后来每个语言就制定了一套自己的编码,由于单字节能表示的字符太少,而且同时也需要与ASCII编码保持兼容,所以这些编码纷纷使用了多字节来表示字符,如GBxxx、BIGxxx等等,他们的规则是,如果第一个字节是\x80以下,则仍然表示ASCII字符;而如果是\x80以上,则跟下一个字节一起(共两个字节)表示一个字符,然后跳过下一个字节,继续往下判断。
这里,IBM发明了一个叫Code Page的概念,将这些编码都收入囊中并分配页码,GBK是第936页,也就是CP936。所以,也可以使用CP936表示GBK。
MBCS(Multi-Byte Character Set)是这些编码的统称。目前为止大家都是用了双字节,所以有时候也叫做DBCS(Double-Byte Character Set)。必须明确的是,MBCS并不是某一种特定的编码,Windows里根据你设定的区域不同,MBCS指代不同的编码,而Linux里无法使用MBCS作为编码。在Windows中你看不到MBCS这几个字符,因为微软为了更加洋气,使用了ANSI来吓唬人,记事本的另存为对话框里编码ANSI就是MBCS。同时,在简体中文Windows默认的区域设定里,指代GBK。
1.3. Unicode
后来,有人开始觉得太多编码导致世界变得过于复杂了,让人脑袋疼,于是大家坐在一起拍脑袋想出来一个方法:所有语言的字符都用同一种字符集来表示,这就是Unicode。
最初的Unicode标准UCS-2使用两个字节表示一个字符,所以你常常可以听到Unicode使用两个字节表示一个字符的说法。但过了不久有人觉得256*256太少了,还是不够用,于是出现了UCS-4标准,它使用4个字节表示一个字符,不过我们用的最多的仍然是UCS-2。
UCS(Unicode Character Set)还仅仅是字符对应码位的一张表而已,比如&汉&这个字的码位是6C49。字符具体如何传输和储存则是由UTF(UCS Transformation Format)来负责。
一开始这事很简单,直接使用UCS的码位来保存,这就是UTF-16,比如,&汉&直接使用\x6C\x49保存(UTF-16-BE),或是倒过来使用\x49\x6C保存(UTF-16-LE)。但用着用着美国人觉得自己吃了大亏,以前英文字母只需要一个字节就能保存了,现在大锅饭一吃变成了两个字节,空间消耗大了一倍……于是UTF-8横空出世。
UTF-8是一种很别扭的编码,具体表现在他是变长的,并且兼容ASCII,ASCII字符使用1字节表示。然而这里省了的必定是从别的地方抠出来的,你肯定也听说过UTF-8里中文字符使用3个字节来保存吧?4个字节保存的字符更是在泪奔……(具体UCS-2是怎么变成UTF-8的请自行搜索)
另外值得一提的是BOM(Byte Order Mark)。我们在储存文件时,文件使用的编码并没有保存,打开时则需要我们记住原先保存时使用的编码并使用这个编码打开,这样一来就产生了许多麻烦。(你可能想说记事本打开文件时并没有让选编码?不妨先打开记事本再使用文件 -& 打开看看)而UTF则引入了BOM来表示自身编码,如果一开始读入的几个字节是其中之一,则代表接下来要读取的文字使用的编码是相应的编码:
BOM_UTF8 '\xef\xbb\xbf'
BOM_UTF16_LE '\xff\xfe'
BOM_UTF16_BE '\xfe\xff'
并不是所有的编辑器都会写入BOM,但即使没有BOM,Unicode还是可以读取的,只是像MBCS的编码一样,需要另行指定具体的编码,否则解码将会失败。
你可能听说过UTF-8不需要BOM,这种说法是不对的,只是绝大多数编辑器在没有BOM时都是以UTF-8作为默认编码读取。即使是保存时默认使用ANSI(MBCS)的记事本,在读取文件时也是先使用UTF-8测试编码,如果可以成功解码,则使用UTF-8解码。记事本这个别扭的做法造成了一个BUG:如果你新建文本文件并输入&姹塧&然后使用ANSI(MBCS)保存,再打开就会变成&汉a&,你不妨试试 :)
2. Python2.x中的编码问题
2.1. str和unicode
str和unicode都是basestring的子类。严格意义上说,str其实是字节串,它是unicode经过编码后的字节组成的序列。对UTF-8编码的str'汉'使用len()函数时,结果是3,因为实际上,UTF-8编码的'汉' == '\xE6\xB1\x89'。
unicode才是真正意义上的字符串,对字节串str使用正确的字符编码进行解码后获得,并且len(u'汉') == 1。
再来看看encode()和decode()两个basestring的实例方法,理解了str和unicode的区别后,这两个方法就不会再混淆了:
# coding: UTF-8
u = u'汉'
print repr(u) # u'\u6c49'
s = u.encode('UTF-8')
print repr(s) # '\xe6\xb1\x89'
u2 = s.decode('UTF-8')
print repr(u2) # u'\u6c49'
# 对unicode进行解码是错误的
# s2 = u.decode('UTF-8')
# 同样,对str进行编码也是错误的
# u2 = s.encode('UTF-8')
需要注意的是,虽然对str调用encode()方法是错误的,但实际上Python不会抛出异常,而是返回另外一个相同内容但不同id的str;对unicode调用decode()方法也是这样。很不理解为什么不把encode()和decode()分别放在unicode和str中而是都放在basestring中,但既然已经这样了,我们就小心避免犯错吧。
2.2. 字符编码声明
源代码文件中,如果有用到非ASCII字符,则需要在文件头部进行字符编码的声明,如下:
#-*- coding: UTF-8 -*-
实际上Python只检查#、coding和编码字符串,其他的字符都是为了美观加上的。另外,Python中可用的字符编码有很多,并且还有许多别名,还不区分大小写,比如UTF-8可以写成u8。参见。
另外需要注意的是声明的编码必须与文件实际保存时用的编码一致,否则很大几率会出现代码解析异常。现在的IDE一般会自动处理这种情况,改变声明后同时换成声明的编码保存,但文本编辑器控们需要小心 :)
2.3. 读写文件
内置的open()方法打开文件时,read()读取的是str,读取后需要使用正确的编码格式进行decode()。write()写入时,如果参数是unicode,则需要使用你希望写入的编码进行encode(),如果是其他编码格式的str,则需要先用该str的编码进行decode(),转成unicode后再使用写入的编码进行encode()。如果直接将unicode作为参数传入write()方法,Python将先使用源代码文件声明的字符编码进行编码然后写入。
# coding: UTF-8
f = open('test.txt')
s = f.read()
print type(s) # &type 'str'&
# 已知是GBK编码,解码成unicode
u = s.decode('GBK')
f = open('test.txt', 'w')
# 编码成UTF-8编码的str
s = u.encode('UTF-8')
f.write(s)
f.close()另外,模块codecs提供了一个open()方法,可以指定一个编码打开文件,使用这个方法打开的文件读取返回的将是unicode。写入时,如果参数是unicode,则使用open()时指定的编码进行编码后写入;如果是str,则先根据源代码文件声明的字符编码,解码成unicode后再进行前述操作。相对内置的open()来说,这个方法比较不容易在编码上出现问题。
# coding: GBK
import codecs
f = codecs.open('test.txt', encoding='UTF-8')
u = f.read()
print type(u) # &type 'unicode'&
f = codecs.open('test.txt', 'a', encoding='UTF-8')
# 写入unicode
f.write(u)
# 写入str,自动进行解码编码操作
# GBK编码的str
s = '汉'
print repr(s) # '\xba\xba'
# 这里会先将GBK编码的str解码为unicode再编码为UTF-8写入
f.write(s)
2.4. 与编码相关的方法
sys/locale模块中提供了一些获取当前环境下的默认编码的方法。
# coding:gbk
import sys
import locale
print '%s.%s(): %s' % (f.__module__, f.__name__, f())
# 返回当前系统所使用的默认字符编码
p(sys.getdefaultencoding)
# 返回用于转换Unicode文件名至系统文件名所使用的编码
p(sys.getfilesystemencoding)
# 获取默认的区域设置并返回元祖(语言, 编码)
p(locale.getdefaultlocale)
# 返回用户设定的文本数据编码
# 文档提到this function only returns a guess
p(locale.getpreferredencoding)
# \xba\xba是'汉'的GBK编码
# mbcs是不推荐使用的编码,这里仅作测试表明为什么不应该用
print r&'\xba\xba'.decode('mbcs'):&, repr('\xba\xba'.decode('mbcs'))
#在笔者的Windows上的结果(区域设置为中文(简体, 中国))
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'
3.一些建议
3.1. 使用字符编码声明,并且同一工程中的所有源代码文件使用相同的字符编码声明。
这点是一定要做到的。
3.2. 抛弃str,全部使用unicode。
按引号前先按一下u最初做起来确实很不习惯而且经常会忘记再跑回去补,但如果这么做可以减少90%的编码问题。如果编码困扰不严重,可以不参考此条。
3.3. 使用codecs.open()替代内置的open()。
如果编码困扰不严重,可以不参考此条。
3.4. 绝对需要避免使用的字符编码:MBCS/DBCS和UTF-16。
这里说的MBCS不是指GBK什么的都不能用,而是不要使用Python里名为'MBCS'的编码,除非程序完全不移植。
Python中编码'MBCS'与'DBCS'是同义词,指当前Windows环境中MBCS指代的编码。Linux的Python实现中没有这种编码,所以一旦移植到Linux一定会出现异常!另外,只要设定的Windows系统区域不同,MBCS指代的编码也是不一样的。分别设定不同的区域运行2.4小节中的代码的结果:
#中文(简体, 中国)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'
#英语(美国)
#sys.getdefaultencoding(): UTF-8
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
#德语(德国)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
#日语(日本)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp932')
#locale.getpreferredencoding(): cp932
#'\xba\xba'.decode('mbcs'): u'\uff7a\uff7a'
可见,更改区域后,使用mbcs解码得到了不正确的结果,所以,当我们需要使用'GBK'时,应该直接写'GBK',不要写成'MBCS'。
UTF-16同理,虽然绝大多数操作系统中'UTF-16'是'UTF-16-LE'的同义词,但直接写'UTF-16-LE'只是多写3个字符而已,而万一某个操作系统中'UTF-16'变成了'UTF-16-BE'的同义词,就会有错误的结果。实际上,UTF-16用的相当少,但用到的时候还是需要注意。
4. 正则表达式基础
4.1. 简单介绍
正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。
下图展示了使用正则表达式进行匹配的流程:
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。
下图列出了Python支持的正则表达式元字符和语法:
4.2. 数量词的贪婪模式与非贪婪模式
正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式&ab*&如果用于查找&abbbc&,将找到&abbb&。而如果使用非贪婪的数量词&ab*?&,将找到&a&。
4.3. 反斜杠的困扰
与大多数编程语言相同,正则表达式里使用&\&作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符&\&,那么使用编程语言表示的正则表达式里将需要4个反斜杠&\\\\&:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r&\\&表示。同样,匹配一个数字的&\\d&可以写成r&\d&。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
4.4. 匹配模式
正则表达式提供了一些可用的匹配模式,比如忽略大小写、多行匹配等,这部分内容将在Pattern类的工厂方法re.compile(pattern[, flags])中一起介绍。
5.1. 开始使用re
Python通过re模块提供对正则表达式的支持。使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。
# encoding: UTF-8
# 将正则表达式编译成Pattern对象
pattern = re.compile(r'hello')
# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
match = pattern.match('hello world!')
# 使用Match获得分组信息
print match.group()
### 输出 ###
<pile(strPattern[, flag]):
这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取&#20540;可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
可选&#20540;有:
re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同) M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图) S(DOTALL): 点任意匹配模式,改变'.'的行为 L(LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定 U(UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。以下两个正则表达式是等价的:
a = re.compile(r&&&\d +
# the integral part
# the decimal point
# some fractional digits&&&, re.X)
b = re.compile(r&\d+\.\d*&)re提供了众多模块方法用于完成正则表达式的功能。这些方法可以使用Pattern实例的相应方法替代,唯一的好处是少写一行re.compile()代码,但同时也无法复用编译后的Pattern对象。这些方法将在Pattern类的实例方法部分一起介绍。如上面这个例子可以简写为:
m = re.match(r&#39;hello&#39;, &#39;hello world!&#39;)
print m.group()
re模块还提供了一个方法escape(string),用于将string中的正则表达式元字符如*/&#43;/?等之前加上转义符再返回,在需要大量匹配元字符时有那么一点用。
5.2. Match
Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。
string: 匹配时使用的文本。 re: 匹配时使用的Pattern对象。 pos: 文本中正则表达式开始搜索的索引。&#20540;与Pattern.match()和Pattern.seach()方法的同名参数相同。endpos: 文本中正则表达式结束搜索的索引。&#20540;与Pattern.match()和Pattern.seach()方法的同名参数相同。lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。 lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。
group([group1, …]):
获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。groups([default]):
以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个&#20540;替代,默认为None。 groupdict([default]):
返回以有别名的组的别名为键、以该组截获的子串为&#20540;的字典,没有别名的组不包含在内。default含义同上。 start([group]):
返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认&#20540;为0。 end([group]):
返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引&#43;1)。group默认&#20540;为0。 span([group]):
返回(start(group), end(group))。 expand(template):
将匹配到的分组代入template中然后返回。template中可以使用\id或\g&id&、\g&name&引用分组,但不能使用编号0。\id与\g&id&是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符'0',只能使用\g&1&0。
m = re.match(r&#39;(\w+) (\w+)(?P&sign&.*)&#39;, &#39;hello world!&#39;)
print &m.string:&, m.string
print &m.re:&, m.re
print &m.pos:&, m.pos
print &m.endpos:&, m.endpos
print &m.lastindex:&, m.lastindex
print &m.lastgroup:&, m.lastgroup
print &m.group(1,2):&, m.group(1, 2)
print &m.groups():&, m.groups()
print &m.groupdict():&, m.groupdict()
print &m.start(2):&, m.start(2)
print &m.end(2):&, m.end(2)
print &m.span(2):&, m.span(2)
print r&m.expand(r&#39;\2 \1\3&#39;):&, m.expand(r&#39;\2 \1\3&#39;)
### output ###
# m.string: hello world!
# m.re: &_sre.SRE_Pattern object at 0x016E1A38&
# m.pos: 0
# m.endpos: 12
# m.lastindex: 3
# m.lastgroup: sign
# m.group(1,2): (&#39;hello&#39;, &#39;world&#39;)
# m.groups(): (&#39;hello&#39;, &#39;world&#39;, &#39;!&#39;)
# m.groupdict(): {&#39;sign&#39;: &#39;!&#39;}
# m.start(2): 6
# m.end(2): 11
# m.span(2): (6, 11)
# m.expand(r&#39;\2 \1\3&#39;): world hello!
5.3. Pattern
Pattern对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。
Pattern不能直接实例化,必须使用re.compile()进行构造。
Pattern提供了几个可读属性用于获取表达式的相关信息:
pattern: 编译时用的表达式字符串。 flags: 编译时用的匹配模式。数字形式。 groups: 表达式中分组的数量。 groupindex: 以表达式中有别名的组的别名为键、以该组对应的编号为&#20540;的字典,没有别名的组不包含在内。
p = re.compile(r&#39;(\w+) (\w+)(?P&sign&.*)&#39;, re.DOTALL)
print &p.pattern:&, p.pattern
print &p.flags:&, p.flags
print &p.groups:&, p.groups
print &p.groupindex:&, p.groupindex
### output ###
# p.pattern: (\w+) (\w+)(?P&sign&.*)
# p.flags: 16
# p.groups: 3
# p.groupindex: {&#39;sign&#39;: 3}
实例方法[ | re模块方法]:
match(string[, pos[, endpos]]) | re.match(pattern, string[, flags]):
这个方法将从string的pos下标处起尝试匹配pattern;如果pattern结束时仍可匹配,则返回一个Match对象;如果匹配过程中pattern无法匹配,或者匹配未结束就已到达endpos,则返回None。
pos和endpos的默认&#20540;分别为0和len(string);re.match()无法指定这两个参数,参数flags用于编译pattern时指定匹配模式。
注意:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符'$'。
示例参见2.1小节。 search(string[, pos[, endpos]]) | re.search(pattern, string[, flags]):
这个方法用于查找字符串中可以匹配成功的子串。从string的pos下标处起尝试匹配pattern,如果pattern结束时仍可匹配,则返回一个Match对象;若无法匹配,则将pos加1后重新尝试匹配;直到pos=endpos时仍无法匹配则返回None。
pos和endpos的默认&#20540;分别为0和len(string));re.search()无法指定这两个参数,参数flags用于编译pattern时指定匹配模式。
# encoding: UTF-8
# 将正则表达式编译成Pattern对象
pattern = re.compile(r&#39;world&#39;)
# 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
# 这个例子中使用match()无法成功匹配
match = pattern.search(&#39;hello world!&#39;)
# 使用Match获得分组信息
print match.group()
### 输出 ###
split(string[, maxsplit]) | re.split(pattern, string[, maxsplit]):
按照能够匹配的子串将string分割后返回列表。maxsplit用于指定最大分割次数,不指定将全部分割。
p = re.compile(r&#39;\d+&#39;)
print p.split(&#39;one1two2three3four4&#39;)
### output ###
# [&#39;one&#39;, &#39;two&#39;, &#39;three&#39;, &#39;four&#39;, &#39;&#39;]
findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags]):
搜索string,以列表形式返回全部能匹配的子串。
p = re.compile(r&#39;\d+&#39;)
print p.findall(&#39;one1two2three3four4&#39;)
### output ###
# [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39;]
finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags]):
搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。
p = re.compile(r&#39;\d+&#39;)
for m in p.finditer(&#39;one1two2three3four4&#39;):
print m.group(),
### output ###
sub(repl, string[, count]) | re.sub(pattern, repl, string[, count]):
使用repl替换string中每一个匹配的子串后返回替换后的字符串。
当repl是一个字符串时,可以使用\id或\g&id&、\g&name&引用分组,但不能使用编号0。
当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
count用于指定最多替换次数,不指定时全部替换。
p = re.compile(r&#39;(\w+) (\w+)&#39;)
s = &#39;i say, hello world!&#39;
print p.sub(r&#39;\2 \1&#39;, s)
def func(m):
return m.group(1).title() + &#39; &#39; + m.group(2).title()
print p.sub(func, s)
### output ###
# say i, world hello!
# I Say, Hello World!
subn(repl, string[, count]) |re.sub(pattern, repl, string[, count]):
返回 (sub(repl, string[, count]), 替换次数)。
p = re.compile(r&#39;(\w+) (\w+)&#39;)
s = &#39;i say, hello world!&#39;
print p.subn(r&#39;\2 \1&#39;, s)
def func(m):
return m.group(1).title() + &#39; &#39; + m.group(2).title()
print p.subn(func, s)
### output ###
# (&#39;say i, world hello!&#39;, 2)
# (&#39;I Say, Hello World!&#39;, 2)
以上就是Python对于正则表达式的支持。熟练掌握正则表达式是每一个程序员必须具备的技能,这年头没有不与字符串打交道的程序了。笔者也处于初级阶段,与君共勉,^_^
另外,图中的特殊构造部分没有举出例子,用到这些的正则表达式是具有一定难度的。有兴趣可以思考一下,如何匹配不是以abc开头的单词,^_^
本文转自:
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:429267次
积分:6750
积分:6750
排名:第1179名
原创:162篇
转载:76篇
译文:68篇
评论:96条
(1)(20)(20)(18)(24)(2)(8)(13)(3)(5)(11)(13)(1)(2)(3)(1)(8)(4)(2)(5)(7)(3)(6)(3)(2)(3)(2)(5)(1)(8)(1)(4)(3)(1)(6)(4)(18)(18)(9)(26)(13)用Python写一个FUSE(用户态文件系统)文件系统_Linux编程_Linux公社-Linux系统门户网站
你好,游客
用Python写一个FUSE(用户态文件系统)文件系统
来源:伯乐在线&
作者:贱圣OMG
如果你是我的长期读者,那么你应该知道我在,最后我写了一个基于的我自己的加密层。
在写的时候,我对仅仅恢复一个文件就必须要下载整个巨大的档案文件的做法不甚满意,但仍然希望能将和 一起使用来实现可远程挂载、加密、去重、版本化备份的功能。
再次试用 后(啰嗦一句:它还是慢的出奇),我注意到了它有一个mount命令。深入研究后,我发现了和,感觉用Python写一个FUSE文件系统应该挺简单的。
聪明的读者可能已经意识到了我接下来要做的事情:我决定用Python写一个加密文件系统层!它与EncFS会非常相似,但也有一些重要的区别:
它默认以反向模式运行,接收正常的文件并且暴露一个被加密的目录。任何备份程序会发现(并且备份)这些加密的目录,不需要任何其它的存储。
它也能接受由一个目录列表组成的配置文件,并且在挂载点将这些目录暴露出来。这样的话,所有的备份脚本就需要将挂载点备份,各种不同的目录会立刻得以备份。
它会更偏重于备份,而不是加密存储。写起来应该会挺有意思的。
一个FUSE文件系统示例
写这个脚本的第一步是写出一个纯粹的传递式的文件系统。它仅仅是接受一个目录,并在挂载点将其暴露出来,确保任何在挂载点的修改都会镜像到源数据中。
fusepy 要求你写一个类,里面定义了各种操作系统级别的方法。你可以选择定义那些你的文件系统想要支持的方法,其他的可以暂时不予定义,但是我需要定义全部的方法,因为我的文件系统是一个传递式的文件系统,它应该表现的与原有的文件系统尽可能一致。
写这段代码会非常简单有趣,因为大部分的方法只是对os模块的一些简单封装(确实,你可以直接给它们赋值,比如 open=os.open 等等,但是我的模块需要一些路径扩展)。不幸的是,fuse-python有一个bug(据我所知)是当打开和读文件的时候,它无法将文件句柄回传给文件系统。因而我的脚本不知道某个应用执行读写操作时对应的是哪个文件句柄,从而导致了失败。只需要对fusepy做极少的改动,它就可以很好地运行。它只有一个文件,所以你可以把它直接放到你的工程里。
在这里,我很乐意给出这段代码,当你打算自己实现文件系统的时候可以拿来参考。这段代码提供了一个很好的起点,你可以直接把这个类复制到你的工程中并且根据需要重写里面的一些方法。
接下来是真正的代码了:
#!/usr/bin/env python&from __future__ import with_statement&import osimport sysimport errno&from fuse import FUSE, FuseOSError, Operations&class Passthrough(Operations):& & def __init__(self, root):& & & & self.root = root&& & # Helpers& & # =======&& & def _full_path(self, partial):& & & & if partial.startswith("/"):& & & & & & partial = partial[1:]& & & & path = os.path.join(self.root, partial)& & & & return path&& & # Filesystem methods& & # ==================&& & def access(self, path, mode):& & & & full_path = self._full_path(path)& & & & if not os.access(full_path, mode):& & & & & & raise FuseOSError(errno.EACCES)&& & def chmod(self, path, mode):& & & & full_path = self._full_path(path)& & & & return os.chmod(full_path, mode)&& & def chown(self, path, uid, gid):& & & & full_path = self._full_path(path)& & & & return os.chown(full_path, uid, gid)&& & def getattr(self, path, fh=None):& & & & full_path = self._full_path(path)& & & & st = os.lstat(full_path)& & & & return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime',& & & & & & & & & & 'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid'))&& & def readdir(self, path, fh):& & & & full_path = self._full_path(path)&& & & & dirents = ['.', '..']& & & & if os.path.isdir(full_path):& & & & & & dirents.extend(os.listdir(full_path))& & & & for r in dirents:& & & & & & yield r&& & def readlink(self, path):& & & & pathname = os.readlink(self._full_path(path))& & & & if pathname.startswith("/"):& & & & & & # Path name is absolute, sanitize it.& & & & & & return os.path.relpath(pathname, self.root)& & & & else:& & & & & & return pathname&& & def mknod(self, path, mode, dev):& & & & return os.mknod(self._full_path(path), mode, dev)&& & def rmdir(self, path):& & & & full_path = self._full_path(path)& & & & return os.rmdir(full_path)&& & def mkdir(self, path, mode):& & & & return os.mkdir(self._full_path(path), mode)&& & def statfs(self, path):& & & & full_path = self._full_path(path)& & & & stv = os.statvfs(full_path)& & & & return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree',& & & & & & 'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag',& & & & & & 'f_frsize', 'f_namemax'))&& & def unlink(self, path):& & & & return os.unlink(self._full_path(path))&& & def symlink(self, target, name):& & & & return os.symlink(self._full_path(target), self._full_path(name))&& & def rename(self, old, new):& & & & return os.rename(self._full_path(old), self._full_path(new))&& & def link(self, target, name):& & & & return os.link(self._full_path(target), self._full_path(name))&& & def utimens(self, path, times=None):& & & & return os.utime(self._full_path(path), times)&& & # File methods& & # ============&& & def open(self, path, flags):& & & & full_path = self._full_path(path)& & & & return os.open(full_path, flags)&& & def create(self, path, mode, fi=None):& & & & full_path = self._full_path(path)& & & & return os.open(full_path, os.O_WRONLY | os.O_CREAT, mode)&& & def read(self, path, length, offset, fh):& & & & os.lseek(fh, offset, os.SEEK_SET)& & & & return os.read(fh, length)&& & def write(self, path, buf, offset, fh):& & & & os.lseek(fh, offset, os.SEEK_SET)& & & & return os.write(fh, buf)&& & def truncate(self, path, length, fh=None):& & & & full_path = self._full_path(path)& & & & with open(full_path, 'r+') as f:& & & & & & f.truncate(length)&& & def flush(self, path, fh):& & & & return os.fsync(fh)&& & def release(self, path, fh):& & & & return os.close(fh)&& & def fsync(self, path, fdatasync, fh):& & & & return self.flush(path, fh)&def main(mountpoint, root):& & FUSE(Passthrough(root), mountpoint, foreground=True)&if __name__ == '__main__':& & main(sys.argv[2], sys.argv[1])
如果你想要运行它,只需要安装fusepy,把这段代码放进一个文件(比如myfuse.py)然后运行 python myfuse.py /你的目录 /挂载点目录 。你会发现 &/你的目录& 路径下的所有文件都跑到&/挂载点目录&,还能像用原生文件系统一样操作它们。
总的来说,我并不认为写一个文件系统就这么简单。接下来要做的是在脚本里添加加密/解密的功能,以及一些帮助类的方法。我的目标是能让它除了有更好的扩展性(因为是用Python写的),以及包含一些针对备份文件的额外特性外,可以成为一个EncFS的完全替代品。
如果你想跟进这个脚本的开发过程,请在下面订阅我的邮件列表,或者在Twitter上我。一如既往的欢迎反馈(在下面评论就很好)。
推荐阅读:
《Python开发技术详解》.( 周伟,宗杰).[高清PDF扫描版+随书视频+代码]
Python脚本获取Linux系统信息
Python 网站文件及数据库备份脚本
Python文件处理:读取文件
如何发布自定义的Python模块
Python爬虫多线程抓取代理服务器
Python中re(正则表达式)模块详解
Python 的详细介绍:Python 的下载地址:
相关资讯 & & &
& (03月08日)
& (11/13/:43)
& (03/18/:17)
& (12/07/:53)
& (11/09/:17)
& (02/19/:11)
图片资讯 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款}

我要回帖

更多关于 python yield 递归 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信