.join()命令,在python中具有最大字符串长度

.join()命令,在python中具有最大字符串长度,python,Python,我想将一个id列表连接到一个字符串,其中每个id由“OR”分隔。在python中,我可以使用 ' OR '.join(list_of_ids) 我想知道是否有办法防止这个字符串变得太大(以字节为单位)。这对我来说很重要的原因是,我在API中使用该字符串,并且该API施加了4094字节的最大长度。 我的解决方案如下,我只是想知道是否有更好的 list_of_query_strings = [] substring = list_of_ids[0] list_of_ids.pop(0) while

我想将一个id列表连接到一个字符串,其中每个id由“OR”分隔。在python中,我可以使用

' OR '.join(list_of_ids)
我想知道是否有办法防止这个字符串变得太大(以字节为单位)。这对我来说很重要的原因是,我在API中使用该字符串,并且该API施加了4094字节的最大长度。 我的解决方案如下,我只是想知道是否有更好的

list_of_query_strings = []
substring = list_of_ids[0]
list_of_ids.pop(0)
while list_of_ids:
    new_addition = ' OR ' + list_of_ids[0]
    if sys.getsizeof(substring + new_addition) < 4094:
        substring += new_addition
    else:
        list_of_query_strings.append(substring)
        substring = list_of_ids[0]
    list_of_ids.pop(0)
list_of_query_strings.append(substring)
查询字符串列表=[]
substring=列表\u的\u id[0]
列出\u id.pop的\u(0)
当列出\u ID时:
新的\u添加='或'+\u ID的列表\u[0]
如果sys.getsizeof(子字符串+新添加)<4094:
子字符串+=新的_加法
其他:
查询字符串的列表。追加(子字符串)
substring=列表\u的\u id[0]
列出\u id.pop的\u(0)
查询字符串的列表。追加(子字符串)

这是一个比当前解决方案更简单的解决方案:

list_of_query_strings = []
one_string = list_of_ids[0]

# Iterate over each id
for id_ in list_of_ids[1:]:
    # Add the id to the substring if it doesn't make it to large
    if len(one_string) + len(id_) + 4 < 4094:
        one_string += ' OR ' + id_
    # Substring too large, so add to the list and reset
    else:
        list_of_query_strings.append(one_string)
        one_string = id_
查询字符串列表=[]
一个\u字符串=\u ID列表[0]
#迭代每个id
对于id的列表中的id[1:]:
#如果子字符串没有变大,请将id添加到子字符串中
如果len(一个字符串)+len(id)+4<4094:
一个字符串+='或'+id_
#子字符串太大,请添加到列表并重置
其他:
列出\u查询\u字符串。追加(一个\u字符串)
一个字符串=id_

只是为了好玩,这是一个过度设计的解决方案(它避免了Schlemiel the Painter重复的连接算法,允许您使用
str.join
进行有效组合):

这样,您就可以将iterable拆分为预先确定大小的组,然后在不使用重复连接的情况下加入它们:

 mystrings = [...]

 myblocks = [' OR '.join(grp) for _, grp in 
             groupby(mystrings, key=CumulativeLengthGrouper(' OR ', 4094)]
如果目标是使用指定的编码生成具有给定字节大小的字符串,则可以调整
CumulativeLengthGrouper
以接受第三个构造函数参数:

class CumulativeLengthGrouper:
    def __init__(self, joiner, maxblocksize, encoding='utf-8'):
        self.encoding = encoding
        self.joinerlen = len(joiner.encode(encoding))
        self.maxblocksize = maxblocksize
        self.groupctr = count()
        self.curgrp = next(self.groupctr)
        # Special cases initial case to cancel out treating first element
        # as requiring joiner, without requiring per call special case
        self.accumlen = -self.joinerlen

    def __call__(self, newstr):
        newbytes = newstr.encode(encoding)
        self.accumlen += self.joinerlen + len(newbytes)
        # If accumulated length exceeds block limit...
        if self.accumlen > self.maxblocksize:
            # Move to new group
            self.curgrp = next(self.groupctr)
            self.accumlen = len(newbytes)
        return self.curgrp

当字符串变长、创建新字符串或从一开始就删除内容时,计划是什么?为什么需要这样做?请注意,
sys.getsizeof
与字符串的长度关系不大,尤其是在Python 3.3+上。它(大致)是用于存储字符串的字节数,包括所有开销,但在Py3上,具体而言,根据它是ASCII、latin-1、BMP还是非BMP、是否具有缓存的UTF-8格式(一个相当不可预测的实现细节)等,这些字节数差别很大。它与字符数没有有用的关系。当连接字符串时,由于删除了对象头冗余,它通常会小于原始字符串的总和。嗨,我澄清了我的问题。。。首先,它是对字节数的限制,而不是字符数,很抱歉造成混淆。。。其次,我之所以需要它,是因为API最大请求限制。@carl:
sys.getsizeof
的字节数仍然是错误的。每个
str
都有一个Python对象头;连接的字符串将
n
标题向下折叠为一个(节省内存)。类似地,连接到单个非BMP字符串的十几个ASCII字符串最终将比组件大小大得多(因为所有ASCII数据最终将存储为每个字符四个字节,而最初是每个字符一个字节)。要了解这一点,唯一有用的方法不是完全任意的,是给定编码中编码形式的大小(例如UTF-8形式的字节大小)。小问题。。。字符串不应以“或”开头。像join命令一样,“OR”应该只出现在ids之间,而且len()只检查字符数,对吗?4094是bytesI纠正该问题的字节数。是,
len
返回字符数。这与仅使用ASCII的字节数相同。如果您需要unicode,我的答案将失败。我的算法仍然有效,除非您需要为某些
字节数
函数关闭
len
。您仍然需要删除最后一行中的'OR',但我同意这将是一个非常好的解决方案。。。与其他解决方案一样,len()只计算字符数,这至少在我使用的API中是失败的。我猜API需要utf8字符串?但我真的不知道。sys.getsizeof()似乎对我有用,但你似乎不喜欢它?@carl:
sys.getsizeof
可能“有用”,但它也会产生比API实际接受的小得多的组(假设它的大小不依赖于原始
PyUnicode\u对象的字节大小)。例如,
sys.getsizeof('a')
是54,即使它在存储到磁盘或通过线路发送时通常是一个字节(并且从不超过四个,甚至编码为UTF-32)
sys.getsizeof('OR')
53(因为奇怪而更小)。因此,如果您输入的是
['a']*100
,加上
'或'
,您会将其视为~10千磅,而它很可能被传输为<500磅。那么您对如何处理这一问题有何建议?就像我说的,如果我使用len(),字符串太长。。。因此,API似乎并不期望ASCII@carl当前位置如果不知道API正在做什么,我不能给你一个建议。如果API编码为UTF-8,那么我的第二个实现是正确的(注意:假设您可以达到但不超过最大长度;如果最大长度是独占的,而不是包含的,那么您应该将测试更改为
>=self.maxblocksize
)。但是API可能会使用字节计数作为前缀,或者使用UTF-16或其他方法。您没有提供足够的详细信息。@carl:BTW,
sys.getsizeof
对于计算连接后的结果字符串的大小是如此无用的一个例子:
sys.getsizeof(chr(0x1fff))+sys.getsizeof('a'*4000)
是4129
sys.getsizeof(chr(0x1fff)+'a'*4000)
(两个输入字符串的串联)是16080(非BMP字符意味着所有四千个
a
s需要以每段4字节的形式存储,而不是以每段1字节的形式存储)
class CumulativeLengthGrouper:
    def __init__(self, joiner, maxblocksize, encoding='utf-8'):
        self.encoding = encoding
        self.joinerlen = len(joiner.encode(encoding))
        self.maxblocksize = maxblocksize
        self.groupctr = count()
        self.curgrp = next(self.groupctr)
        # Special cases initial case to cancel out treating first element
        # as requiring joiner, without requiring per call special case
        self.accumlen = -self.joinerlen

    def __call__(self, newstr):
        newbytes = newstr.encode(encoding)
        self.accumlen += self.joinerlen + len(newbytes)
        # If accumulated length exceeds block limit...
        if self.accumlen > self.maxblocksize:
            # Move to new group
            self.curgrp = next(self.groupctr)
            self.accumlen = len(newbytes)
        return self.curgrp