上下文
在Python中,给定的是一个包含嵌套的点标记引用的任意字符串,稍后将用实际值替换它。
str = 'Allocate {ref:network.node.{ref:global.environment}.api} with {ref:local.value}'需要从内到外替换引用,因此ref:global.environment='prod‘,然后ref:network.node.prod.api='/prod/api',ref:local.value='UUID',因此结果是:
result = 'Allocate /prod/api with UUID'解析
试图用Trying解析来解决这个问题,因为regex在嵌套引用中有点丢失了。目标是有一个引用列表,我可以在以后的步骤中处理/替换这些引用。
lbrack = '{ref:'
rbrack = '}'
ref = Forward()
ref << lbrack + Word(alphas, alphanums + '.') + ZeroOrMore(ref) + rbrack
ref.parseString(str)与此类似的结果将有帮助:
references = [['{ref:global.environment}'], '{ref:network.node.{ref:global.environment}.api}', '{ref:local.value}']但我错过了一些解析说明,让这个工作,也许你有一个想法。多谢你们的支持。
#更新1-解决方案
获取PaulMcG的答案后,当前的代码是:
lbrack = "{ref:"
rbrack = "}"
ref = pp.Forward()
ident = pp.Word(pp.alphas, pp.alphanums)
ref <<= pp.Group(lbrack + pp.delimitedList(ref | ident, delim=".") + rbrack)
def eval_ref(tokens):
# Skip lbrack and rbrack, i.e. [1:-1]
return reduce(operator.getitem, tokens[0][1:-1], ns)
ref.addParseAction(eval_ref)
test = 'Allocate {ref:network.node.{ref:global.environment}.api} with {ref:local.value}'
print(ref.transformString(test))作为附带说明,将eval_ref()的代码简化为一行。
发布于 2021-03-23 20:49:05
你走在正确的轨道上,但我需要帮助你解决一个共同的基本问题。
当人们使用以下方法定义限定标识符时,我会看到很多这样的情况:
Word(alphas, alphanums + ".")它有一些固有的问题,因为它不仅将与"a“和"a.b.c.d”相匹配,而且还将与"a.“、”a.“、"a.c.”和“a.c.0”匹配。都是标识符。在您的示例中,您还希望支持嵌入式ref而不是限定标识符。
所以,你可以这样想:
qualified_ident ::= ident_term ["." ident_term]...
ident_term := reference | identifier
reference := "{ref:" qualified_ident "}"
identifier := "A-Za-z" "A-Za-z0-9"...现在你的合格身份可以由推荐人组成,而推荐人本身也可以由合格的偶像组成。
在pyparsing解析中,这类似于(使用带有“”的分隔列表。(符合资格的身分证):
ref = Forward()
ident = Word(alphas, alphanums)
ref <<= Group(lbrack + delimitedList(ref | ident, delim=".") + rbrack)现在delimitedList将压制“。”分隔符,但我们并不在乎,因为我们无论如何都要跳过它们。我们将编写一个解析操作来执行ref对某些查找数据的解析。
首先,让我们从一些JSON中创建一个简单的嵌套dict,以支持示例字符串:
# define a nested dict for lookup values from refs
import json
ns = json.loads("""
{
"network" : {
"node" : {
"prod": {
"api": "prod_api"
},
"test": {
"api": "test_api"
}
}
},
"local" : {
"value" : 1000
},
"global": {
"environment" : "prod"
}
}
""")现在,我们将编写一个解析操作,该操作将使用此命名空间块计算引用的路径:
def eval_ref(tokens):
ret = ns
# uncomment for debugging
# print(tokens[0])
# resolve next level down in the reference path
for t in tokens[0][1:-1]:
ret = ret[t]
return ret
# and add as a parse action to ref
ref.addParseAction(eval_ref)应该是这样的,让我们在测试字符串上尝试它(我重命名了测试字符串,因为str是Python中的内置类型,不太适合用变量名掩盖它)。不过,我们将使用transformString而不是parseString。transformString将用任何解析操作发出的文本替换任何源文本(如果包装在抑制中,则会被抑制),这将递归地发生在您的内部引用中,这样您的内部引用将被计算,然后外部引用将被使用该内部解析值进行计算。
test = 'Allocate {ref:network.node.{ref:global.environment}.api} with {ref:local.value}'
print(ref.transformString(test))应给予:
Allocate prod_api with 1000发布于 2021-03-23 12:46:26
如果您可以实现扫描并替换自己,您可以这样做(只需使用简单的字典将值插入作为示例):
from typing import List, Tuple
string = 'Allocate {ref:network.node.{ref:global.environment}.api} with {ref:local.value}'
values = {
'global.environment': 'prod',
'network.node.prod.api': '/prod/api',
'local.value': 'UUID'
}
# Returns the start, end, and name of the next reference to replace.
# Note that for nested references, this will always be the most nested one.
def get_next_ref(input_string: str) -> Tuple[int, int, str]:
starting_stack = []
for i, char in enumerate(input_string):
if char == '{':
starting_stack.append(i)
elif char == '}':
start = starting_stack.pop()
return start, i, input_string[start:i+1]
return -1, -1, ''
next_ref = get_next_ref(string)
while next_ref[0] != -1:
value = values[next_ref[2][5:-1]]
print("Replacing '%s' with '%s'" % (next_ref[2], value))
string = string.replace(next_ref[2], value)
next_ref = get_next_ref(string)
print("Final string:", string)显然,它需要扩展以处理不属于引用的大括号(如果它们可能存在于您正在处理的字符串中),而且我还没有包括错误检查,例如,不匹配的大括号或对未知值的引用。
https://stackoverflow.com/questions/66762220
复制相似问题