带文件名的嵌套排列

问题描述 投票:0回答:1

我正在创建一个可以处理嵌套模板字符串和文件名的程序。例如:

模板字符串:

{person, Fox, bear, joe, mary, lou, {animals.txt}} 跳过了汽车

animals.txt的内容

猫 狗 狐狸

生成的字符串:

[在animals.txt之前生成字符串]...

猫跳过车 狗从车上跳过去 狐狸跳过车

我已经创建了一个名为 Expand_template() 的函数来扩展文本文件并填充模板字符串,如下:

def expand_template(template_string):
        # Find all template variables in the string
        template_vars = re.findall(r'{(\w+\.txt)}', template_string)
        # Dictionary to store the contents of each template file
        var_contents = {}

        # Read the contents of each template file
        for var in template_vars:
            if os.path.isfile(var):
                with open(var, 'r') as file:
                    var_contents[var] = [line.strip() for line in file]
            else:
                print(f"File not found: {var}")
                sys.exit(1)

        # Replace template variables with their contents
        for var in template_vars:
            template_string = template_string.replace('{' + var + '}', '{' + ','.join(var_contents[var]) + '}')

        return template_string

这是生成排列的函数:

def generate_permutations(template_string):
    # Find all template variables in the string
    variables = re.findall(r'{([^}]+)}', template_string)

    # Extract the options for each variable
    options = [var.split(', ') for var in variables]

    # Generate all possible combinations of options
    combinations = list(itertools.product(*options))

    permutations = []
    for combination in combinations:
        # Replace each variable with its corresponding option
        permutation = template_string
        for var, option in zip(variables, combination):
            permutation = permutation.replace('{' + var + '}', option)
        permutations.append(permutation)

    return permutations

我需要的是一种将这两者放在一起的算法,以便能够处理嵌套的模板字符串,并且无论嵌套有多深都能够处理它们。

我尝试使用正则表达式来查找嵌套大括号对,但代码只是挂起。

另外,如果有程序可以实现这个功能,请指点一下。

python nested permutation
1个回答
0
投票

您在评论中说您可以有嵌套的选项集。在这种情况下,寻找初始字符串形式的所有可能选项的任务就变成了遍历选项树的任务。这里每个节点的后代都是嵌套的选项集。 为了解决这个设置中的问题,我可以为您提供这个选项

import typing


class Stack:
    def __init__(self):
        self._items: typing.Any = []

    def push(self, item: typing.Any):
        self._items.append(item)

    def pop(self) -> typing.Any:
        self._items.pop()

    def is_empty(self) -> bool:
        return len(self._items) == 0



def get_first_substitution_group(string: str) -> str:
    """
        Finds and returns the first available part of the string, 
        limited by the opening and closing brackets.
        Example: 
            string: 'A sculpture of a {seagull {on a pier, on a beach}}'

            result: 'seagull {on a pier, on a beach}'
    """
    stack = Stack()
    for i, сhar in enumerate(string):
        if сhar == '{':
            if stack.is_empty():
                start = i
            stack.push('{')
        elif сhar == '}':
            if stack.is_empty():
                raise Exception('Closing сharacter precedes the opening one')
            stack.pop()
            if stack.is_empty():
                return string[start + 1:i]

    if not stack.is_empty():
        raise Exception(
            'The number of opening characters is not equal '
            'to the number of closing ones'
        )


def extract_options(
    string: str, split_char:str = ','
) -> typing.Generator[str, None, None]:
    """
        Splits a string with a splitting character at the first level, 
        the parts of the string inside the brackets remain unchanged.
        Example: 
            string: 'seagull {on a pier, on a beach}, poodle {on a sofa, in a truck}'
            split_char: ','
            result: 
                first iteration: 'seagull {on a pier, on a beach}'
                second iteration: 'poodle {on a sofa, in a truck}'
    """
    stack = Stack()
    start = 0
    for i, сhar in enumerate(string):
        if сhar == '{':
            stack.push('{')
        elif сhar == '}':
            if stack.is_empty():
                raise Exception('Closing сharacter precedes the opening one')
            stack.pop()
        elif сhar == split_char:
            if stack.is_empty():
                yield string[start: i]
                start = i + 1

    yield string[start:]

    if not stack.is_empty():
        raise Exception(
            'The number of opening characters is not equal '
            'to the number of closing ones'
        )


def string_forms_generator(template: str) -> typing.Generator[str, None, None]:
    """
        Substitutes the options listed in brackets into the original string and
        provides in this way all possible forms of the string
        Example: 
            string: 'seagull {on a pier, on a beach}, poodle {on a sofa, in a truck}'
            split_char: ','
            result: 
                first iteration: 'seagull on a pier, poodle on a sofa'
                second iteration: 'seagull on a pier, poodle in a truck'
                third iteration: 'seagull on a beach, poodle on a sofa'
                fourth iteration: 'seagull on a beach, poodle in a truck'
    """
    if '{' not in template:
        yield template
    else:
        first_substitution_group = get_first_substitution_group(template)
        for option in extract_options(first_substitution_group):
            yield from string_forms_generator(
                template.replace(
                    f'{{{first_substitution_group}}}', option.strip()
                )
            )


if __name__ == '__main__':
    # Here you can substitute values from files using your function 
    # `expand_template`

    template = (
        'A {sculpture, painting} of a {seagull {on a pier, on a beach}, '
        'poodle {on a sofa, in a truck}}'
    )
    for string_form in string_forms_generator(template):
        print(string_form)

这里我使用了简单的递归深度优先遍历。如果您可能有高度嵌套,那么此解决方案将不适合您。

© www.soinside.com 2019 - 2024. All rights reserved.