【问题标题】:Slicing list by characters and elements, Python按字符和元素切片列表,Python
【发布时间】:2014-03-07 05:00:49
【问题描述】:

我有一个宽度有限的文本列,每一行都是多个元素的列表,由分号分隔。我想删除所有导致行超过字符限制的列表元素。

以前我用的是

   if len(row[7].split(';')) > 5:
        row[7] = ('; '.join(row[7].split(';')[1:5]).strip())[:45]

这会产生两个明显的问题:

  • 某些列表的元素少于 5 个且字符多于 45 个,因此条件不会像应有的那样删除多余的元素
  • 列表元素在单词中间被截断。

这是一个示例输入:

 Foo; Bar; Aoicsdeadwcwewrw; owierwicowmwoemow; aoweirwoer
 ODIFUWE
 acowierwe; asodicjwoer; s; ow; w; w

这是相应的示例输出:

 Foo; Bar; Aoicsdeadwcwewrw
 ODIFUWE
 acowierwe; asodicjwoer; s; ow; w

限制为 5 个元素或 45 个字符,如果行达到这些限制中的任何一个,则应切断尾随元素。

【问题讨论】:

  • 45 个字符中的分号和空格你算吗?
  • 另外,如果列表中的第一项超过 45 个字符,你想做什么?剪切所有内容,导致一个空字符串?
  • 是的,是的,很好的说明。

标签: python list python-2.7 csv slice


【解决方案1】:
>>> data = """ Foo; Bar; Aoicsdeadwcwewrw; owierwicowmwoemow; aoweirwoer
...  ODIFUWE
...  acowierwe; asodicjwoer; s; ow; w; w""".split("\n")
>>> 
>>> for row in data:
...     row = row.split(";")[:5]
...     res = []
...     for item in row:
...         if len(";".join(res + [item])) > 45: break
...         res.append(item)
...     print ";".join(res)
... 
 Foo; Bar; Aoicsdeadwcwewrw
 ODIFUWE
 acowierwe; asodicjwoer; s; ow; w

【讨论】:

  • 与其反复加入以获得一个字符串来测试长度,为什么不在你的字符串上使用+=呢?您似乎在手动重新创建 str.join 通常用来避免的 Schlemiel the painter's algorithm
  • @Blckknght,这是对要求的一种字面解释。很清楚发生了什么。如果性能不重要,最好选择简单而正确的方法。
【解决方案2】:
def myfilter(x, wmax=5, cmax=45, d=';'):
    words = x.split(d)

    nwords = 0
    nchars = 0
    s = []
    for i in words:
        nwords += 1
        nchars += len(i) + len(d)
        if (nwords >= wmax) | (nchars > cmax+1):
            break
        s.append(i)
    return ';'.join(s)

这样的事情应该可以工作。

【讨论】:

    【解决方案3】:

    我认为这个生成器是确定在哪里剪切字符串列表的最有效方法:

    def limit(iterable, max_num, max_length, padding_length):
        seen_length = -padding_length  # the first value will not be padded so start negative
        for i, s in enumerate(iterable, 1):
            if i > max_num or seen_length + padding_length + len(s) > max_length:
                return
            seen_length += padding_length + len(s)
            yield s
    

    像这样使用它:

    row[7] = "; ".join(limit(row[7].split("; "), 5, 45, 2)
    

    生成器不连接任何字符串,只是将它们的长度相加,因此使用它和一个join 将是O(N+M),其中N 是字符串的数量,M 是结果的长度细绳。这比gnibbler 的解决方案要好,因为重复joins 是O(N*M)。这种算法改进对于相对较短和较少的字符串可能并不重要,就像您描述的那样,但是如果您试图限制要说的内容,500 个项目和数千个字符的长度,您可能会注意到差异。

    【讨论】:

    • 这有点超出我的想象,所以很高兴看到这两种方法。但是,由于我在数十个全局 CSV 中使用了数十万个项目,因此性能可能值得关注。
    【解决方案4】:

    这是一个功能细分,它应该更清楚发生了什么:

    data = [
        " Foo; Bar; Aoicsdeadwcwewrw; owierwicowmwoemow; aoweirwoer",
        " ODIFUWE",
        " acowierwe; asodicjwoer; s; ow; w; w"
    ]
    
    def first_n_chars(s, break_on, n):
        if len(s) > n:
            return s[:s.rfind(break_on, 0, n + len(break_on))]
        else:
            return s
    
    def first_n_groups(s, break_on, n):
        try:
            end = -1
            for _ in range(n):
                end = s.index(break_on, end+1)
            return s[:end]
        except ValueError:
            return s
    
    fortyfivechars = (first_n_chars (s, '; ', 45) for s in data)
    fivegroups     = (first_n_groups(s, '; ', 5)  for s in fortyfivechars)
    trimmed_data   = list(fivegroups)
    

    导致

    [' Foo; Bar; Aoicsdeadwcwewrw',
     ' ODIFUWE',
     ' acowierwe; asodicjwoer; s; ow; w']
    

    【讨论】:

      猜你喜欢
      • 2018-08-17
      • 2020-08-08
      • 2011-10-24
      • 1970-01-01
      • 1970-01-01
      • 2014-11-02
      • 2016-09-03
      • 2014-12-04
      • 2010-12-14
      相关资源
      最近更新 更多