以识别自定义 if 语句的方式解析纯文本

2024-03-31

我有以下字符串:

$string = "The man has {NUM_DOGS} dogs."

我通过以下函数运行它来解析它:

function parse_text($string)
{
    global $num_dogs;

    $string = str_replace('{NUM_DOGS}', $num_dogs, $string);

    return $string;
}

parse_text($string);

Where $num_dogs是预设变量。根据$num_dogs,这可能返回以下任何字符串:

  • 该男子有 1 只狗。
  • 该男子有2只狗。
  • 该男子养有 500 只狗。

问题是,在“该男子有 1 条狗”的情况下,dog是复数的,这是不希望的。我知道这可以通过不使用来解决parse_text函数,而不是做类似的事情:

if($num_dogs = 1){
    $string = "The man has 1 dog.";
}else{
    $string = "The man has $num_dogs dogs.";
}

但在我的应用程序中,我解析的不仅仅是{NUM_DOGS}写出所有条件需要很多行。

我需要一种速记方式,可以将其写入初始$string我可以通过解析器运行它,理想情况下这不会限制我只有两种真/假的可能性。

例如,让

$string = 'The man has {NUM_DOGS} [{NUM_DOGS}|0=>"dogs",1=>"dog called fred",2=>"dogs called fred and harry",3=>"dogs called fred, harry and buster"].';

最后发生了什么事情清楚吗?我尝试使用竖线后面的方括号内的部分来启动数组的创建,然后将新数组的键与 {NUM_DOGS} 的解析值(现在将是 $num_dogs 变量)进行比较在垂直条的左侧),并返回具有该键的数组条目的值。

如果这不是完全令人困惑,是否可以使用 preg_* 函数?


你提这个问题的前提是你想match一个具体的pattern进而replace表演后附加处理在匹配的文本上。

似乎是一个理想的候选人preg_replace_callback http://php.net/preg_replace_callback

用于捕获匹配的括号、引号、大括号等的正则表达式可能会变得非常复杂,并且使用正则表达式来完成这一切实际上效率相当低。事实上,如果您需要的话,您需要编写一个适当的解析器。

对于这个问题,我将假设有限的复杂性,并使用正则表达式通过两阶段解析来解决它。


首先,我能想到的最简单的正则表达式用于捕获大括号之间的标记。

/{([^}]+)}/

让我们来分解一下。

{        # A literal opening brace
(        # Begin capture
  [^}]+  # Everything that's not a closing brace (one or more times)
)        # End capture
}        # Literal closing brace

当应用于字符串时preg_match_all结果看起来像这样:

array (
  0 => array (
    0 => 'A string {TOK_ONE}',
    1 => ' with {TOK_TWO|0=>"no", 1=>"one", 2=>"two"}',
  ),
  1 => array (
    0 => 'TOK_ONE',
    1 => 'TOK_TWO|0=>"no", 1=>"one", 2=>"two"',
  ),
)

到目前为止看起来不错。

请注意,如果您的字符串中有嵌套大括号,即{TOK_TWO|0=>"hi {x} y"},这个正则表达式将不起作用。如果这不是问题,请跳至下一部分。

可以进行顶级匹配,但我能够做到这一点的唯一方法是通过递归。大多数正则表达式老手都会告诉您,一旦您向正则表达式添加递归,它就不再是正则表达式了。

这就是额外的处理复杂性开始出现的地方,对于长而复杂的字符串,很容易耗尽堆栈空间并使程序崩溃。如果您确实需要使用它,请小心使用。

递归正则表达式取自我的其他答案之一 https://stackoverflow.com/a/11468459/599857并做了一些修改。

`/{((?:[^{}]*|(?R))*)}/`

崩溃了。

{                   # literal brace
(                   # begin capture
    (?:             # don't create another capture set
        [^{}]*      # everything not a brace
        |(?R)       # OR recurse
    )*              # none or more times
)                   # end capture
}                   # literal brace

这次输出仅匹配顶级大括号

array (
  0 => array (
    0 => '{TOK_ONE|0=>"a {nested} brace"}',
  ),
  1 => array (
    0 => 'TOK_ONE|0=>"a {nested} brace"',
  ),
)

再次强调,除非必要,否则不要使用递归正则表达式。 (如果您的系统有旧的 PCRE 库,甚至可能不支持它们)


有了这个,我们需要弄清楚令牌是否有与之关联的选项。我建议不要根据您的问题匹配两个片段,而是根据我的示例保留带有标记的选项。{TOKEN|0=>"option"}

让我们假设$match如果我们检查管道,则包含匹配的令牌|,并获取后面所有内容的子字符串,我们将留下选项列表,我们再次可以使用正则表达式来解析它们。 (别担心,最后我会将所有内容整合在一起)

/(\d)+\s*=>\s*"([^"]*)",?/

崩溃了。

(\d)+    # Capture one or more decimal digits
\s*      # Any amount of whitespace (allows you to do 0    =>    "")
=>       # Literal pointy arrow
\s*      # Any amount of whitespace
"        # Literal quote
([^"]*)  # Capture anything that isn't a quote
"        # Literal quote
,?       # Maybe followed by a comma

以及一个示例匹配

array (
  0 => array (
    0 => '0=>"no",',
    1 => '1 => "one",',
    2 => '2=>"two"',
  ),
  1 => array (
    0 => '0',
    1 => '1',
    2 => '2',
  ),
  2 => array (
    0 => 'no',
    1 => 'one',
    2 => 'two',
  ),
)

如果您想在引号内使用引号,则必须为其创建自己的递归正则表达式。


总结一下,这是一个工作示例。

一些初始化代码。

$options = array(
    'WERE' => 1,
    'TYPE' => 'cat',
    'PLURAL' => 1,
    'NAME' => 2
);

$string = 'There {WERE|0=>"was a",1=>"were"} ' .
    '{TYPE}{PLURAL|1=>"s"} named bob' . 
    '{NAME|1=>" and bib",2=>" and alice"}';

一切都在一起。

$string = preg_replace_callback('/{([^}]+)}/', function($match) use ($options) {
    $match = $match[1];

    if (false !== $pipe = strpos($match, '|')) {
        $tokens = substr($match, $pipe + 1);
        $match = substr($match, 0, $pipe);
    } else {
        $tokens = array();
    }

    if (isset($options[$match])) {
        if ($tokens) {
            preg_match_all('/(\d)+\s*=>\s*"([^"]*)",?/', $tokens, $tokens);

            $tokens = array_combine($tokens[1], $tokens[2]);

            return $tokens[$options[$match]];
        }
        return $options[$match];
    }
    return '';
}, $string);

请注意,错误检查是最少的,如果您选择不存在的选项,将会出现意外的结果。

可能有更简单的方法来完成所有这一切,但我只是接受了这个想法并付诸实践。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以识别自定义 if 语句的方式解析纯文本 的相关文章

随机推荐

  • 为什么默认字符串比较器无法保持传递一致性?

    我知道这个问题之前已经注意到 https stackoverflow com questions 9354966 string sorting issue in c sharp 9355086 9355086 或多或少简洁 但我仍然创建这个
  • 在 Google Colab 上设置 MLflow

    我经常使用 Google Colab 来训练 TF PyTorch 模型 因为 Colab 为我提供了 GPU TPU 运行时 此外 我喜欢使用 MLflow 来存储和比较经过训练的模型 跟踪进度 共享等 将 MLflow 与 Google
  • 如何在UITableView中显示滚动条

    我想显示某种指示来引导用户滚动 通常 当我们触摸 UITableView 时 如果需要 滚动条就会出现 但我希望这个滚动条指示已经显示在我的表格视图上 怎么可能这样做呢 如果您有一个超出屏幕的表格视图 您可以调用 self tableVie
  • 使用持久登录 Cookie 时,如何根据数据库中的 bcrypt-hashed 令牌检查 Cookie 令牌?

    In 这个流行的解决方案 https stackoverflow com a 477578 869849对于涉及生成随机 128 位 令牌 以保存在用户 Cookie 中的持久登录 Cookie Jens Roland 建议 And 不要将
  • 如何使用动态规划确定最长递增子序列?

    我有一组整数 我想找到最长递增子序列 https en wikipedia org wiki Longest increasing subsequence该集合使用动态规划 好的 我将首先描述最简单的解决方案 即 O N 2 其中 N 是集
  • 条件 haml - if else 嵌套

    我想要的是 if 中的内容和 else 中的内容以包含 main block if transparency content inner style gt background url images illustrations transp
  • iOS 推送通知中的粗体(或其他格式)

    内置的消息应用程序会在推送通知中显示消息发送者的姓名bold 可以为我的应用程序执行此操作吗 这样的有效负载会是什么样子 如果相关的话 我正在使用 Mac 应用程序 APN Tester 进行测试 以使用此类有效负载向我的 iPhone 发
  • Fitnesse 与任何其他子系统测试工具[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我们目前正在使用 Fitness 进行子系统测试 我们在使用该工具时遇到很多问题 很少提及 编写 Fi
  • 从 lambda (C++) 创建的 std::function 的奇怪返回行为

    如果函数返回引用但返回类型未显式调用为引用 则我在使用从 lambda 创建的 std functions 时遇到问题 看起来 std function 创建得很好 没有任何警告 但是在调用它时 当需要引用时会返回一个值 导致事情崩溃 这是
  • 为什么枚举中的静态和实例初始化块的行为与类中的行为不同

    在学习 Java 认证测试时 我了解到静态初始化块在加载类时按照源代码中出现的顺序运行一次 实例初始化块在每次创建实例时运行 并且构造函数中的代码每次运行之后创建一个实例 为了测试我创建了一个带有一些静态和实例初始化块的类以及一个带有打印内
  • 如何使用一列来确定从何处获取另一列的值?

    我正在尝试使用一列来确定将哪一列用作另一列的值 它看起来像这样 X Y Z Target 1 a b c X 2 d e f Y 3 g h i Z 我想要看起来像这样的东西 X Y Z Target TargetValue 1 a b c
  • 使用 time min() 和 time max() 从选定的某个值 id 表中删除不必要的时间

    我尝试删除不必要的时间 因为我只需要min and max 数据 来自选定的 ID 并且还通过 Stack Overflow 阅读了同样的问题 SQL 仅选择列上具有最大值的行 https stackoverflow com questio
  • 需要将图像固定到页面上的特定位置

    我需要将 gif 图像修复到主页上的特定位置 我已将图像放入 HTML 中 但 position fixed 并没有执行我想要的操作 页面内容的其余部分在图像下方滚动 我希望图像始终保持在同一个位置 免责声明 我对 HTML 和 CSS 几
  • 在php中使用fpdf/fpdi添加垂直文本

    我有一个脚本可以从数据库 blob 构建多页 pdf 这个pdf文件可以工作并输出良好的电流 但我需要在每页的左侧添加一条垂直的文本行 我已经设法让它适用于某些 pdf 文件 但对于某些 pdf 文件 我收到损坏的文件错误 有谁有不同的方法
  • Visual Studio 2013 更新 3 的问题

    我刚刚安装了 VS 2013 Update 3 现在打开解决方案时出现错误 The Microsoft VisualStudio Web Publish WebPublishPackage MircosoftVisualStudio Web
  • 如何借助 amp-story-consent 构建 AMP cookie 同意?

    我想向我的 AMP 静态网站添加 cookie 同意 我想利用模式对话框amp story consent可以提供给我 我研究了各种例子 也许最完整的是 示例文档https amp dev documentation examples us
  • 新 PayPal 结账时大型购物车的 PayPal HTML 按钮损坏

    几周以来 我们一直在与 PayPal 支付标准表格问题作斗争 我们多年来一直运行相同的代码 但注意到我们的较大发票 包含 20 多个项目 最近在提交给 PayPal 时出现白屏死机 沙盒中的测试和以前一样运行良好 在到处搜索后 我找不到任何
  • 桌面应用程序转换器

    我使用 Desktop Bridge 一段时间了 最 近我开始遇到问题 它停止构建软件包 我尝试将其安装在不同的虚拟机上 但遇到了相同的错误 PowerShell Error Record DesktopAppConverter error
  • 如何使 CATransform3dMakeRotation 以另一种方式旋转?并链在一起

    我第一次使用一些核心动画 在实现可以翻转的扑克牌的过程中 我决定使用CALayer显示内容 不确定我将如何获得两侧 但这是另一个问题 并且我需要能够翻转它 移动它等等 我在用着CATransaction取得了一些成功 在下面的代码片段中 卡
  • 以识别自定义 if 语句的方式解析纯文本

    我有以下字符串 string The man has NUM DOGS dogs 我通过以下函数运行它来解析它 function parse text string global num dogs string str replace NU