Сортировка двух текстовых файлов с выравниванием текста с отступом - PullRequest
1 голос
/ 10 июля 2020

Я хотел бы сравнить два моих файла журнала, сгенерированные до и после реализации, чтобы увидеть, повлияло ли это на что-нибудь. Однако порядок журналов, которые я получаю, не всегда одинаков. Поскольку в файле журнала также есть несколько строк с отступом, когда я пытался отсортировать, все сортируется. Но я бы хотел, чтобы ребенок оставался нетронутым вместе с родителем. Строки с отступом - это пробелы, а не табуляция.

Любая помощь будет принята с благодарностью. Меня устраивает любое решение windows или Linux.

Пример файла:

# Это пример кода

Parent1 to be verified

    Child1 to be verified

    Child2 to be verified
        Child21 to be verified
        Child23 to be verified
        Child22 to be verified
            Child221 to be verified

    Child4 to be verified

    Child5 to be verified
        Child53 to be verified
        Child52 to be verified
            Child522 to be verified
            Child521 to be verified

    Child3 to be verified

Ответы [ 2 ]

0 голосов
/ 13 июля 2020
• 1000 1004 *

См. Сценарий python ниже:

"""Attach parent to children in an indentation-structured text"""
from typing import Tuple, List
import sys

# A unique separator to separate the parent and child in each line
SEPARATOR = '@'
# The indentation
INDENT = '    '

def parse_line(line: str) -> Tuple[int, str]:
    """Parse a line into indentation level and its content
    with indentation stripped

    Args:
        line (str): One of the lines from the input file, with newline ending

    Returns:
        Tuple[int, str]: The indentation level and the content with
            indentation stripped.

    Raises:
        ValueError: If the line is incorrectly indented.
    """
    # strip the leading white spaces
    lstripped_line = line.lstrip()
    # get the indentation
    indent = line[:-len(lstripped_line)]

    # Let's check if the indentation is correct
    # meaning it should be N * INDENT
    n = len(indent) // len(INDENT)
    if INDENT * n != indent:
        raise ValueError(f"Wrong indentation of line: {line}")

    return n, lstripped_line.rstrip('\r\n')


def format_text(txtfile: str) -> List[str]:
    """Format the text file by attaching the parent to it children

    Args:
        txtfile (str): The text file

    Returns:
        List[str]: A list of formatted lines
    """
    formatted = []
    par_indent = par_line = None

    with open(txtfile) as ftxt:
        for line in ftxt:
            # get the indentation level and line without indentation
            indent, line_noindent = parse_line(line)

            # level 1 parents
            if indent == 0:
                par_indent = indent
                par_line = line_noindent
                formatted.append(line_noindent)

            # children
            elif indent > par_indent:
                formatted.append(par_line +
                                 SEPARATOR * (indent - par_indent) +
                                 line_noindent)

                par_indent = indent
                par_line = par_line + SEPARATOR + line_noindent

            # siblings or dedentation
            else:
                # We just need first `indent` parts of parent line as our prefix
                prefix = SEPARATOR.join(par_line.split(SEPARATOR)[:indent])
                formatted.append(prefix + SEPARATOR + line_noindent)
                par_indent = indent
                par_line = prefix + SEPARATOR + line_noindent

    return formatted

def sort_and_revert(lines: List[str]):
    """Sort the formatted lines and revert the leading parents
    into indentations

    Args:
        lines (List[str]): list of formatted lines

    Prints:
        The sorted and reverted lines
    """
    sorted_lines = sorted(lines)
    for line in sorted_lines:
        if SEPARATOR not in line:
            print(line)
        else:
            leading, _, orig_line = line.rpartition(SEPARATOR)
            print(INDENT * (leading.count(SEPARATOR) + 1) + orig_line)

def main():
    """Main entry"""
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <file>")
        sys.exit(1)

    formatted = format_text(sys.argv[1])
    sort_and_revert(formatted)

if __name__ == "__main__":
    main()

Давайте сохраним его как format.py, и у нас есть тестовый файл, скажем test.txt:

parent2
    child2-1
        child2-1-1
    child2-2
parent1
    child1-2
        child1-2-2
        child1-2-1
    child1-1

Давайте проверим это:

$ python format.py test.txt
parent1
    child1-1
    child1-2
        child1-2-1
        child1-2-2
parent2
    child2-1
        child2-1-1
    child2-2

Если вам интересно, как функция format_text форматирует текст, вот промежуточные результаты, которые также объясняют, почему мы можем отсортировать файл так, как мы хотели:

parent2
parent2@child2-1
parent2@child2-1@child2-1-1
parent2@child2-2
parent1
parent1@child1-2
parent1@child1-2@child1-2-2
parent1@child1-2@child1-2-1
parent1@child1-1

Вы можете видеть, что у каждого ребенка есть свои родители, вплоть до root. Чтобы потомки одного родителя были отсортированы вместе.

0 голосов
/ 11 июля 2020

Краткий ответ (Linux решение):

sed ':a;N;$!ba;s/\n /@/g' test.txt | sort | sed ':a;N;$!ba;s/@/\n /g'

Протестируйте: test.txt

parent2
  child2-1
    child2-1-1
  child2-2
parent1
  child1-1
  child1-2
    child1-2-1
$ sed ':a;N;$!ba;s/\n /@/g' test.txt | sort | sed ':a;N;$!ba;s/@/\n /g'
parent1
  child1-1
  child1-2
    child1-2-1
parent2
  child2-1
    child2-1-1
  child2-2

Пояснение:

Идея заключается в замене новой строки, за которой следует отступ / пробел символом, отличным от новой строки, который должен быть уникальным в вашем файле (здесь я использовал @, например, если он не уникален в вашем файле, используйте другие символы или даже строка), потому что нам нужно вернуть ее обратно к новой строке и отступу / пробелу позже.

О команде sed:

  1. :a создать метку 'a'
  2. N добавить следующую строку в пространство шаблона
  3. $! если не последняя строка, ba метка перехода (go в) 'a'
  4. s замена, /\n / регулярное выражение для новой строки, за которым следует пробел
  5. /@/ уникальный символ для замены новой строки и пробела если он не уникален в вашем файле, используйте другие символы или даже строку
  6. /g глобальное совпадение (столько раз, сколько возможно)
...