如何在 VS Code 中使用正则表达式在括号之间查找和替换?

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

我对此感到疯狂,所以我有很多这样的:

VfxEmitterDefinitionData {
                BindWeight: embed = ValueFloat {
                    ConstantValue: f32 = 0
                }
                IsSingleParticle: flag = true
                ChildParticleSetDefinition: pointer = VfxChildParticleSetDefinitionData {
                    ChildrenIdentifiers: list[embed] = {
                        VfxChildIdentifier {
                            EffectKey: hash = "distort"
                        }
                    }
                }
                EmitterName: string = "distort"
            }

VfxEmitterDefinitionData {
                BindWeight: embed = ValueFloat {
                    ConstantValue: f32 = 0
                }
                ChildParticleSetDefinition: pointer = VfxChildParticleSetDefinitionData {
                    ChildrenIdentifiers: list[embed] = {
                        VfxChildIdentifier {
                            EffectKey: hash = "DarkCore"
                        }
                    }
                }
                EmitterName: string = "DarkCore"
                IsSingleParticle: flag = true
            }

我想使用查找和替换将它们变成这些:

        "distort" = VfxSystemDefinitionData {
        ComplexEmitterDefinitionData: list[pointer] = {
        VfxEmitterDefinitionData {
                BindWeight: embed = ValueFloat {
                    ConstantValue: f32 = 0
                }
                IsSingleParticle: flag = true
                ChildParticleSetDefinition: pointer = VfxChildParticleSetDefinitionData {
                    ChildrenIdentifiers: list[embed] = {
                        VfxChildIdentifier {
                            EffectKey: hash = "distort"
                        }
                    }
                }
                EmitterName: string = "distort"
            }
        }
        ParticleName: string = "distort"
        ParticlePath: string = "distort"
    }
        
        "DarkCore" = VfxSystemDefinitionData {
        ComplexEmitterDefinitionData: list[pointer] = {
        VfxEmitterDefinitionData {
                BindWeight: embed = ValueFloat {
                    ConstantValue: f32 = 0
                }
                ChildParticleSetDefinition: pointer = VfxChildParticleSetDefinitionData {
                    ChildrenIdentifiers: list[embed] = {
                        VfxChildIdentifier {
                            EffectKey: hash = "DarkCore"
                        }
                    }
                }
                EmitterName: string = "DarkCore"
                IsSingleParticle: flag = true
            }
        }
        ParticleName: string = "DarkCore"
        ParticlePath: string = "DarkCore"
    }

基本上只是想在开始时添加这个:

    "(Name from EmitterName)" = VfxSystemDefinitionData {
    ComplexEmitterDefinitionData: list[pointer] = {

这位于每个 VfxEmitterDefinitionData 的末尾:

    }
    ParticleName: string = "(Name from EmitterName)"
    ParticlePath: string = "(Name from EmitterName)"
}

即使忽略名称部分并只专注于在括号之间进行选择,我也找不到一种方法来匹配它们中的每一个,我已经尝试过

VfxEmitterDefinitionData \{[\s\S\r]*\}
,但这会选择文件中的所有它们,就好像它是一个一样,并且
VfxEmitterDefinitionData \{[\s\S\r]*?\} 
它确实选择了它们中的每一个,但仅在第一个左括号和第一个右括号之间进行选择。 那么使用 VS Code 是否可以做到这一点,还是太复杂了?还有其他工具可以帮助完成此类工作吗?

regex visual-studio-code select search replace
1个回答
0
投票

需要考虑的事项

如果您的内容中包含大括号,这可能会变得相当棘手 字符串:

"Example of \"moustache\" emoji face => :-{"

或在评论中:

// Use a closing curly bracket "}" to close your block.

这意味着正则表达式必须考虑 字符串和注释中的大括号以获得平衡 打开/关闭块正确。这就是为什么它可以快速到达 正则表达式引擎的局限性!

使用 PCRE 代替

PHP 的 PCRE 引擎接受递归,所以你可以使用另一个 编辑器或工具来更改您的文件。

但是由于您需要捕获部分代码 发射器的名称我想你必须编写一个脚本来 转换您的文件。这就是我在 PHP 中使用 PCRE 所做的

x
标志,可让您在多个上添加注释并编写正则表达式 线。同时,我们可以声明一些其他模式 在主模式中重用(有点像函数)。这使得 更容易编写和理解。

匹配块的正则表达式模式,使用

~
作为 模式分隔符,这样我就可以使用
/
而无需转义它:

~
(?(DEFINE) # Define some sub-patterns for later use and readability.

  (?<comment>
      /{2}[^\r\n]*    # Line comment: // Blabla
    |                 # or
      /\*[\s\S]*?\*\/ # Block comment: /* Blabla */
  )

  (?<string>
    "         # Opening double-quote.
    (?:
      \\[\\"] # Backslash or double-quote: \\ or \"
      |       # or
      [^"]    # Any char not being a double-quote.
    )*
    "         # Closing double-quote.
  )

  (?<balanced_braces>
    \{
    (?: \s* | \g<comment> | \g<string> | [^{}"]* | \g<balanced_braces> )*?
    \}
  )

)

# Pattern starts here:
(?<block>
  \h*
  VfxEmitterDefinitionData
  \s*
  \g<balanced_braces>
)
~gxu

在这里测试:https://regex101.com/r/nR7ey2/1

一些细节:

  • 分隔符和标志:

    • ~
      是模式分隔符(通常是 JS 中的
      /
      )。
    • x
      用于 ex 的语法(注释和空格)。
    • g
      用于 g全局匹配,意味着匹配所有出现的情况。 这将在 PHP 脚本中被删除,因为它是一个参数。
    • u
      代表 unicode。
  • (?(DEFINE) ... )
    是可以声明子模式的部分 就像函数一样。这是为了帮助编写复杂的模式。

  • (?<your_pattern> ... )
    是声明子模式/函数的方式。 当你想使用它时,你使用
    \g<your_pattern>

    请注意,它与
    \k<your_pattern>
    不同,后者用于 引用与模式匹配但不是模式的文本 图案本身。

  • 行注释是两个斜杠,后跟任何不是的字符 回车或换行字符。

  • 块注释

    /* */
    包含任何内容(以一种不贪婪的方式, 使用
    [\s\S]
    而不是
    .
    ,这样我们就不需要启用
    s
    标志使点与新行匹配)。

  • 字符串以

    "
    开头,然后可以包含多个内容, 多次:

    • 反斜杠字符,已转义:
      \\
    • 转义双引号:
      \"
    • 除双引号之外的任何其他内容。这应该包括任何 字符或序列,例如
      \n
      \t
      等,但至少 在匹配任何字符之前,我们已经“吃掉”了反斜杠。 这样它通常应该正确匹配完整的字符串。我做到了 不要尝试匹配像
      'Hello'
      这样的单引号字符串。
  • 然后,在搜索完注释和字符串后,我们可以尝试 匹配任何不是大括号或双引号的字符。

  • 递归是通过寻找平衡大括号块来完成的 通过用

    \g<balanced_braces>
    来引用它,即使在它的 自己的定义。

PHP 脚本

我使用

preg_replace_callback()
因为我们想要提取 找到的块中的发射器名称。这是在回调中完成的 在更换步骤中起作用。

我真的不确定这个脚本是否能够在大文件上运行 由于递归,它可能很快就会遇到 PCRE 的限制 和复杂的分析。您可能需要更改一些 INI 设置 PCRE 甚至调整脚本以首先找到 有趣的块,然后提取块本身来使用 需要分析的字符串较短。

经过修改的输入的 PHP 代码,以显示可能存在的问题。 语法颜色突出显示在 stackoverflow 上有错误,因为它没有 处理 PHP 的 Heredoc 语法:

<?php

/**
 * @author Patrick Janser
 * @see https://stackoverflow.com/questions/77692961/how-to-find-and-replace-between-brackets-using-regex-in-vs-code
 */

//ini_set('pcre.backtrack_limit',    100000000); // default is 1000000.
//ini_set('pcre.backtrack_recursion', 10000000); // default is  100000.

define('EXIT_PCRE_ERROR', 1);

// A regex to find a VfxEmitterDefinitionData block.
define('REGEX_BLOCK', <<<'END_OF_REGEX'
~
(?(DEFINE) # Define some sub-patterns for later use and readability.

  (?<comment>
      /{2}[^\r\n]*    # Line comment: // Blabla
    |                 # or
      /\*[\s\S]*?\*\/ # Block comment: /* Blabla */
  )

  (?<string>
    "         # Opening double-quote.
    (?:
      \\[\\"] # Backslash or double-quote: \\ or \"
      |       # or
      [^"]    # Any char not being a double-quote.
    )*
    "         # Closing double-quote.
  )

  (?<balanced_braces>
    \{
    (?: \s* | \g<comment> | \g<string> | [^{}"]* | \g<balanced_braces> )*?
    \}
  )
)

# Pattern starts here:
(?<block>
  \h*
  VfxEmitterDefinitionData
  \s*
  \g<balanced_braces>
)
~xu
END_OF_REGEX
);

// To capture the emitter of the VfxEmitterDefinitionData block.
define('REGEX_EMITTER', <<<'END_OF_REGEX'
/
EmitterName\s*:\s*string\s*=\s*"
(?<id>
  (?:
    \\[\\"] # Backslash or double-quote: \\ or \"
  |         # or
    [^"]    # Any char not being a double-quote.
  )*
)
"
/xu
END_OF_REGEX
);

// Use this to read a file instead.
//$input = file_get_contents('your-input-file.code');

$input = <<<'END_OF_INPUT'
VfxEmitterDefinitionData {
    BindWeight: embed = ValueFloat {
        // A comment with a "{" which could break things.
        // Another comment.
        ConstantValue: f32 = 0
    }
    IsSingleParticle: flag = true
    ChildParticleSetDefinition: pointer = VfxChildParticleSetDefinitionData {
        ChildrenIdentifiers: list[embed] = {
            VfxChildIdentifier {
                EffectKey: hash = "\"distort\"\n}"
            }
        }
    }
    EmitterName: string = "\"distort\"\n}"
}

    // We could have spaces before too
    VfxEmitterDefinitionData {
        /* A block comment {:-) */
        BindWeight: embed = ValueFloat {
            ConstantValue: f32 = 0
        }
        ChildParticleSetDefinition: pointer = VfxChildParticleSetDefinitionData {
            ChildrenIdentifiers: list[embed] = {
                VfxChildIdentifier {
                    EffectKey: hash = "DarkCore :-{"
                }
            }
        }
        EmitterName: string = "DarkCore :-{"
        IsSingleParticle: flag = true
    }
END_OF_INPUT;

print "Input\n=====\n\n$input\n\n";

$output = preg_replace_callback(
    REGEX_BLOCK,
    function ($match) {
        $block = $match[0];
        
        // 1. Extract the emitter.
        if (preg_match(REGEX_EMITTER, $block, $subMatch)) {
            $emitterString = '"' . $subMatch['id'] . '"';
        } else {
            die("Could not find the id inside the block :-(\n");
        }
        
        // Wrap the found block in it's container.
        return "$emitterString = VfxSystemDefinitionData {\n" .
               "    ComplexEmitterDefinitionData: list[pointer] = {\n" .
               // Indent the block 2 times to the right = 8 spaces.
               preg_replace('~^~m', '        ', $block) . "\n" .
               "    ParticleName: string = $emitterString\n" .
               "    ParticlePath: string = $emitterString\n" .
               "}\n";
    },
    $input
);

if (is_null($output)) {
    fprintf(STDERR, "PCRE error %d: %s\n", preg_last_error(), preg_last_error_msg());
    exit(EXIT_PCRE_ERROR);
}

print "Output\n======\n\n$output";

您可以在这里运行演示:https://onlinephp.io/c/48450

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