如果你在 Python 字符串上使用string.split()
,它会返回一个字符串列表。这些被拆分出来的子字符串是其父字符串部分的副本。
是否有可能获得一些更便宜的切片对象,它只包含拆分出的位的引用、偏移量和长度?
是否有可能有一些“字符串视图”来提取和处理这些子字符串,就好像它们是字符串一样,而不复制它们的字节?
(我问,因为我有非常大的字符串,我想切片并且偶尔会耗尽内存;删除副本将是一个廉价的配置文件引导的胜利。)
如果你在 Python 字符串上使用string.split()
,它会返回一个字符串列表。这些被拆分出来的子字符串是其父字符串部分的副本。
是否有可能获得一些更便宜的切片对象,它只包含拆分出的位的引用、偏移量和长度?
是否有可能有一些“字符串视图”来提取和处理这些子字符串,就好像它们是字符串一样,而不复制它们的字节?
(我问,因为我有非常大的字符串,我想切片并且偶尔会耗尽内存;删除副本将是一个廉价的配置文件引导的胜利。)
buffer
会给你一个字符串的只读视图。
>>> s = 'abcdefghijklmnopqrstuvwxyz'
>>> b = buffer(s, 2, 10)
>>> b
<read-only buffer for 0x7f935ee75d70, size 10, offset 2 at 0x7f935ee5a8f0>
>>> b[:]
'cdefghijkl'
在 Python 中,字符串对象总是指向一个以 NUL 结尾的缓冲区,因此必须复制子字符串。正如 Ignacio 指出的那样,您可以使用buffer()
来获取字符串内存的只读视图。不过,buffer()
内置函数已被更通用的memoryview
对象取代,这些对象在 Python 2.7 和 3.x 中可用(buffer()
在 Python 3.x 中已消失)。
s = "abcd" * 50
view = memoryview(s)
subview = view[10:20]
print subview.tobytes()
此代码打印
cdabcdabcd
只要您调用tobytes()
,就会创建字符串的副本,但是buffer
在 Ignacio 的回答中切片旧对象时也会发生同样的情况。
这是我想出的快速的类似字符串的缓冲区包装器;我能够使用它来代替经典字符串,而无需更改预期使用字符串的代码。
class StringView:
def __init__(self,s,start=0,size=sys.maxint):
self.s, self.start, self.stop = s, start, min(start+size,len(s))
self.size = self.stop - self.start
self._buf = buffer(s,start,self.size)
def find(self,sub,start=0,stop=None):
assert start >= 0, start
assert (stop is None) or (stop <= self.size), stop
ofs = self.s.find(sub,self.start+start,self.stop if (stop is None) else (self.start+stop))
if ofs != -1: ofs -= self.start
return ofs
def split(self,sep=None,maxsplit=sys.maxint):
assert maxsplit > 0, maxsplit
ret = []
if sep is None: #whitespace logic
pos = [self.start,self.start] # start and stop
def eat(whitespace=False):
while (pos[1] < self.stop) and (whitespace == (ord(self.s[pos[1]])<=32)):
pos[1] += 1
def eat_whitespace():
eat(True)
pos[0] = pos[1]
eat_whitespace()
while pos[1] < self.stop:
eat()
ret.append(self.__class__(self.s,pos[0],pos[1]-pos[0]))
eat_whitespace()
if len(ret) == maxsplit:
ret.append(self.__class__(self.s,pos[1]))
break
else:
start = stop = 0
while len(ret) < maxsplit:
stop = self.find(sep,start)
if -1 == stop:
break
ret.append(self.__class__(self.s,self.start+start,stop-start))
start = stop + len(sep)
ret.append(self.__class__(self.s,self.start+start,self.size-start))
return ret
def split_str(self,sep=None,maxsplit=sys.maxint):
"if you really want strings and not views"
return [str(sub) for sub in self.split(sep,maxsplit)]
def __cmp__(self,s):
if isinstance(s,self.__class__):
return cmp(self._buf,s._buf)
assert isinstance(s,str), type(s)
return cmp(self._buf,s)
def __len__(self):
return self.size
def __str__(self):
return str(self._buf)
def __repr__(self):
return "'%s'"%self._buf
if __name__=="__main__":
test_str = " this: is: a: te:st str:ing :"
test = Envelope.StringView(test_str)
print "find('is')"
print "\t",test_str.find("is")
print "\t",test.find("is")
print "find('is',4):"
print "\t",test_str.find("is",4)
print "\t",test.find("is",4)
print "find('is',4,7):"
print "\t",test_str.find("is",4,7)
print "\t",test.find("is",4,7)
print "split():"
print "\t",test_str.split()
print "\t",test.split()
print "split(None,2):"
print "\t",test_str.split(None,2)
print "\t",test.split(None,2)
print "split(':'):"
print "\t",test_str.split(":")
print "\t",test.split(":")
print "split('x'):"
print "\t",test_str.split("x")
print "\t",test.split("x")
print "''.split('x'):"
print "\t","".split("x")
print "\t",Envelope.StringView("").split("x")