在Python中按特定模式打印字母

21

我有以下字符串,并对其进行了拆分:

>>> st = '%2g%k%3p'
>>> l = filter(None, st.split('%'))
>>> print l
['2g', 'k', '3p']
现在我想要打印两次字母 g,一次字母 k 和三次字母 p。
ggkppp

怎么可能呢?


这是来自 HTML 代码吗? - Iron Fist
1
数字在字母前面会超过一位数吗?数字会是零吗? - PM 2Ring
@Irano 不,这不是... - MLSC
@PM 是的,它可能超过一个数字... - MLSC
1
你需要一个更正式/详细的输入语言规范。百分号(%)到底代表什么,数字有哪些有效值,要打印的“字符串”有哪些有效值?例如,%234 是什么意思?它是表示“打印两次34”,还是无效因为后面没有字母,或者其他什么意思?那么 %55a5 呢?是打印五十五次 a5,还是打印五次 5a5,或者先打印五次 5 然后再打印 a5?这里有很多情况你都没有具体说明。 - jpmc26
5个回答

15
你可以使用带有 isdigit()generator 来检查你的第一个字符是否为数字,然后根据适当的计数返回以下字符串。 然后,您可以使用join来获得输出:

你可以使用 generatorisdigit() 来检查你的首个字符是否为数字,并根据相应的计数返回以下字符串。之后,你可以使用 join 来获取你的输出:

''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in l)

演示:

In [70]: [i[1:]*int(i[0]) if i[0].isdigit() else i for i in l ]
Out[70]: ['gg', 'k', 'ppp']

In [71]: ''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in l)
Out[71]: 'ggkppp'

编辑

当第一个数字为多位数时,使用re模块:

''.join(re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1)) if re.search('(\d+)(\w+)', i) else i for i in l)

例子:

In [144]: l = ['12g', '2kd', 'h', '3p']

In [145]: ''.join(re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1)) if re.search('(\d+)(\w+)', i) else i for i in l)
Out[145]: 'ggggggggggggkdkdhppp'

编辑2

对于您的输入,例如:

st = '%2g_%3k%3p'
你可以将_替换为空字符串,然后如果列表中的单词以_符号结尾,则在末尾添加_
st = '%2g_%3k%3p'
l = list(filter(None, st.split('%')))
''.join((re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1))).replace("_", "") + '_' * i.endswith('_') if re.search('(\d+)(\w+)', i) else i for i in l)

输出:

'gg_kkkppp'

编辑3

解决方案没有使用re模块,但使用适用于2位数字的常规循环。您可以定义函数:

def add_str(ind, st):
    if not st.endswith('_'):
        return st[ind:] * int(st[:ind])
    else:
        return st[ind:-1] * int(st[:ind]) + '_'

def collect(l):
    final_str = ''
    for i in l:
        if i[0].isdigit():
            if i[1].isdigit():
                final_str += add_str(2, i)
            else:
                final_str += add_str(1, i)
        else:
            final_str += i
    return final_str

然后将它们用作:

l = ['12g_', '3k', '3p']

print(collect(l))
gggggggggggg_kkkppp

2
''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in ['12g', 'k', '3p']) = '2gkppp' - Sayse
1
@MLSC 你只需要针对下划线符号 _ 进行这种行为吗? - Anton Protopopov
@MLSC,你们有最高的数字是多少?它们只有两位数吗?还是可能更多? - Anton Protopopov
@安东,它可以更好...谢谢。 - MLSC
1
@MLSC 请尝试编辑后的版本。您可以根据自己的喜好进行扩展。 - Anton Protopopov
显示剩余7条评论

13

一行正则表达式实现:

>>> import re
>>> st = '%2g%k%3p'
>>> re.sub(r'%|(\d*)(\w+)', lambda m: int(m.group(1))*m.group(2) if m.group(1) else m.group(2), st)
'ggkppp'
%|(\d*)(\w+) 正则表达式匹配所有的 %,并将任何单词字符之前出现的零个或多个数字捕获到一个组中,并将随后的单词字符捕获到另一个组中。 当替换时,所有匹配的字符都应替换为替换部分中给定的值。 因此,应该删除这些匹配的 % 字符。

或者

>>> re.sub(r'%(\d*)(\w+)', lambda m: int(m.group(1))*m.group(2) if m.group(1) else m.group(2), st)
'ggkppp'

你能解释一下为什么你使用了 \b 吗?因为我觉得它不是必要的。 - Iron Fist
测试:re.sub(r'%(\d*)(\w*)', '-RE-',st) ---> '-RE--RE--RE-' - Iron Fist
仅针对小写字母 r'%(\d*)([a-z]+)' - Avinash Raj

11

假设您总是打印单个字母,但以前的数字可能比十进制中的单个数字更长。

seq = ['2g', 'k', '3p']
result = ''.join(int(s[:-1] or 1) * s[-1] for s in seq)
assert result == "ggkppp"

或者作为一行代码:result = ''.join([int(s[:-1] or 1) * s[-1] for s in st.split('%') if s])。你可以给.join一个生成器表达式,但实际上给它一个列表推导更有效率。.join必须解析其输入两次:第一遍确定输出字符串的大小,第二遍构建输出。因此,如果你给.join一个生成器表达式,它必须运行生成器并从中构建一个列表,然后才能开始实际连接。 - PM 2Ring

7

迟到了但准备就绪

另一种方法是定义一个函数,将 nC 转换为 CCCC...C (重复 n 次),然后将其传递给 map 在来自 % 的列表 l 的每个元素上应用它,最后用 join 将它们全部连接起来,代码如下:

>>> def f(s):
        x = 0
        if s:
            if len(s) == 1:
                out = s
            else:
                for i in s:
                    if i.isdigit():
                        x = x*10 + int(i)
                out = x*s[-1]

        else:
            out = ''
        return out

>>> st
'%4g%10k%p'
>>> ''.join(map(f, st.split('%')))
'ggggkkkkkkkkkkp'
>>> st = '%2g%k%3p'
>>> ''.join(map(f, st.split('%')))
'ggkppp'

如果你想把所有这些内容放入一个函数定义中:

>>> def f(s):
        out = ''
        if s:
            l = filter(None, s.split('%'))
            for item in l:
                x = 0
                    if len(item) == 1:
                        repl = item
                    else:
                        for c in item:
                            if c.isdigit():
                                x = x*10 + int(c)
                        repl = x*item[-1]
                    out += repl

        return out

>>> st
'%2g%k%3p'
>>> f(st)
'ggkppp'
>>> 
>>> st = '%4g%10k%p'
>>> 
>>> f(st)
'ggggkkkkkkkkkkp'
>>> st = '%4g%101k%2p'
>>> f(st)
'ggggkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkpp'
>>> len(f(st))
107

编辑:

如果在OP不想让字符_重复出现的情况下存在,我认为最好的方法是使用re.sub,这样可以使事情变得更容易,做法如下:

>>> def f(s):
        pat = re.compile(r'%(\d*)([a-zA-Z]+)')
        out = pat.sub(lambda m:int(m.group(1))*m.group(2) if m.group(1) else m.group(2), s)
        return out

>>> st = '%4g_%12k%p__%m'
>>> f(st)
'gggg_kkkkkkkkkkkkp__m'

@MLSC...在这种情况下,您期望的输出是什么?gg_kkkkkkkkkkkkkppp - Iron Fist
抱歉,我是这样说的:如果我说st = %2g_%12k%3p,结果不会是gg_kkkkkkkkkkkkkppp,甚至当st = %2g___%12k%3p时,结果也必须是gg___kkkkkkkkkkkkkppp - MLSC
@MLSC .. 没问题 .. :) - Iron Fist

4

循环列表,检查第一个条目是否为数字,然后将第二位数字及其后面的数字添加到列表中:

string=''
l = ['2g', 'k', '3p']
for entry in l:
    if len(entry) ==1:
        string += (entry)
    else:
        number = int(entry[0])
        for i in range(number):
            string += (entry[1:])

string 不是一个很好的变量名,因为它是一个标准的 Python 模块的名称。此外,如果一个数字有多于 1 位数,这个答案将无法正常工作。 - PM 2Ring

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接