LINUX.ORG.RU

Замена в строке с помощью Python

 


0

3

Большая просьба помочь. У меня есть несколько одинаковых слов в строке. Нужно произвести замену лишь некоторых из этих одинаковых слов. В питоне есть метод: S.replace(old, new) — заменить в строке S ВСЕ ВХОЖДЕНИЯ подстроки old на подстроку new. А если мне надо заменить не все, а например пятое или третье, то как быть??? S.replace(old[5], new) - вот так не работает. все равно во всех заменяет. Неужели в питоне нет решений для казалось бы такой простой задачи??

Вообще re.sub для этого лучше использовать, а так можешь и по строке как по массиву пробежаться.

alozovskoy ★★★★★
()

Делай поиск слова в строке, пока не найдешь нужное смещение.

panter_dsd ★★★★
()
Ответ на: комментарий от alozovskoy

sub тоже ищет все совпадения, как и replace. Ну неужели не реализована возможность замены только того, что нужно((

cdi1989
() автор топика
Ответ на: комментарий от cdi1989

sub умеет в регулярки. Разбей строку на подстроки, замени нужные подстроки и объедини обратно.

alozovskoy ★★★★★
()
Ответ на: комментарий от cdi1989
pos = 0
s = '-------'
replace_text = '-------'
for i in range(5):
    pos = s.find(replace_text, pos)
s = s[:pos] + 'some_new_text' + s[pos+len(replace_text):]

На примерно то, что нужно сделать. Тестируй, могут быть баги, не проверял. Обязательно сделай бэкап!

panter_dsd ★★★★
()

Неужели в питоне нет решений для казалось бы такой простой задачи??

А в каком языке есть?

pi11 ★★★★★
()
Python 3.4.2 (v3.4.2:ab2c023a9432, Oct  6 2014, 22:15:05) [MSC v.1600 32 bit (In
tel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> s = 'test 123 test 456 test 789 test 0 test'
>>> # replace 2'nd and 5'th test by TEST
...
>>> re.sub('(.*test.*)(test)(.*test.*test.*)(test)(.*)', r'\1TEST\3TEST\5', s)
'test 123 TEST 456 test 789 test 0 TEST'
>>>

Если я правильно понял, что тебе нужно.

winlook38 ★★
()

Python не имеет встроенных функций управления алгоритмом поиска, так сказать event-driven functions. Обычно такое пишется «на коленке».

# -*- coding: utf-8 -*-

s = "abc 123 abc 345 abc ABC def ABC abc abc"

import re
def fr(s,subs,n):
  f = re.compile("abc")
  b = "" # начало строки
  for i in xrange(n):
    r = f.search(s)
    if r == None: print "None"; continue
    k1 = r.start()
    k2 = r.end()
    if i == n-1:
      print b+s[:k1]+"---"+s[k2:] # на последней итерации
    b = b + s[:k2] # наращиваем начало
    s = s[k2:] # остается после i-го найденного фрагмента

fr(s,"abc",3)
fr(s,"abc",5)
fr(s,"abc",6)

pacify ★★★★★
()
def custom_replace(source, substr, repl, num):
    """
    """
    tmp = source.split(substr)
    if (len(tmp)-1) < num:
        return source
    return substr.join(tmp[:num])+repl+substr.join(tmp[num:])
s_str = 'ноладындватриадынчетыреадынадынадын'
print(custom_replace(s_str, 'адын', '1', 4))

> ноладындватриадынчетыреадын1адын

vvn_black ★★★★★
()
Последнее исправление: vvn_black (всего исправлений: 2)

Вообще у str.replace есть третий аргумент - count. Будет заменять n первых попавшихся слов, а не все.

Так что можешь отрезать подстроку с начала, котороче как-то так:

s  = 'test test test'
result s[:5]+s[5:].replace('test','TEST',1)
#'test TEST test'
pawnhearts ★★★★★
()

Не знаю питон, на перле будет как-то так:

$sm = "test1 test2 test3 test4";
@sm = split ' ', $sm; #преобразуем строку в массив
@sm1 = ("$sm[0]", "$sm[2]"); #второй массив, с нужными нам элементами
s/e/ж/ for @sm1; #делаем замену
$sm[0] = $sm1[0]; #заменяем элементы
$sm[2] = $sm1[1];
$sm = join ' ', @sm; #преобразуем массив в строку
print $sm
tжst1 test2 tжst3 test4
Очень коряво написано, конечно, но зато работает, а выше по треду никто ничего работающего не предложил.

svobodka_fighter
()

Первое, что пришло в голову:

def replaceone(s, oldpart, newpart, num):
    parts = s.split(oldpart, num)
    return oldpart.join(parts[:num]) + newpart + parts[num]

replaceone('раз два три тест три четыре тест пять тест шесть сем тест восемь', "тест", "foo", 3)
'раз два три тест три четыре тест пять foo шесть сем тест восемь'

По-хорошему, ещё бы не мешало проверять, не выходит ли num за пределы количества вхождений oldpart, но суть ясна.

Psych218 ★★★★★
()
Последнее исправление: Psych218 (всего исправлений: 4)
$ python
Python 2.7.10 (default, Jul  5 2015, 14:15:43) 
[GCC 5.1.1 20150618 (Red Hat 5.1.1-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a="111abc22222abc3333abc44abc55555abc6666abc777"
>>> type(a)
<type 'str'>
>>> b = a.split("abc")
>>> type(b)
<type 'list'>
>>> b
['111', '22222', '3333', '44', '55555', '6666', '777']
>>> c = b[0] + "abc" + b[1] + "___xxx___" + b[2] + "abc" + b[3] + "abc" + b[4] + "___xxx___" + b[5] + "abc" + b[6]
>>> type(c)
<type 'str'>
>>> c
'111abc22222___xxx___3333abc44abc55555___xxx___6666abc777'
justAmoment ★★★★★
()
Ответ на: комментарий от Psych218

Ну то есть, как-то так:

def replaceone(s, oldpart, newpart, num):
    parts = s.split(oldpart, num)
    if len(parts) < num+1:
        return s  # возвращаем оригинал строки, если заменяется допустим восьмое вхождение, когда их всего семь
    else:
        return oldpart.join(parts[:num]) + newpart + parts[num]

Psych218 ★★★★★
()
Ответ на: комментарий от justAmoment

через join

>>> z = "___xxx___".join(["abc".join(b[0:2]), "abc".join(b[2:5]), "abc".join(b[5:])])
>>> type(z)
<type 'str'>
>>> z
'111abc22222___xxx___3333abc44abc55555___xxx___6666abc777'

justAmoment ★★★★★
()

Такое легко пишется на коленке. Как-то так, например:

def replacer(s, word, replacement, tbl=[0,1,0]):
  out = []
  for flag, chunk in zip(tbl, s.split(word)):
      out.append(replacement) if flag else out.append(word)
  return "".join(out)

Где tbl=[0,1,0] говорит что только второе вхождение надо менять. Код не проверял, сам отладишь :)

true_admin ★★★★★
()
Последнее исправление: true_admin (всего исправлений: 1)

После твоего треда, непременно кто-нибудь запилит библиотеку для таких случаев.

javaQest
()
import re

def subn(string, old, new, pos):
    count = 0
    def subber(match):
        nonlocal count
        count += 1
        if count == pos:
            return new
        else:
            return old
    return re.sub(old, subber, string)
Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от javaQest
import re
from itertools import count
c=count(); re.sub('foo', lambda m: 'bar' if c.next() in {2, 5} else m.group(), "foo foo foo foo foo foo foo foo foo")
# 'foo foo bar foo foo bar foo foo foo'
Davidov ★★★★
()

Странно что оффсеты не принимает эта функция, аналог в cl принимает start и end, тогда можно делать поиск и замену.

loz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.