【问题标题】:PHP preg_replace: find string part not starting with an exclamation pointPHP preg_replace:查找不以感叹号开头的字符串部分
【发布时间】:2017-10-07 20:37:36
【问题描述】:

我正在处理一些非常混乱的 Excel 表格,并尝试使用 PHP 来寻找线索..

我有一个 MySQL 数据库,其中包含 excel 文档中的所有公式,并且像往常一样,当前工作表中的单元格名称没有“工作表名称!”在它面前。为了使其可搜索(并在公式中找到死路),我喜欢将数据库中的所有公式替换为其工作表名称作为前缀。

例子:

=+(sheet_factory_costs!A17/sheet_employees!D23)+T12+W12

数据库包含当前工作表的名称,我喜欢用该工作表名称更改上面的公式(我们称之为“sheet_turnover”)。

=+(sheet_factory_costs!A17 / sheet_employees!D23)+sheet_turnover!T12+sheet_turnover!W12

我在 PHP 中使用 preg_replace 进行了尝试,我认为我需要以下规则:

  1. 找到一个或两个字母,后跟一个数字。这始终是公式中的单元格地址。
  2. 当有 !在之前的位置上,已经有一个sheetname。所以我只寻找不以感叹号开头的字母和数字。

问题似乎是!也是模式中的特殊符号。即使我试图逃避它,它也不起作用:

$newformula = 
preg_replace('/(?<\!)[A-Z]{1,2}[0-9]/', 
'lala', 
$oldformula);

(lala 是我的临时标记,看它是否选择了正确的单元格地址)

(是的,lala 只放在第一个数字上,但现在没问题)

(是的,所有 Excel $..$..(永久)标记都已被替换。无需在公式中构建它)

【问题讨论】:

  • 您需要将反斜杠加倍,因为它在字符串文字中具有转义含义,会“吃掉”它们。
  • 实际上,(?&lt;\!) 在 PCRE 中是一个无效的正则表达式构造,如果你尝试它,你会得到一个关于不完整组结构的警告。这与这里需要多少转义反斜杠无关。

标签: php regex preg-replace


【解决方案1】:

您的负面回溯已损坏,您需要将其定义为(?&lt;!!)。但是,您还需要在它之前使用单词边界,或使用(?&lt;![A-Z]) 后视来确保[A-Z]{1,2} 之前没有其他字母。

所以,你可以使用

'~\b(?<!!)[A-Z]{1,2}[0-9]~'

请参阅regex demo。替换为sheet_turnover!$0,其中$0 是整个匹配值。

详情

  • \b - 单词边界(这是必要的,否则 name!AA11 仍然会匹配)
  • (?&lt;!!) - 没有 ! 紧邻当前位置的左侧
  • [A-Z]{1,2} - 1 或 2 个字母
  • [0-9] - 一个数字。

另一种方法是匹配并跳过“错误”上下文,然后匹配并保留“正确”上下文:

'~\w+![A-Z]{1,2}[0-9](*SKIP)(*F)|\b[A-Z]{1,2}[0-9]~'

this regex demo

这里,\w+![A-Z]{1,2}[0-9](*SKIP)(*F)| 部分匹配 1 个或多个单词字符,然后是 1 个或 2 个大写 ASCII 字母,然后是一个数字,(*SKIP)(*F) 将省略匹配项,并使引擎在结束后继续查找匹配项上一场比赛。

【讨论】:

  • 那个加倍的感叹号起到了作用。我不明白为什么我必须这样做,但它工作正常。有或没有 /b 的结果没有差异。没有 /b 它不会影响前面的单词(工作表名称):旧 :=+D16+(P25/(O25*(Entry_machines!C12))) 新:=+lala6+(lala5/(lala5*(Entry_machines!C12) ))
  • @TwomuchTime 我宁愿你把它当作! 里面的(?&lt;!...)(消极的lookbehind)构造。然后,就很清楚了。
  • @TwomuchTime 如果列名中有两个字母,\b 会有所不同。试试+D16+(P25/(O25*(Entry_machines!CC12)))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多