解析包含多个没有定界字符的条目的严格格式化的文本

Parse strictly formatted text containing multiple entries with no delimiting character

我有一个包含多个产品订单的字符串,这些订单在没有分隔符的情况下连接在一起。

我需要解析输入字符串并将三个子字符串集转换为单独的数据行。

我尝试使用 split()strstr() 函数拆分字符串,但无法生成所需的结果。

如何将此语句转换为不同的列?

RM是马币

来自这个声明:

"2 x Brew Coffeee Panas: RM7.42 x Tongkat Ali Ais: RM8.6"

进入单独的行:

并将这 2 行放入数据库中的 table:

Table:产品

Product Name Quantity Total Amount (RM)
Brew Coffeee Panas 2 7.4
Tongkat Ali Ais 2 8.6

*注意:“总金额”子字符串将可靠地具有精确到小数点后一位的数值。

如果您的字符串格式一致,您可以使用 regex。这是一个可以做到这一点的表达式:

(\d) x (.+?): RM(\d+\.\d)

基本用法

$re = '/(\d) x (.+?): RM(\d+\.\d)/';
$str = '2 x Brew Coffeee Panas: RM7.42 x Tongkat Ali Ais: RM8.6';
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
var_export($matches);

给出

array (
  0 => 
  array (
    0 => '2 x Brew Coffeee Panas: RM7.4',
    1 => '2',
    2 => 'Brew Coffeee Panas',
    3 => '7.4',
  ),
  1 => 
  array (
    0 => '2 x Tongkat Ali Ais: RM8.6',
    1 => '2',
    2 => 'Tongkat Ali Ais',
    3 => '8.6',
  ),
)

第 0 组始终是完全匹配,之后的组将是数量、产品和价格。

Try it online

我要做的第一件事是使用 preg_replace 执行简单的替换,以根据单个文件的已知格式借助 back-reference 插入捕获的项目小数点。超出该小数点的任何内容都构成下一项的一部分 - 在本例中为数量。

$str="2 x Brew Coffeee Panas: RM7.42 x Tongkat Ali Ais: RM8.625 x Koala Kebabs: RM15.23 x Fried Squirrel Fritters: RM32.4";

#   qty price
#   2   7.4
#   2   8.6
#   25  15.2
#   3   32.4



/*
    Our RegEx to find the decimal precision, 
    to split the string apart and the quantity
*/
$pttns=(object)array(
    'repchar'   =>  '@(RM\d{1,}\.\d{1})@',
    'splitter'  =>  '@(\|)@',
    'combo' =>  '@^((\d{1,}) x)(.*): RM(\d{1,}\.\d{1})$@'
);
# create a new version of the string with our specified delimiter - the PIPE
$str = preg_replace( $pttns->repchar, '|', $str );

# split the string intp pieces - discard empty items
$a=array_filter( preg_split( $pttns->splitter, $str, null ) );

#iterate through matches - find the quantity,item & price
foreach($a as $str){
    preg_match($pttns->combo,$str,$matches);
    $qty=$matches[2];
    $item=$matches[3];
    $price=$matches[4];
    
    printf('%s %d %d<br />',$item,$qty,$price);
}

产生:

Brew Coffeee Panas 2 7
Tongkat Ali Ais 2 8
Koala Kebabs 25 15
Fried Squirrel Fritters 3 32
  • 捕获一位或多位数字
  • 匹配space、x、space
  • 捕获一个或多个非冒号字符,直到第一个出现的冒号
  • 匹配冒号,space,然后RM
  • 捕获最大十进制长度为 1 的浮点值
    OP 在问题下的评论中说:it only take one decimal place for the amount

我的模式中没有“惰性量词”,因此正则表达式可以最快地移动。

这个正则表达式模式在样本数据和需求解释允许的情况下准确高效,因为它只包含贪婪的量词,如 Concise,因为它可以归功于取反字符 class,以及作为 Readable 的模式,因为没有多余的字符。

代码:(Demo)

var_export(
    preg_match_all('~(\d+) x ([^:]+): RM(\d+\.\d)~', $string, $m)
        ? array_slice($m, 1)  // omit the fullstring matches
        : []                  // if there are no matches
);

输出:

array (
  0 => 
  array (
    0 => '2',
    1 => '2',
  ),
  1 => 
  array (
    0 => 'Brew Coffeee Panas',
    1 => 'Tongkat Ali Ais',
  ),
  2 => 
  array (
    0 => '7.4',
    1 => '8.6',
  ),
)

您可以将 PREG_SET_ORDER 参数添加到 preg_match_all() 调用以帮助将匹配作为行进行迭代。

preg_match_all('~(\d+) x ([^:]+): RM(\d+\.\d)~', $string, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
    echo '<tr><td>' . implode('</td><td>', array_slice($match, 1)) . '</td></tr>';
}

您可以像这样使用正则表达式:

/(\d+)\sx\s([^:]+):\sRM(\d+\.?\d?)(?=\d|$)/

解释:

  • (\d+) 捕获一个或多个数字
  • \s 匹配空白字符
  • ([^:]+): 捕获一个或多个位于 : 字符之前的非 : 字符(如果您确切知道哪些字符可以存在于 : 字符之前 - 在本例中为小写和大写字母、数字 0 到 9 和空白字符)
  • (\d+\.?\d?) 捕获一个或多个数字,后跟 . 和另一个数字(如果存在)
  • (?=\d|$) 是一个积极的前瞻,它匹配主表达式之后的数字而不将其包含在结果中或字符串的末尾

您还可以将 PREG_SET_ORDER 标志添加到 preg_match_all() 以对结果进行分组:

PREG_SET_ORDER

Orders results so that $matches[0] is an array of first set of matches, $matches[1] is an array of second set of matches, and so on.

代码示例:

<?php
    $txt = "2 x Brew Coffeee Panas: RM7.42 x Tongkat Ali Ais: RM8.62 x B026 Kopi Hainan Kecil: RM312 x B006 Kopi Hainan Besar: RM19.5";
    $pattern = "/(\d+)\sx\s([^:]+):\sRM(\d+\.?\d?)(?=\d|$)/";

    if(preg_match_all($pattern, $txt, $matches, PREG_SET_ORDER)) {
       print_r($matches);
    }
?>

输出:

Array
(
    [0] => Array
        (
            [0] => 2 x Brew Coffeee Panas: RM7.4
            [1] => 2
            [2] => Brew Coffeee Panas
            [3] => 7.4
        )
    [1] => Array
        (
            [0] => 2 x Tongkat Ali Ais: RM8.6
            [1] => 2
            [2] => Tongkat Ali Ais
            [3] => 8.6
        )
    [2] => Array
        (
            [0] => 2 x B026 Kopi Hainan Kecil: RM31
            [1] => 2
            [2] => B026 Kopi Hainan Kecil
            [3] => 31
        )
    [3] => Array
        (
            [0] => 2 x B006 Kopi Hainan Besar: RM19.5
            [1] => 2
            [2] => B006 Kopi Hainan Besar
            [3] => 19.5
        )
)

现场观看php live editor and here regex tester