Как уменьшите значения в строках в 2 раза, а количество строк увеличить в 2 раза?

Есть текстовый файл с таким содержимым:

Sleep 96 ms
Move 4 0
Sleep 96 ms
Move 12 8

Как разделить всё на 2 (значения четные по умолчанию), но увеличив количество строк в 2 раза (сохранив исходную сумму значений) и привести это к такому виду и такому порядку и сохранить в этот же или отдельный файл:

Sleep 48 ms
Move 2 0
Sleep 48 ms
Move 2 0
Sleep 48 ms
Move 6 4
Sleep 48 ms
Move 6 4

Начал искать выход через парсинг значений

re.findall('[0-9]+', txt_data)

Но в общем уперся в стену, умные люди помогите :) p.s. на python конечно :)


Ответы (2 шт):

Автор решения: Qwertiy

Если делать регулярками, то заменять надо полностью по 2 строки:

(Sleep\s+)(\d+)(\s*\w+(\r?\n)Move\s+)(\d+)(\s+)(\d+)(\r?\n|$)

Т. е. метчимся на каждую пару строк полностью порезанную на группы:

  • Sleep с пробелами
  • Длительность sleep
  • Необязательные пробелы, единицы измерения, и move с пробелами
    • Внутри этой группы ещё ловим группу с переводом строки
  • Значение x
  • Пробелы
  • Значение y
  • Перевод строки или конец строки

А потом из этого считаем новые значения и собираем желаемую строку. Группа с переводом строки нужна на случай, если файл не завершается переводом строки: нам в любом случае надо вставлять перевод, поскольку мы из одного блока делаем 2, а значит его надо где-то взять в соответствующем входному файлу виде.

Вот реализация на js:

console.log(`Sleep 96 ms
Move 4 0
Sleep 96 ms
Move 12 8`.replace(
  /(Sleep\s+)(\d+)(\s*\w+(\r?\n)Move\s+)(\d+)(\s+)(\d+)(\r?\n|$)/g,
  (m, t1, s, t2, br, x, t3, y, t4) => {
    s /= 2
    x /= 2
    y /= 2
    
    var part = `${t1}${s}${t2}${x}${t3}${y}`
    return part + br + part + t4
  }
))

Если хочется удостовериться, что заменяется весь текст, можно сделать что-то такое. Добавить к регулярке возможность сметчится на 1 любой символ если не удалось выделить блок. В таком случае мы попадаем в функцию замены с этой последней группой и можем кинуть исключение. Специфика регулярок в js позволит оставлять пустые строки между блоками, но на мой взгляд, это не страшно. А вот если действительно что-то не распарсится, то будет исключение.

console.log(`Sleep 96 ms
Move 4 0
Sleep 96 ms
Move 12 8`.replace(
  /(Sleep\s+)(\d+)(\s*\w+(\r?\n)Move\s+)(\d+)(\s+)(\d+)(\r?\n|$)|(.)/g,
  (m, t1, s, t2, br, x, t3, y, t4, fail) => {
    if (fail) throw new Error("Failed to parse")
  
    s /= 2
    x /= 2
    y /= 2
    
    var part = `${t1}${s}${t2}${x}${t3}${y}`
    return part + br + part + t4
  }
))

→ Ссылка
Автор решения: Namerek
import re

txt = """
Sleep 96 ms
Move 4 0
Sleep 96 ms
Move 12 8
"""

text = re.sub(
    # Убираем пустые строки
    r'^\n',
    '',
    re.sub(
        # Ищем числа
        r'\d+',
        # Меняем их на их значения приведенные к int деленные на 2
        lambda x: str(int(x.group(0)) // 2),
        txt
    ),
    flags=re.S
)
print(
    # Повторяем текст 2 раза
    # Здесь возможны варианты но это от исходника зависит
    text * 2
)

Sleep 48 ms
Move 2 0
Sleep 48 ms
Move 6 4
Sleep 48 ms
Move 2 0
Sleep 48 ms
Move 6 4

UPD:
Ну вот такой апдейт получился

import re


def chunks(iterable, chunk_size: int):
    for i in range(0, len(iterable), chunk_size):
        yield iterable[i:i + chunk_size]


def string_mod(string: str):
    return re.sub(
        # Убираем пустые строки
        r'^\n',
        '',
        re.sub(
            # Ищем числа
            r'\d+',
            lambda x: '{:.0f}'.format(int(x.group(0)) / 2),
            string
        ),
        flags=re.S
    )


txt = """
Sleep 96 ms
Move 4 0
Sleep 96 ms
Move 12 8
"""

new_text_lines = []

# Вытащил в отдельную переменную
# рулит тем, какое количество строк нужно повторять
block_length = 2

# Тоже в отдельной переменной
# отвечает за количество повторений блока
factor = 2

for part in chunks(txt.strip('\n').split('\n'), block_length):
    new_text_lines += map(string_mod, part * factor)

print(
    '\n'.join(new_text_lines)
)

Sleep 48 ms
Move 2 0
Sleep 48 ms
Move 2 0
Sleep 48 ms
Move 6 4
Sleep 48 ms
Move 6 4
→ Ссылка