【问题标题】:PHP: tokenizing, using a regular Expression (mostly there)PHP:标记化,使用正则表达式(主要在那里)
【发布时间】:2015-10-25 14:54:54
【问题描述】:

我想对格式化字符串进行标记(非常类似于 printf),我想我只是缺少一点点:

  • %[number][one letter ctYymd] 应成为token²
  • $1...$10 将成为代币
  • 其他所有内容(普通文本)都成为标记。

我在the regExp simulator 中走得很远。这看起来应该这样做:

²更新:现在使用 # 而不是 %。 (windows命令行参数少麻烦)

这并不可怕,如果你专注于三个部分,通过管道连接(作为非此即彼),所以基本上它只是三个匹配。由于我想从头到尾匹配,所以我将东西包裹在 /^...%/ 中,并被一个可能重复 1 次或多次的不匹配组 (?:... 包围:

$exp = '/^(?:(%\\d*[ctYymd]+)|([^$%]+)|(\\$\\d))+$/'; 

我的来源仍然没有提供:

$exp = '/^(?:(%\\d*[ctYymd]+)|([^$%]+)|(\\$\\d))+$/';
echo "expression: $exp \n";

$tests = [
        '###%04d_Ball0n%02d$1',
        '%03d_Ball0n%02x$1%03d_Ball0n%02d$1',
        '%3d_Ball0n%02d',
    ];

foreach ( $tests as $test )
{
    echo "teststring: $test\n";
    if( preg_match( $exp, $test, $tokens) )
    {
        array_shift($tokens);
        foreach ( $tokens as $token )
            echo "\t\t'$token'\n";
    }
    else
        echo "not valid.";
} // foreach

我得到了结果,但是:匹配有问题。第一个 %[number][letter] 从不匹配,因此其他匹配双精度:

expression: /^((%\d*[ctYymd]+)|([^$%]+)|(\$\d))+$/ 
teststring: ###%04d_Ball0n%02d$1
        '$1'
        '%02d'
        '_Ball0n'
        '$1'
teststring: %03d_Ball0n%02x$1%03d_Ball0n%02d$1
not valid.teststring: %3d_Ball0n%02d
        '%02d'
        '%02d'
        '_Ball0n'
teststring: %d_foobardoo
        '_foobardoo'
        '%d'
        '_foobardoo'
teststring: Ball0n%02dHamburg%d
        '%d'
        '%d'
        'Hamburg'

【问题讨论】:

    标签: php regex tokenize


    【解决方案1】:

    解决方案(由 OP 编辑​​):我使用了两个细微的变化(仅关于“包装”):首先用于验证,然后用于标记化:

    #\d*[ctYymd]+|\$\d+|[^#\$]+
    

    RegEx Demo

    代码:

    $core = '#\d*[ctYymd]+|\$\d+|[^#\$]+';
    $expValidate = '/^('.$core.')+$/m';
    $expTokenize = '/('.$core.')/m';
    
    $tests = [
            '#3d-',
            '#3d-ABC',
            '***#04d_Ball0n#02d$1',
            '#03d_Ball0n#02x$AwrongDollar',
            '#3d_Ball0n#02d',
            'Badstring#02xWrongLetterX'
        ];
    
    foreach ( $tests as $test )
    {
        echo "teststring: [$test]\n";
    
        if( ! preg_match_all( $expValidate, $test) )
        {
            echo "not valid.\n";
            continue;
        }
        if( preg_match_all( $expTokenize, $test, $tokens) ) {
            foreach ( $tokens[0] as $token )
                echo "\t\t'$token'\n";
        }
    
    } // foreach
    

    输出:

    teststring: [#3d-]
            '#3d'
            '-'
    teststring: [#3d-ABC]
            '#3d'
            '-ABC'
    teststring: [***#04d_Ball0n#02d$1]
            '***'
            '#04d'
            '_Ball0n'
            '#02d'
            '$1'
    teststring: [#03d_Ball0n#02x$AwrongDollar]
    not valid.
    teststring: [#3d_Ball0n#02d]
            '#3d'
            '_Ball0n'
            '#02d'
    teststring: [Badstring#02xWrongLetterX]
    not valid.
    

    【讨论】:

    • 太棒了!太感谢了!分析我的错误:preg_match_all 而不是preg_match。因为:根本就错了。并将我从不匹配的外括号中拯救出来。
    • 我唯一缺少的东西:不会在错误的字符串上对我大喊大叫,只是跳过,即Badstring%02xWrongLetterX
    • 您的前瞻 (? 并没有为我解决问题。错误的表达式导致了一个匹配的块(这不能确定表示错误的格式字符串)。想出了两个小的变化,编辑你的帖子。 (希望没问题)。 (出于不相关的原因,也将 % 替换为 #。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-24
    • 1970-01-01
    相关资源
    最近更新 更多