正则表达式第n个匹配

4

我知道这可能看起来是个愚蠢的请求,但我还是要问一下。

我希望使用正则表达式在数字列表中找到每个第n个逗号。例如:

    88574,93243,129659,135504,136357,141052,141619,141619,142195,144622,144946,...

您可以将每4个逗号','替换为',\r\n',这样就可以将数字列表转换为n行4列的网格。

查找所有逗号很简单:

    [^0-9]

在上述列表中,哪个可以找到所有逗号。现在我要如何将这些匹配项分组以排除每四个中的三个。

我可以使用PHP preg_matches来完成此操作,但我正在使用它与mysql正则表达式替换函数一起使用,因此更喜欢纯正则表达式答案(如果存在)。

我在MySQL中使用的函数如下:

    DROP FUNCTION IF EXISTS `regex_replace`$$  

    CREATE DEFINER=`root`@`127.0.0.1`   
    FUNCTION `regex_replace`(pattern VARCHAR(1000),replacement VARCHAR(1000),original TEXT)   
    RETURNS VARCHAR(1000) CHARSET latin1  
        DETERMINISTIC  
    BEGIN  
     DECLARE temp VARCHAR(1000);  
     DECLARE ch VARCHAR(1);  
     DECLARE i INT;  
     SET i = 1;  
     SET temp = '';  
     IF original REGEXP pattern THEN  
     loop_label: LOOP  
       IF i>CHAR_LENGTH(original) THEN  
                 LEAVE loop_label;  
       END IF;  
       SET ch = SUBSTRING(original,i,1);  
         IF NOT ch REGEXP pattern THEN  
            SET temp = CONCAT(temp,ch);  
       ELSE  
          SET temp = CONCAT(temp,replacement);  
                END IF;  
       SET i=i+1;  
              END LOOP;  
     END IF;  
     RETURN temp;  
    END$$  

正如您所看到的,正则表达式本身不必处理复杂的匹配。因此,一个能够选择第n个逗号的正则表达式就足够了。

我希望这样可以澄清问题。

编辑:

我已经将lib_mysqludf_preg库添加到服务器上,其中包含preg_replace函数。这是MySQL的PCRE实现,如果我能解决选择每四个“,”并替换为“,\r\n”的正则表达式问题,它应该可以工作。


这可能是将数字分组为四个一组的好方法:([0-9]*[^0-9]){4} - James Webster
谢谢James, 与Tims的答案非常相似。请查看他的回复和更新后的问题。再次感谢, Fin - Fin
1
我不使用MySQL,但如果我正确理解了您的编辑,那么您现在可以访问preg_replace,因此@Tim的解决方案应该是可行的。 - Alan Moore
2个回答

4
$result = preg_replace('/(?:[^,]*,){4}/', '\0\r\n', $subject);

这个匹配四个逗号分隔的值(我假设你在组内字符串中不会出现逗号),并在它们之后添加CRLF。请注意,以上是基于PHP的解决方案。如需使用纯MySQL解决方案,请安装lib_mysqludf_preg并使用:
    SELECT preg_replace('/(?:[^,]*,){4}/', '${0}\r\n', `fieldname`) as 'new_layout' from `tablename`;

感谢所有为此做出贡献的人。


1
谢谢你的回答,Tim。这非常接近我想要做的事情,但还不够完美。你的正则表达式匹配了整个字符组,即“88574,93243,129659,135504,”,而我需要匹配每四个逗号,如下所示:88574,93243,129659,135504,136357,141052,141619,141619,142195,144622,144946,177824,... no no no yes no no no yes no no no yes我不能使用PHP,因为结果是由MySQL函数处理的。希望我的意思表达清楚。 - Fin
MySQL的正则表达式无法完成此任务。它们只能选择与正则表达式匹配或不匹配的列。就是这样。 - Tim Pietzcker
我已经更新了问题,以反映使用MySQL并添加了我正在使用的函数。 - Fin
我仍然看不到一种方法——如果你只想选择逗号,你需要使用环视断言,而MySQL不支持这些。你必须逐个字符地遍历字符串,计算逗号并在每四个逗号上执行操作。 - Tim Pietzcker
1
再次感谢您的帮助,我可能一时脑抽了。一旦我正确编译和安装了lib_mysqludf_preg,以下命令就可以在MySQL中给我提供所需的内容:SELECT preg_replace('/(?:[^,]*,){4}/', '${0}\r\n', '88574,93243,129659,135504,136357,141052,141619,141619,142195,144622,144946,177824,... [MORE NUMBERS HERE] ...212489,217352); 你一开始就非常接近了(注意'${0}'是唯一的区别),我必须要给你正确答案的功劳。干杯,Fin。 - Fin

1

如果您想匹配每个逗号,那么更直接的模式,也可以使用。

要匹配每四个逗号,如果MySQL支持向后查找,也许您可以使用(?<=(^|\r\n)(\d+,){3}\d+),。这假设每次替换都在下一次匹配之前执行。否则,也许(?<=^((\d+,){4})*(\d+,){3}\d+),会起作用。


MySQL不支持向后查找断言。更不用说在向后查找中有不定重复了。不过在.NET中,你的正则表达式应该可以正常工作。 - Tim Pietzcker
谢谢!有点怀疑。不过还是值得一试的。 - henko
谢谢,Henko,最后一个问题,它只会选择第四个逗号的实例,而不是每四个(四分之一)。我已经最后一次更新了问题。 - Fin
1
鉴于您的正则表达式引擎支持向后查找,我相信这些模式将匹配每个第四个逗号。使用哪种模式取决于它们是一次匹配/替换一个还是全部匹配/替换。 - henko
谢谢Henko, 我的错。我已经找到了我认为是困惑的点,尽管PCRE库支持向后查找,但它不支持在向后查找中使用可变重复。是否有任何方法可以为这种情况重新编写"(?<=^((\d+,){4})*(\d+,){3}\d+)"。再次感谢,Fin - Fin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接