如何优化匹配n长度的子串,但只有整个单词?

时间:2013-01-25 19:59:17

标签: python regex string

我们需要从一个较大的字符串中显示一些“预览文本”,该字符串最多可以是“n”个字符。不幸的是,我在PyPi上找不到处理这个问题的现有模块。

我希望能够做出正确的解决方案。虽然我的快速&下面的脏解决方案有效,效率不高 - 大量不断的比较。有没有人知道如何改进?我尝试了一个正则表达式,但在20分钟后放弃了。

我想出的kludgy解决方案足以满足大多数需求,我只知道这可以更快,更简洁地完成 - 我很想知道如何。

sample = "This is a sample string and I would like to break it down by words, without exceeding max_chars."

def clean_cut( text , max_chars ):
    rval = []
    words = text.split(' ')
    for word in words:
        len_rval = len(' '.join(rval))
        if len_rval + 1 + len(word) > max_chars :
            break
        rval.append(word)
    return ' '.join(rval)

for i in ( 15, 16, 17,30,35):
    cutdown = clean_cut( sample , i )
    print "%s | %s" % ( i , cutdown )

输出正确......

15 | This is a
16 | This is a sample
17 | This is a sample
30 | This is a sample string and I
35 | This is a sample string and I would

4 个答案:

答案 0 :(得分:3)

以下实施可能适合您

def clean_cut(st, end):
    st += ' ' #In case end > len(st)
    return st[:st[:end + 1].rfind(' ')]
for i in ( 15, 16, 17,30,35):
    cutdown = clean_cut( sample , i )
    print "%s | %s" % ( i , cutdown )

输出

15 | This is a
16 | This is a sample
17 | This is a sample
30 | This is a sample string and I
35 | This is a sample string and I would

注意

与textwrap相比,此实现速度提高了50倍

>>> stmt_ab = """
for i in ( 15, 16, 17,30,35):
    cutdown = sample[:sample[:i + 1].rfind(' ')]
"""
>>> stmt_mg = """
for i in ( 15, 16, 17,30,35):
    cutdown =  textwrap.wrap(sample[:i+1],i)[0]
"""
>>> import timeit
>>> t1_ab = timeit.Timer(stmt=stmt_ab, setup = "from __main__ import sample")
>>> t1_mg = timeit.Timer(stmt=stmt_mg, setup = "from __main__ import sample, textwrap")
>>> t1_ab.timeit(10000)
0.10367805429780219
>>> t1_mg.timeit(10000)
5.597085870104877
>>> 

答案 1 :(得分:1)

def substring_match(length, string):
    return re.search('(.{1,%d}) ' % length, string).group(0).strip()

应该工作,为我的琐碎测试做了

答案 2 :(得分:1)

您可以使用textwrap

textwrap.wrap(yourstring[:length+1],length)[0]

切割字符串并不是特别必要,但可能会使整个事情变得更有效......

>>> textwrap.wrap(sample[:15+1],15)[0]
'This is a'
>>> textwrap.wrap(sample[:16+1],16)[0]
'This is a sample'
>>> textwrap.wrap(sample[:17+1],17)[0]
'This is a sample'
>>> textwrap.wrap(sample[:30+1],30)[0]
'This is a sample string and I'
>>> textwrap.wrap(sample[:35+1],35)[0]
'This is a sample string and I would'

答案 3 :(得分:1)

有很好的图书馆功能可以为你完成这项工作,例如textwrap作为@ mgilson回答的指针。

我只是为了好玩而添加一个正则表达式的答案:

^.{0,n}(?<=\S)(?!\S)

将n替换为限制,并使用此正则表达式搜索第一个匹配项(最多只有1个匹配项)。我认为任何非空格字符都是单词的一部分。积极的后视确保匹配的最后一个字符是非空格,负前瞻确保匹配中最后一个字符后面的字符是空格字符或字符串结尾。

如果你想在字符串以长序列的非空格开头时匹配某些东西,这个正则表达式只会破坏字符限制的非空格字符序列:

^.{0,n}(?<=\S)(?!\S)|^\S{n}