Вставте рядок у заданий рядок із заданим індексом у Python


77

Я новачок у Python, який зіткнувся з проблемою: Як вставити деякі поля у вже існуючий рядок?

Наприклад, припустимо, я прочитав один рядок з будь-якого файлу, який містить:

line = "Name Age Group Class Profession"

Тепер я повинен вставити 3-те поле (група) у 3 рази більше в той самий рядок перед полем Class. Це означає, що вихідний рядок повинен бути:

output_line = "Name Age Group Group Group Group Class Profession"

Я можу легко отримати 3-те поле (за допомогою splitметоду), але, будь ласка, повідомте мені найпростіший спосіб вставки в рядок?

Відповіді:


139

Важливим моментом, який часто кусає нових програмістів Python, але інші плакати не роблять явного, є те, що рядки в Python незмінні - ви ніколи не можете їх змінити на місці.

Вам потрібно перекваліфікуватися під час роботи зі рядками в Python, щоб замість того, щоб думати: "Як я можу змінити цей рядок?" натомість ти думаєш "як я можу створити новий рядок, який містить деякі фрагменти з цього, який я вже отримав?"


40
Це насправді не виправдовує Python за відсутність індексованої вставки чи заміни! Результатом може бути просто новий рядок, що містить бажаний результат.
Codie CodeMonkey

8
@CodieCodeMonkey, що Python Zen згадує, що "явний краще, ніж неявний". Ви хочете, щоб розробник знав, що він буде працювати над копією. В іншому випадку він, безсумнівно, зіткнеться з проблемами з ідентичністю об’єкта, які буде неприємно налагодити. Мисляча струна - думайте функціонально.
Zakum

10
@Zakum, я розумію вашу думку, але для цього є прецеденти, наприклад str.strip (). Розробник, який уважно не прочитав документацію, може подумати, що strip () оперує оригіналом.
Codie CodeMonkey

2
Це відповіло на запитання без жодного рядка коду. Сила правильного мислення.
jchnxu

4
"how can I create a new string that has some pieces from this one I've already gotten?"Гаразд, але як ?
Випуск

124

Задля майбутніх "новачок", які вирішують цю проблему, я думаю, що швидка відповідь підходить до цієї теми.

Як сказав bgporter : Рядки Python незмінні, і тому для того, щоб змінити рядок, потрібно скористатися частинами, які вже є.

У наступному прикладі я вставляю 'Fu'в to 'Kong Panda', щоб створити'Kong Fu Panda'

>>> line = 'Kong Panda'
>>> index = line.find('Panda')
>>> output_line = line[:index] + 'Fu ' + line[index:]
>>> output_line
'Kong Fu Panda'

У наведеному вище прикладі я використовував значення індексу, щоб «нарізати» рядок на 2 підрядки: 1, що містить підрядок перед індексом вставки, а інший, що містить решту. Потім я просто додаю потрібний рядок між ними і voilà, ми вставили рядок всередину іншого.

Нотація фрагментів Python має чудову відповідь, яка пояснює тему нарізки рядків.


42
Чудова відповідь, однак я вважаю, що назва фільму написана " Кунг- фу панда"
Джеймс Віккері,

9
Насправді, як "пишеться", так і "пишеться" є правильним. "орфографія" - це орфографія Великобританії. "Спельта" неприйнятна лише в Америці, тому що, "му," фермерство ". Вона варіюється від діалекту до діалекту, а" орфографія "настільки ж стара, як і" орфографія ". Наприклад, це "сокира питання" чи "задати питання?" Відповідь обидва - обом зараз майже дві тисячі років, і з цих двох, я вважаю, "сокира" з'являється першою; "запитати" просто було більш популярним.
Matthew T.

1
Зауважте, що find()поверне індекс початку першого входження, і він повернеться, -1якщо пошуковий рядок не знайдено. Отже, це не буде узагальнено до ситуацій, коли рядок інтересів, у цьому випадку Panda, може відбуватися кілька разів або взагалі не відбуватися.
tony_tiger

19

Я знаю, що це погано, але IMHO простий спосіб:

def insert (source_str, insert_str, pos):
    return source_str[:pos]+insert_str+source_str[pos:]

2
що в цьому
малозагальне

1
@sehe Він використовує конкатенацію рядків, що не дуже ефективно
Роб Смальлшир,

Дякую, я сподівався, що ви додасте пояснення до відповіді. Не всі зрозуміли б мінус, і людям корисно це знати :)
sehe,

@RobSmallshire Що може бути більш ефективним методом?
Jacob Jones

@JacobJones Використання методу об'єднання str, швидше за все, було б більш ефективним, потенційно уникаючи великих проміжних результатів. return '' .join ((source_str [: pos], insert_str, source_str [pos:]))
Роб Смальлшир

7
line='Name Age Group Class Profession'
arr = line.split()
for i in range(3):
    arr.insert(2, arr[2])
print(' '.join(arr))

str.join () має перевагу зменшення використання пам'яті, оскільки не створює проміжні рядки (хоча IIRC це було оптимізовано в останніх версіях CPython / PyPy). З іншого боку, він повільніший за конкатенацію, коли поєднує лише кілька рядків, і тому є найбільш корисним для роботи з великими рядками або коли вам інакше довелося б виконати велику кількість конкатенацій. У Python 3.6 ви можете використовувати f-рядки замість конкатенації, щоб зберегти кілька додаткових циклів процесора, коли кількість підстановок фіксована (наприклад, f '{source_str [: pos]} {insert_str} {source_str [pos:]}' ' ).
kgriffs

4

Є кілька способів зробити це:

Один із способів - використовувати нарізання:

>>> a="line=Name Age Group Class Profession"
>>> b=a.split()
>>> b[2:2]=[b[2]]*3
>>> b
['line=Name', 'Age', 'Group', 'Group', 'Group', 'Group', 'Class', 'Profession']
>>> a=" ".join(b)
>>> a
'line=Name Age Group Group Group Group Class Profession'

Іншим було б використання регулярних виразів:

>>> import re
>>> a=re.sub(r"(\S+\s+\S+\s+)(\S+\s+)(.*)", r"\1\2\2\2\2\3", a)
>>> a
'line=Name Age Group Group Group Group Class Profession'

4

У мене була подібна проблема при призначенні ДНК, і я скористався порадою bgporter, щоб відповісти на неї. Ось моя функція, яка створює новий рядок ...

def insert_sequence(str1, str2, int):
    """ (str1, str2, int) -> str

    Return the DNA sequence obtained by inserting the 
    second DNA sequence into the first DNA sequence 
    at the given index.

    >>> insert_sequence('CCGG', 'AT', 2)
    CCATGG
    >>> insert_sequence('CCGG', 'AT', 3)
    CCGATG
    >>> insert_sequence('CCGG', 'AT', 4)
    CCGGAT
    >>> insert_sequence('CCGG', 'AT', 0)
    ATCCGG
    >>> insert_sequence('CCGGAATTGG', 'AT', 6)
    CCGGAAATTTGG

    """

    str1_split1 = str1[:int]
    str1_split2 = str1[int:]
    new_string = str1_split1 + str2 + str1_split2
    return new_string

1

Впровадження

Функції нижче дозволять вставити один рядок в інший рядок:

def str_insert(from_me, into_me, at):
    """
    Inserts the string <from_me> into <into_me>

    Input <at> must be an integer index of <into_me> or a substring of <into_me>

    Inserts <from_me> AFTER <at>, not before <at>

    Inputs <from_me> and <into_me> must have working __str__ methods defined.
    This is satisfied if they already are strings.

    If not already strings, <from_me>, <into_me> are converted into strings.

    If you try to insert an empty string, that's fine, and the result
    is no different from the original.

    In order to insert 'from_me' after nothing (insert at the beginning of the string) use:
        at = ''  or  at = 0
    """
    try:
        return str_insert_or_raise(from_me, into_me, at)
    except ValueError as err:
        serr = str(err)
        if (str_insert_or_raise.__name__ in serr) and 'not found' in serr and '<at>' in serr:
            # if can't find where to insert stuff, don't bother to insert it
            # use str_insert_or_raise if you want an exception instead
            return into_me
        else:
            raise err

##############################################################

def str_insert_or_raise(from_me, into_me, at):
    """
    Inserts the string <from_me> into <into_me>

    Inserts <from_me> AFTER <at>, not before <at>

    Input <at> must be an integer index of <into_me> or a substring of <into_me>

    If <at> is the string '15', that substring will be searched for,
    '15' will not be interpreted as an index/subscript.        

    Inputs <from_me> and <into_me> must have working __str__ methods defined.
    If not already strings, <from_me>, <into_me> are converted into strings. 

    If you try to insert something, but we cannot find the position where
    you said to insert it, then an exception is thrown guaranteed to at least
    contain the following three substrings:
        str_insert_or_raise.__name__
        'not found'
        '<at>'
    """
    try:
        if isinstance(at, int):
            return str_insert_by_int(from_me, into_me, at)
        # Below, the calls to str() work fine if <at> and <from_me> are already strings
        # it makes them strings if they are not already
        return str_insert_by_str(str(from_me), str(into_me), str(at))
    except ValueError as err:
        serr = str(err)
        if 'empty string' in serr:
            return into_me # We allow insertion of the empty string
        elif ("<at>" in serr) and 'not found' in serr:
            msg_start = "In " + str_insert_or_raise.__name__ + ":  "
            msg = [msg_start, "\ninput ", "<at> string", " not found in ", "<into_me>",
                              "\ninput <",   str(at)  , "> not found in <", str(into_me), ">"]
            msg = ''.join(msg)
            raise ValueError(msg) from None
        else:
           raise err
#############################################################
def str_insert_by_str(from_me, into_me, at):
    """
    Inserts the string <from_me> into <into_me>

    puts 'from_me' AFTER 'at', not before 'at'
    For example,
        str_insert_or_raise(at = '2',  from_me = '0', into_me = '123')
    puts the zero after the 2, not before the 2
    The call returns '1203' not '1023'

    Throws exceptions if input arguments are not strings.

    Also, if <from_me> is empty or <at> is not a substring of <into_me> then
    an exception is raised.

    For fewer exceptions, use <str_insert_or_raise> instead.
    """
    try:
        s = into_me.replace(at, at + from_me, 1)
    except TypeError as terr: # inputs to replace are not strings
        msg_list = ['Inputs to function ', str_insert_by_str.__name__, '() must be strings']
        raise TypeError(''.join(msg_list)) from None
    # At the end of call to replace(), the '1'  indicates we will replace
    # the leftmost occurrence of <at>, instead of every occurrence of <at>
    if (s == into_me): # <at> string not found and/or <from_me> is the empty string
        msg_start = "In " + str_insert_by_str.__name__ + ":  "
        if from_me == '':
            msg = ''.join([msg_start, "attempted to insert an empty string"])
            raise ValueError(msg) from None
        raise ValueError(msg_start, "Input <at> string not found in <into_me>.",
                                    "\nUnable to determine where you want the substring inserted.") from None
    return s
##################################################
def str_insert_by_int(from_me, into_me, at):
    """
    * Inserts the string <from_me> into <into_me> at integer index <at>    
    * throws exceptions if input arguments are not strings.    
    * Also, throws an  exception if you try to insert the empty string    
    * If <at> is less than zero, <from_me> gets placed at the
      beginning of <into_me>    
    * If <at> is greater than the largest index of <into_me>,
      <from_me> gets placed after the end of <into_me>

    For fewer exceptions, use <str_insert_or_raise> instead.
    """
    at = into_me[:(at if at > 0 else 0)]
    return str_insert_by_str(from_me, into_me, at)

Використання

У наведеному нижче коді показано, як викликати str_insertфункцію, подану раніше

def foo(*args):
    return args

F = 'F. '

s = 'Using the string \'John \' to specify where to make the insertion'
result = str_insert(from_me = F, into_me ='John Kennedy', at ='John ')
print(foo('\n\n', s, '\n', result))

s = 'Using an int returned by find(\'Ken\') to specify where to make the insertion'
index = 'John Kennedy'.find('Ken') # returns the position of the first letter of 'Ken', not the last letter
result = str_insert(from_me = F, into_me ='John Kennedy', at = index)
print(foo('\n\n', s, '\n', result))

s = 'Using an int (5) to specify where to make the insertion.'
result = str_insert(from_me = F, into_me ='John Kennedy', at = 5)
print(foo('\n\n', s, '\n', result))

s = "Looking for an 'at' string which does not exist"
result = str_insert(from_me = F, into_me ='John Kennedy', at ='x')
print(foo('\n\n', s, '\n', result))

s = ''.join(["Looking for the empty string.",
             "\nFind one immediately at the beginning of the string"])
result = str_insert(from_me = F, into_me ='John Kennedy', at = '')
print(foo('\n\n', s, '\n', result))

s = "Insert an empty string at index 3. No visible change"
result = str_insert(from_me = '', into_me = 'John Kennedy', at = 3)
print(foo('\n\n', s, '\n', result))    

for index in [-5, -1, 0, 1, 997, 999]:
    s = "index " + str(index)
    result = str_insert(from_me = F, into_me = 'John Kennedy', at = index)
    print(foo('\n\n', s, '\n', result))

Попередження про відсутність можливості змінювати на місці

Жодна з функцій вище не змінить рядок "in-place". Кожна функція повертає змінену копію рядка, але оригінальний рядок залишається цілим.

Наприклад,

s = ''.join(["Below is what we get when we forget ",
             "to overwrite the string with the value",
             " returned by str_insert_or_raise:"])

examp_str = 'John Kennedy'
str_insert('John ', F, examp_str)
print(foo('\n\n', s, '\n', examp_str))

# examp_str is still 'John Kennedy' without the F

0

Відповідь на Вставлення символів рядка в інші рядки, розташовані за позиціями

str1 = "ibuprofen"
str2 = "MEDICAL"
final_string=""
Value = 2
list2=[]
result=[str1[i:i+Value] for i in range(0, len(str1), Value)]
count = 0

for letter in result:
    if count < len(result)-1:
        final_string = letter + str2[count]
        list2.append(final_string)
    elif ((len(result)-1)==count):
        list2.append(letter + str2[count:len(str2)])
        break
    count += 1

print(''.join(list2))

Примітка: відповів на stackoverflow.com/questions/53168288/…, оскільки відповідь там була відключена і обманута цим
pavan kumar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.