Python - 通过信件打包/拆包

时间:2013-09-22 11:32:22

标签: python string

我刚刚开始学习python,我有这个让我感到困惑的练习: 创建一个可以打包或解包字母串的函数。 所以aaabb将被打包a3b2,反之亦然。

对于函数的打包部分,我写了以下

def packer(s):
    if s.isalpha():        # Defines if unpacked

    stack = []

        for i in s:
            if s.count(i) > 1:
                if (i + str(s.count(i))) not in stack:
                    stack.append(i + str(s.count(i)))
            else:
                stack.append(i)

        print "".join(stack)

    else:
        print "Something's not quite right.."
        return False
packer("aaaaaaaaaaaabbbccccd")

这似乎一切正常。但作业说明了这一点 如果输入有(例如)b或c之后的字母a,那么 它应该在以后解压缩成原始形式。 所以“aaabbkka”应该变成a3b2k2a,而不是a4b2k2。 因此我想,我不能使用“count()”命令,因为 计算整个字符串中所有项目的出现次数,对吗? 那么我的选择是什么呢?

打开包装 - 我已经想到了我的代码需要做的基础知识 -

  1. 在“if s.isalpha():”之间,否则,我应该添加一个elif 检查字符串中是否有数字。 (我想这会是 足以确定它是打包版本还是解压缩。)
  2. 创建一个for循环,并在其中包含一个if语句,然后检查每个元素:

    2.1。如果它后面有一个数字>返回(或添加到空堆栈)数字乘以数字
    2.2。如果它之后没有数字>只返回元素。

  3. 第2号大问题 - 我如何检查它是一个数字还是另一个数字 列表中元素后面的字母元素?我想这一定必须完成 切片,但那些只采取整数。这可以通过索引命令来实现吗?

    此外 - 如果这有任何相关性 - 到目前为止我基本上涵盖了列表,字符串,if和for 而且我被告知这种练习对那些练习是可行的(......所以如果你不介意保持这个非常基础)

    所有帮助对新手爱好者表示赞赏!


    解决:

    def packer(s):
        if s.isalpha():        # Defines if unpacked
            groups= []
            last_char = None
    
            for c in s:
                if c == last_char:
                    groups[-1].append(c)
                else:
                    groups.append([c])
                last_char = c
    
            return ''.join('%s%s' % (g[0], len(g)>1 and len(g) or '') for g in groups)
    
        else:                   # Seems to be packed
    
            stack = ""
    
            for i in range(len(s)):
                if s[i].isalpha():
                    if i+1 < len(s) and s[i+1].isdigit():
                        digit = s[i+1]
                        char = s[i]
                        i += 2
    
                        while i < len(s) and s[i].isdigit():
                            digit +=s[i]
                            i+=1
                        stack += char * int(digit)
    
                    else:
                        stack+= s[i]
                else:
                    ""
            return "".join(stack) 
    print (packer("aaaaaaaaaaaabbbccccd"))
    print (packer("a4b19am4nmba22")) 
    

    所以这是我的最终代码。几乎设法用for循环和if语句来完成它。 最后虽然我不得不引入while循环来解决读取多位数的问题。我想我仍然设法保持足够简单。非常感谢他们和其他所有人一起切入!

3 个答案:

答案 0 :(得分:2)

我认为您可以使用`itertools.grouby'功能

例如

import itertools
data = 'aaassaaasssddee'
groupped_data = ((c, len(list(g))) for c, g in itertools.groupby(data))
result = ''.join(c + (str(n) if n > 1 else '') for c, n in groupped_data)

当然可以使用generator而不是generator语句使这段代码更具可读性

答案 1 :(得分:2)

一个简单的解决方案: 如果char不同,请创建一个新组。否则将其附加到最后一组。最后计算所有组并加入它们。

def packer(s):
    groups = []
    last_char = None
    for c in s:
        if c == last_char:
            groups[-1].append(c)
        else:
            groups.append([c])
        last_char = c
    return ''.join('%s%s'%(g[0], len(g)) for g in groups)

另一种方法是使用re

正则表达式r'(.)\1+'可以匹配长于1的连续字符。使用re.sub,您可以轻松对其进行编码:

regex = re.compile(r'(.)\1+')

def replacer(match):
    return match.group(1) + str(len(match.group(0)))

regex.sub(replacer, 'aaabbkka')
#=> 'a3b2k2a'

答案 2 :(得分:2)

这是我在评论中概述的算法的实现:

from itertools import takewhile, count, islice, izip

def consume(items):
    from collections import deque
    deque(items, maxlen=0)

def ilen(items):
    result = count()
    consume(izip(items, result))
    return next(result)

def pack_or_unpack(data):
    start = 0
    result = []

    while start < len(data):
        if data[start].isdigit():
            # `data` is packed, bail
            return unpack(data)
        run = run_len(data, start)

        # append the character that might repeat
        result.append(data[start]) 

        if run > 1:
            # append the length of the run of characters
            result.append(str(run))

        start += run

    return ''.join(result)


def run_len(data, start):
    """Return the end index of the run of identical characters starting at 
    `start`"""
    return start + ilen(takewhile(lambda c: c == data[start], 
                                  islice(data, start, None)))

def unpack(data):
    result = []
    for i in range(len(data)):
        if data[i].isdigit():
            # skip digits, we'll look for them below
            continue

        # packed character
        c = data[i]
        # number of repetitions
        n = 1
        if (i+1) < len(data) and data[i+1].isdigit():
            # if the next character is a digit, grab all the digits in the 
            # substring starting at i+1
            n = int(''.join(takewhile(str.isdigit, data[i+1:])))

        # append the repeated character
        result.append(c*n) # multiplying a string with a number repeats it
    return ''.join(result)

print pack_or_unpack('aaabbc')
print pack_or_unpack('a3b2c')
print pack_or_unpack('a10')
print pack_or_unpack('b5c5')
print pack_or_unpack('abc')

unpack()的正则表达式版本将是:

import re
UNPACK_RE = re.compile(r'(?P<char> [a-zA-Z]) (?P<count> \d+)?', re.VERBOSE)
def unpack_re(data):
    matches = UNPACK_RE.finditer(data)
    pairs = ((m.group('char'), m.group('count')) for m in matches)
    return ''.join(char * (int(count) if count else 1)
                   for char, count in pairs)

此代码演示了实现该算法的最简单(或“基本”)方法。它不是特别优雅或惯用或必然有效。 (如果用C语言编写,但Python有一些注意事项,例如:索引字符串会将字符复制到新字符串中,而似乎过度复制数据的算法可能比尝试避免这种情况更快,如果复制完成的话C和解决方法是用Python循环实现的。)