Слияние двух почти одинаковых строк - PullRequest
3 голосов
/ 28 мая 2020

У меня есть два объекта, один из которых представляет собой список кортежей с (int, str), например:

first_input = [
    (0  ,  "Lorem ipsum dolor sit amet, consectetur"),
    (1  ,  " adipiscing elit"),
    (0  ,  ". In pellentesque\npharetra ex, at varius sem suscipit ac. "),
    (-1 ,  "Suspendisse luctus\ncondimentum velit a laoreet. "),
    (0  ,  "Donec dolor urna, tempus sed nulla vitae, dignissim varius neque.")
]
# Note that the strings contain newlines `\n` on purpose.

Другой объект - это строка, которая является результатом серии операций (* ), что по замыслу приведет к объединению всех вышеперечисленных строк, но с некоторыми дополнительными вставками новой строки \n.

(*: очевидно, что этого нельзя сделать при сохранении структуры list of tuples)

Например:

second_input = "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit. In pellentesque\npharetra ex, at varius sem\nsuscipit ac. Suspendisse luctus\ncondimentum velit a laoreet. Donec dolor urna, tempus sed\nnulla vitae, dignissim varius neque."
# Note that there are 3 new newlines,  here ^ for instance
# but also in "sem\nsuscipit" and "sed\nnulla"

Моя цель - go вернуться к первой структуре, но с сохранением дополнительных символов новой строки. Итак, в моем примере я бы получил:

expected_output = [
    (0  ,  "Lorem ipsum dolor sit amet,\nconsectetur"),  # new newline here
    (1  ,  " adipiscing elit"),
    (0  ,  ". In pellentesque\npharetra ex, at varius sem\nsuscipit ac. "), # new newline here
    (-1 ,  "Suspendisse luctus\ncondimentum velit a laoreet. "),
    (0  ,  "Donec dolor urna, tempus sed\nnulla vitae, dignissim varius neque.") # new newline here
]

Есть ли у вас разумный способ сделать это, кроме восстановления строки с символом путем сравнения символов?

(NB: I неважно, в каком из двух кортежей он заканчивается, если новый \n находится на границе строки. Например, получение [(0, "foo\n"), (1, "bar")] или [(0, "foo"), (1, "\nbar")] не имеет значения.)


Изменить: чего я хочу избежать, так это сделать что-то вроде этого:

position=0
output = []
for tup in first_input:
    reconstructed_string = ""
    for letter in tup[1]:
        if letter == second_input[position]:
            reconstructed_string = reconstructed_string + letter
        else:
            reconstructed_string = reconstructed_string + second_input[position]
        position +=1
    output.append((tup[0], reconstructed_string))
# Note: this is hastily written to give you an idea, I have no idea if it would work properly, probably not
# Well, it does seem to work without bug, at least in my example. That's unexpected lol. Anyway, if you can think of a better solution...!

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

Ответы [ 3 ]

1 голос
/ 28 мая 2020

Я думаю, что самый простой способ - это перевести все операции, которые вы выполняете над объединенной строкой, обратно на части, но я думаю, вы уже думали об этом. Вместо этого нельзя было вставить какие-либо символы новой строки, но можно было создать список позиций, в которые они могли бы быть введены . Отслеживая длину битов строки, это могло бы выглядеть так, если предположить, что позиции, в которых '' должен быть заменен на '\ n', хранятся в переменной posis:

 import numpy as np
 posis = [27,98187,227] # position of the newlines in your sample, length of full string as last entry
 lengths = [len(string) for _, string in first_input]
 covered_distance = 0 # lengths of all strings we looked at already                                           
 j = 0  # iterating index for positions                                                                       
 output = []                                                                     
 rel_pos = posis[0]-covered_distance # initialize relative position in the current string                    
 inserted_newlines = 0 # keep track of newlines we added already                  
 for i, [n, string] in enumerate(first_input):                                                                           
     while rel_pos < lengths[i]:                                                 
         string = string[:rel_pos+inserted_newlines]+'\n'\                       
                 +string[rel_pos+inserted_newlines+1:]  # replace the character at the relative position                         
         j += 1 # advance to the next newline to be inserted                              
         rel_pos = posis[j]-covered_distance # update the relative position                     
         inserted_newlines += 1  # keep track of inserted newlines      
     output.append((n, string))  # store resulting string               
     covered_distance += lengths[i]  # update the number of characters we passed                        
     rel_pos = posis[j]-covered_distance                                         

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

0 голосов
/ 29 мая 2020

Хорошо, учитывая, что НИКАКИЕ СИМВОЛЫ не заменяются или не изменяются (как указано в OP), вот что я мог бы придумать:

first_input_no_newline = list(map(lambda x: (x[0], x[1].replace('\n', '')), first_input))

expected_output = []
for item in first_input_no_newline:
    next_index = len(item[1])

    second_input_copy = second_input
    offset = 0
    while True:
        amount = second_input_copy[:next_index].count("\n")
        if not amount:
            next_index += offset
            break
        offset += amount
        second_input_copy = second_input_copy.replace('\n', '', amount)

    expected_output.append((item[0], second_input[:next_index]))
    second_input = second_input[next_index:]

print(expected_output)

Объяснение: вам не нужно отслеживать новые строки или что-нибудь в этом роде. Кроме того, новые строки в "first_input" не имеют особого значения, потому что у нас все они находятся во втором вводе (плюс их больше).

Итак, просто возьмите длину каждого элемента first_input_no_newline, это также должна быть длина подстроки в second_input, если в ней не было новых строк, но, если есть новые строки, хорошо, просто продолжайте подсчитывать и удаляя их из копии second_input и добавляйте этот результат как смещение для вырезания исходный second_input.

Пример ввода (фиксированный исходный ввод OP, добавление недостающих белых символов между некоторыми фразами):

first_input = [
        (0, "Lorem ipsum dolor sit amet, consectetur"),
        (1, " adipiscing elit"),
        (0, ". In pellentesque\npharetra ex, at varius sem suscipit ac. "),
        (-1, "Suspendisse luctus\ncondimentum velit a laoreet. "),
        (0, "Donec dolor urna, tempus sed nulla vitae, dignissim varius neque.")
    ]

second_input = "Lorem ipsum dolor sit amet, \nconsectetur adipiscing elit. In pellentesque\npharetra ex, at varius sem \nsuscipit ac. Suspendisse luctus\ncondimentum velit a laoreet. Donec dolor urna, tempus sed \nnulla vitae, dignissim varius neque."

Вывод:

[
    (0, 'Lorem ipsum dolor sit amet, \nconsectetur'), 
    (1, ' adipiscing elit'), 
    (0, '. In pellentesque\npharetra ex, at varius sem \nsuscipit ac. '), 
    (-1, 'Suspendisse luctus\ncondimentum velit a laoreet. '), 
    (0, 'Donec dolor urna, tempus sed \nnulla vitae, dignissim varius neque.')
]

0 голосов
/ 28 мая 2020

Как я бы это сделал - написано ужасным кодом. Я написал это довольно поспешно </p> <pre><code>import re first_input = [ (0 , "Lorem ipsum dolor sit amet, consectetur"), (1 , " adipiscing elit"), (0 , ". In pellentesque\npharetra ex, at varius sem suscipit ac. "), (-1 , "Suspendisse luctus\ncondimentum velit a laoreet. "), (0 , "Donec dolor urna, tempus sed nulla vitae, dignissim varius neque.") ] second_input = "Lorem ipsum dolor sit amet,\n consectetur adipiscing elit. In pellentesque\npharetra ex, at varius sem\n suscipit ac. Suspendisse luctus\ncondimentum velit a laoreet. Donec dolor urna, tempus sed\n nulla vitae, dignissim varius neque." first_sanitized = [x[1].replace('\n', '') for x in first_input] second_sanitized = second_input.replace('\n', '') newline_positions = [m.start() for m in re.finditer('\n' ,second_input, re.M)] new_output = [] i = 0 print(second_sanitized) newlines_so_far = 0 for first_str in first_sanitized: print(first_str) index = second_sanitized.index(first_str) number_of_newlines_in_between = sum([1 for x in newline_positions if (x > index and x < index + len(first_input[i][1]))]) new_string = second_input[index : (index + len(first_input[i][1]) + number_of_newlines_in_between + newlines_so_far)] newlines_so_far += number_of_newlines_in_between new_element = (first_input[i][0], new_string) new_output.append(new_element) i = i + 1

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