我愿意包含更多的代码,而不仅仅是正则表达式。
我正在编写一些代码,拍摄一张照片,运行两个Imagick过滤器,然后运行一个tesseractOCR
传递,以输出文本。
从该文本中,我使用带PHP的regex提取一个SKU
(产品的模数)并将结果输出到一个数组中,然后将数组插入到一个表中。
一切都很好,除了我现在用的表情:
@#-$^&*():;,œ∑††π∂ƒc.∆˚Ω≈√∫˜≤≥{4,20}
我还会得到一些只包含字母的字符串。
最终目标是:
可以包含大写字母和数字的-strings,
只包含数字的-strings,
不包含字母的-strings,
不包含任何小写字母的-strings,
-these字符串必须介于4-20个字符之间。
例如:
SKU
可以是5209
,也可以是WRE5472UFG5621
。
发布于 2022-01-11 22:23:43
在regex ma雌激素出现之前,像我这样的懒人只会在这个问题上做两轮,并且保持简单。首先,匹配所有仅为A-Z
、0-9
的字符串(而不是编制大量的无列表或外观)。然后,使用带有preg_grep()
标志的PREG_GREP_INVERT
删除所有仅为A的字符串。最后,过滤唯一的匹配,以消除重复噪声。
$str = '-9 Cycles 3 Temperature Levels Steam Sanitizet+ -Sensor Dry | ALSO AVAILABLE (PRICES MAY VARY) |- White - 1258843 - DVE45R6100W {+ Platinum - 1501 525 - DVE45R6100P desirable: 1258843 DVE45R6100W';
$wanted = [];
// First round: Get all A-Z, 0-9 substrings (if any)
if(preg_match_all('~\b[A-Z0-9]{6,24}\b~', $str, $matches)) {
// Second round: Filter all that are A-Z only
$wanted = preg_grep('~^[A-Z]+$~', $matches[0], PREG_GREP_INVERT);
// And remove duplicates:
$wanted = array_unique($wanted);
}
结果:
array(3) {
[2] · string(7) "1258843"
[3] · string(11) "DVE45R6100W"
[4] · string(11) "DVE45R6100P"
}
请注意,我已经将匹配长度增加到了{6,24}
,尽管您提到了一个4个字符的匹配,因为示例字符串中有不属于“理想”列表的4位子字符串。
编辑:--我已经将preg_match_all()
移动到包含其余操作的条件构造中,默认情况下将$wanted
设置为空数组。您可以方便地捕获匹配,并评估是否一次匹配(而不是有if(!empty($matches))
)。
更新:在@mickmackusa的回答中使用了一个更有说服力的regex,使用了查找,我对过滤的“普通”正则表达式的性能感到好奇,而不是使用查找。然后,一个测试用例 (仅在3v4l处进行1次迭代以避免轰炸它们,请使用您自己的服务器获取更多!)。
测试用例使用了100个具有潜在匹配的生成字符串,使用这两种方法在5000次迭代中运行。返回的匹配结果是相同的。具有前瞻性的单步正则表达式平均花费0.83秒,而两步“平原”正则表达式平均花费0.69秒。看起来,使用前瞻性比“直截了当”的方法要贵得多。
发布于 2022-01-12 21:40:00
好吧,你已经接受了一个间接的答案,因为我在问题下面的评论中要求改进问题。我的理解是,这意味着你不打算进一步澄清这个问题,而另一个答案则按你的意愿工作。因此,我将提供一个regex解决方案,这样您就不需要在进行初始regex提取之后使用迭代regex筛选。
对于有限的样本数据,您的需求归结为:
匹配整个“单词”(由空格分隔的可见字符),其中:
如果需要,可以随后使用array_unique()
消除重复的匹配字符串。
代码:(演示)
$str = '-9 Cycles 3 Temperature Levels Steam Sanitizet+ -Sensor Dry | ALSO AVAILABLE (PRICES MAY VARY) |- White - 1258843 - DVE45R6100W {+ Platinum - 1501 525 - DVE45R6100P desirable: 1258843 DVE45R6100W';
if (preg_match_all('~\b(?:[A-Z]{4,20}(*SKIP)(*FAIL)|[A-Z\d]{4,20})\b~', $str, $m)) {
var_export(array_unique($m[0]));
}
输出:
array (
0 => '1258843',
1 => 'DVE45R6100W',
2 => '1501',
3 => 'DVE45R6100P',
)
模式分解:
\b #the zero-width position between a character matched by \W and a character matched by \w
(?: #start non-capturing group
[A-Z]{4,20}(*SKIP)(*FAIL) #match and disqualify all-letter words
| #or
[A-Z\d]{4,20} #match between 4 and 20 digits or uppercase letters
) #end non-capturing group
\b #the zero-width position between a character matched by \W and a character matched by \w
这里有几种可供比较的可选择的正则表达式--一种不使用任何旁观者的模式,使用“跳过失败”技术来取消纯粹按字母顺序排列的“单词”。
\b(?=\S*\d)[A-Z\d]{4,20}\b
\b(?=[A-Z]*\d)[A-Z\d]{4,20}\b
\b(?:[A-Z]{4,20}(*SKIP)(*FAIL)|[A-Z\d]{4,20})\b
等价的非正则化过程(我不赞同)是:(演示)
foreach (explode(' ', $str) as $word) {
$length = strlen($word);
if ($length >= 4 // has 4 characters or more
&& $length <= 20 // has 20 characters or less
&& !isset($result[$word]) // not yet in result array
&& ctype_alnum($word) // comprised numbers and/or letters only
&& !ctype_alpha($word) // is not comprised solely of letters
&& $word === strtoupper($word) // has no lowercase letters
) {
$result[$word] = $word;
}
}
var_export(array_values($result));
https://stackoverflow.com/questions/70672645
复制相似问题