解析包含多个没有定界字符的条目的严格格式化的文本
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 x Brew Coffeee Panas: RM7.4
- 2 x 东革阿里艾斯: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 组始终是完全匹配,之后的组将是数量、产品和价格。
我要做的第一件事是使用 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。
我有一个包含多个产品订单的字符串,这些订单在没有分隔符的情况下连接在一起。
我需要解析输入字符串并将三个子字符串集转换为单独的数据行。
我尝试使用 split()
和 strstr()
函数拆分字符串,但无法生成所需的结果。
如何将此语句转换为不同的列?
RM是马币
来自这个声明:
"2 x Brew Coffeee Panas: RM7.42 x Tongkat Ali Ais: RM8.6"
进入单独的行:
- 2 x Brew Coffeee Panas: RM7.4
- 2 x 东革阿里艾斯: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 组始终是完全匹配,之后的组将是数量、产品和价格。
我要做的第一件事是使用 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。