Как заменить повторяющиеся экземпляры символа одним экземпляром этого символа в python - PullRequest
17 голосов
/ 07 октября 2010

Я хочу заменить повторяющиеся экземпляры символа "*" в строке одним экземпляром "*".Например, если строка "***abc**de*fg******h", я хочу, чтобы она была преобразована в "*abc*de*fg*h".

Я довольно плохо знаком с Python (и программирую в целом) и пытался использовать регулярные выражения и string.replace() вроде:

import re    
pattern = "***abc**de*fg******h"
pattern.replace("*"\*, "*")

, где \* должен заменить все вхождения символа "*".Но я получил: SyntaxError: неожиданный символ после символа продолжения строки.

Я также пытался манипулировать им с помощью цикла for наподобие:

def convertString(pattern):
for i in range(len(pattern)-1):
    if(pattern[i] == pattern[i+1]):
        pattern2 = pattern[i]
return pattern2

, но при этом он выдает ошибку "* "потому что pattern2 = pattern [i] постоянно переопределяет, что такое pattern2 ...

Буду признателен за любую помощь.

Ответы [ 10 ]

21 голосов
/ 07 октября 2010

Наивный способ сделать это с помощью re -

re.sub('\*+', '*', text)

, который заменяет серии из 1 или более звездочек одной звездочкой.Для пробегов ровно одной звездочки, она работает очень тяжело, просто чтобы оставаться на месте.Намного лучше заменить несколько звездочек ДВА или более одной звездочкой:

re.sub('\*\*+', '*', text)

Это может стоить сделать:

\python27\python -mtimeit -s"t='a*'*100;import re" "re.sub('\*+', '*', t)"
10000 loops, best of 3: 73.2 usec per loop

\python27\python -mtimeit -s"t='a*'*100;import re" "re.sub('\*\*+', '*', t)"
100000 loops, best of 3: 8.9 usec per loop

Обратите внимание, что.sub вернет ссылку на входную строку, если не найдет совпадений, сохраняя больше износа на вашем компьютере, вместо целой новой строки.

6 голосов
/ 07 октября 2010

как насчет не-регулярных выражений

def squeeze(char,s):
    while char*2 in s:
        s=s.replace(char*2,char)
    return s
print squeeze("*" , "AB***abc**def**AA***k")
4 голосов
/ 07 октября 2010

Я бы предложил использовать подфункцию re module :

import re

result = re.sub("\*+", "*", "***abc**de*fg******h")

Я настоятельно рекомендую прочитать статью о RE и передовой практике.Они могут быть хитрыми, если вы не знакомы с ними.На практике использование сырых строк - хорошая идея.

2 голосов
/ 07 октября 2010

Вы писали:

pattern.replace("*"\*, "*")

Вы имели в виду:

pattern.replace("\**", "*")
#                ^^^^

Вы действительно имели в виду:

pattern_after_substitution= re.sub(r"\*+", "*", pattern)

, которая делает то, что вы хотели.

1 голос
/ 07 октября 2010

Что ж, с регулярными выражениями я бы поступил именно так, как предложил Джош. Но одно улучшение здесь.

Использование -

regex  = re.compile('\*+')
result = re.sub(regex, "*", string)

Это, по сути, кеширует ваше регулярное выражение. Поэтому последующее использование этого в цикле сделает ваши операции с регулярными выражениями быстрыми.

0 голосов
/ 24 января 2019

Я рассчитал все методы в текущих ответах (с Python 3.7.2, macOS High Sierra).

b() был лучшим в целом, c() был лучшим, когда не было найдено совпадений.

def b(text):
    re.sub(r"\*\*+", "*", text)

# aka squeeze()
def c(text):
    while "*" * 2 in text:
        text = text.replace("*" * 2, "*")
    return text

Ввод 1, без повторов: 'a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*'

  • а) 10000 циклов, лучшее из 5: 24,5 мксек на цикл
  • b) 100000 циклов, лучшее из 5: 3,17 мксек на цикл
  • c) 500000 циклов, лучшее из 5: 508 нс на цикл
  • d) 10000 петель, лучшее из 5: 25,4 мксек на петлю
  • e) 5000 петель, лучшее из 5: 44,7 усек за петлю
  • f) 500000 циклов, лучшее из 5: 522 нсек на цикл

Вход 2, с повторениями: 'a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*****************************************************************************************************a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*'

  • а) 5000 циклов, лучшее из 5: 46,2, т. Е. На цикл
  • b) 50000 петель, лучшее из 5: 5,21 мксек на петлю
  • в) 20000 циклов, лучшее из 5: 13,4 мксек на цикл
  • d) 5000 петель, лучшее из 5: 47,4 на одну петлю
  • e) 2000 петель, лучшее из 5: 103 мксек на петлю
  • f) 20000 циклов, лучшее из 5: 13,1, т. Е. На цикл

b() is best overall, c() best if no matches

Методы:

#!/usr/bin/env python
# encoding: utf-8
"""
See which function variants are fastest. Run like:
python -mtimeit -s"import time_functions;t='a*'*100" "time_functions.a(t)"
python -mtimeit -s"import time_functions;t='a*'*100" "time_functions.b(t)"
etc.
"""
import re


def a(text):
    return re.sub(r"\*+", "*", text)


def b(text):
    re.sub(r"\*\*+", "*", text)


# aka squeeze()
def c(text):
    while "*" * 2 in text:
        text = text.replace("*" * 2, "*")
    return text


regex = re.compile(r"\*+")


# like a() but with (premature) optimisation
def d(text):
    return re.sub(regex, "*", text)


def e(text):
    return "".join(c for c, n in zip(text, text[1:] + " ") if c + n != "**")


def f(text):
    while True:
        if "**" in text:  # if two stars are in the variable pattern
            text = text.replace("**", "*")  # replace two stars with one
        else:  # otherwise
            break  # break from the infinite while loop
    return text
0 голосов
/ 11 декабря 2017

Это будет работать для любого количества последовательных звездочек, хотя вам может потребоваться заменить тильду другой строкой, которая, как вы знаете, будет уникальной во всей строке.

    string = "begin*************end"

    string.replace("**", "~*").replace("*~", "").replace("~*", "*").replace("**", "*")

Я полагаю, что подходы к регулярным выражениям, как правило, были бы более вычислительно дорогими, чем этот.

0 голосов
/ 20 августа 2017

Предположим, что в этом примере ваш персонаж - пробел.

Вы также можете сделать это следующим образом:

while True:
    if "  " in pattern: # if two spaces are in the variable pattern
        pattern = pattern.replace("  ", " ") # replace two spaces with one
    else: # otherwise
        break # break from the infinite while loop

Это:

File Type                       : Win32 EXE
File Type Extension             : exe
MIME Type                       : application/octet-stream
Machine Type                    : Intel 386 or later, and compatibles
Time Stamp                      : 2017:04:24 09:55:04-04:00

Становится:

File Type : Win32 EXE
File Type Extension : exe
MIME Type : application/octet-stream
Machine Type : Intel 386 or later, and compatibles
Time Stamp : 2017:04:24 09:55:04-04:00

Я считаю, что это немного легче, чем возиться с модулем re, который иногда может немного раздражать (я думаю).

Надеюсь, чтобыло полезно.

0 голосов
/ 07 октября 2010

без регулярного выражения вы можете использовать общее удаление повторяющегося элемента с проверкой '*':

source = "***abc**dee*fg******h"
target = ''.join(c for c,n in zip(source, source[1:]+' ') if  c+n != '**')
print target
0 голосов
/ 07 октября 2010
re.sub('\*+', '*', pattern)

Это будет делать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...