Замените пробел на символ новой строки в пределах максимальной длины строки в python

#python #python-3.x

#python #python-3.x

Вопрос:

Я пытаюсь написать скрипт на Python, который разбивает непрерывную строку на строки, когда max_line_length было превышено.

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

По какой-то причине он не прерывается в пределах указанного предела.
Например, при определении max_line_length = 80 текст иногда прерывается на 82 или 83 и т.д.

С некоторых пор я пытаюсь решить проблему, однако мне кажется, что у меня туннельное видение и я не вижу проблемы здесь:

 #!/usr/bin/python
import sys

if len(sys.argv) < 3:
    print('usage:   $ python3 breaktext.py <max_line_length> <file>')
    print('example: $ python3 breaktext.py 80 infile.txt')
    exit()

filename = str(sys.argv[2])
with open(filename, 'r') as file:
    text_str = file.read().replace('n', '')

    m = int(sys.argv[1])        # max_line_length
    text_list = list(text_str)  # convert string to list
    l = 0;                      # line_number
    i = m 1                     # line_character_index
    index = m 1                 # total_list_index
    while index < len(text_list):
        while text_list[l * m   i] != ' ':
            i -= 1
            pass
        text_list[l * m   i] = 'n'
        l  = 1
        i = m 1
        index  = m 1
        pass

    text_str = ''.join(text_list)
    print(text_str)
  

Комментарии:

1. Почему вы поместили имена переменных в комментарии? Зачем преобразовывать строку в список, когда вы можете просто разрезать или проиндексировать строку? Почему вы предполагаете, что все строки имеют одинаковую длину в l * m ? Я думаю, что последнее является частью вашей проблемы, потому что невозможно сказать, как далеко i продвинулись назад, прежде чем пропустить вперед другие m символы… вполне возможно, что более ранняя строка снова была разделена.

2. @YannVernier 1) Потому что литералы делают код более читаемым, чем слишком длинные объяснительные имена переменных. 2) Я привык работать с указателями из C , в Python невозможно изменить строку посредством индексации, однако со списками это возможно. 3) Что вы имеете в виду, предполагая, что все строки имеют одинаковую длину? Есть только одна очень длинная строка, которая должна быть отформатирована в несколько маленьких строк.

3. textwrap Модуль stdlib, вероятно, будет делать то, что вы хотите, с гораздо меньшим количеством кода с вашей стороны.

4. @PaulMcG import textwrap with print(textwrap.fill(text_str, width=m)) справился с задачей, тай! Тем не менее, я не понимаю, в чем проблема выше :/

Ответ №1:

Я думаю, мы возьмем это сверху.

 text_str = file.read().replace('n', '')
  

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

 text_list = list(text_str)  # convert string to list
  

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

 index = m 1                 # total_list_index
while index < len(text_list):
    #...
    index  = m 1
  

Давайте рассмотрим, что это значит. Мы не входим в цикл, если index превышает text_list длину. Но index продвигается с шагом m 1 . Итак, мы разделяем math.floor(len(text)/(max_line_length 1)) время. Если каждая строка не содержит ровно max_line_length символов, не считая пробела, который мы заменяем новой строкой, это слишком мало раз. Слишком мало раз означает слишком длинные строки, по крайней мере, в конце.

 l = 0;                      # line_number
i = m 1                     # line_character_index
#loop:
    while text_list[l * m   i] != ' ':
        i -= 1
    text_list[l * m   i] = 'n'
    l  = 1
    i = m 1
  

Это усложняет математику индекса. Совершенно очевидно, что один индекс, который мы когда-либо использовали, l * m i . Это происходит довольно странным образом; он ищет пробел в обратном направлении, затем переходит вперед с l увеличением и i сбросом. В какую бы позицию он ни вернулся, она теряется, поскольку все переходы выполняются с шагом m .

Давайте применим m=5 к строке "Fee fie faw fum who did you see now" . Для первой итерации 0 * 5 5 1 попадает на второе слово и i возвращается к первому пробелу. Тогда первая строка — «Плата», как и ожидалось. Второй поиск начинается с 1*5 5 1 , который является пробелом, а вторая строка становится «fie faw», что уже превышает наш лимит в 5! Причина в том, что l * m это не начало строки; на самом деле это в середине «fie», несоответствие, которое может только увеличиваться по мере продолжения работы с файлом. Он увеличивается всякий раз, когда вы отделяете строку, которая короче m .

Решение включает в себя запоминание того, где вы сделали разделение. Это может быть так же просто, как заменить l * m на index и обновить его на index = i вместо m 1 .

Еще один странный эффект возникает, если вы когда-либо сталкиваетесь со словом, превышающим максимальную длину строки. Помимо значения, что строка длиннее предела, i все равно будет выполняться поиск в обратном направлении, пока не найдет пробел; тогда этот пробел может быть вообще в более ранней строке, создавая как слишком короткие строки, так и слишком длинные. Это результат обработки всего текста как одного массива и без ограничения того, какой раздел мы рассматриваем.

Лично я бы предпочел использовать встроенные методы Python, такие как str.rindex , которые могут найти определенный символ в заданной области внутри строки:

 s = "Fee fie faw fum who did you see now"
maxlen = 5
start = 8
end = s.rindex(' ', start, start maxlen)
print(s[start:end])
start = end   1
  

Мы также, как указал PaulMcG, можем использовать полные «батарейки в комплекте» и использовать стандартный библиотечный модуль textwrap для всей задачи.