这个问题几乎与https://stackoverflow.com/questions/18318530/how-to-transform-structured-textfiles-into-php-multidimensional-array重复,但由于无法理解所给出的基于正则表达式的解决方案,我再次发布了该问题。最好只使用PHP来解决这个问题,这样我才能真正从中学习(regex在这一点上太难理解了)。
假设以下文本文件:
HD Alcoa Earnings Soar; Outlook Stays Upbeat
BY By James R. Hagerty and Matthew Day
PD 12 July 2011
LP
Alcoa Inc.'s profit more than doubled in the second quarter.
The giant aluminum producer managed to meet analysts' forecasts.
However, profits wereless than expected
TD
Licence this article via our website:
http://example.com我使用PHP读取这个文本文件,这是一种将文件内容放入数组的健壮方法,如下所示:
array(
[HD] => Alcoa Earnings Soar; Outlook Stays Upbeat,
[BY] => By James R. Hagerty and Matthew Day,
[PD] => 12 July 2011,
[LP] => Alcoa Inc.'s profit...than expected,
[TD] => Licence this article via our website: http://example.com
)单词HD BY PD LP TD是标识文件中新部分的键。在数组中,所有换行符都可以从值中删除。理想情况下,我可以在没有正则表达式的情况下做到这一点。我相信在所有的钥匙上爆炸可能是一种方法,但它会非常肮脏:
$fields = array('HD', 'BY', 'PD', 'LP', 'TD');
$parts = explode($text, "\nHD ");
$HD = $parts[0];是否有人对如何循环文本(甚至一次)有一个更清晰的想法,并将其分割成上面给出的数组?
发布于 2013-08-23 19:47:07
这是另一种甚至更短的不用正则表达式的方法。
/**
* @param array array of stopwords eq: array('HD', 'BY', ...)
* @param string Text to search in
* @param string End Of Line symbol
* @return array [ stopword => string, ... ]
*/
function extract_parts(array $parts, $str, $eol=PHP_EOL) {
$ret=array_fill_keys($parts, '');
$current=null;
foreach(explode($eol, $str) AS $line) {
$substr = substr($line, 0, 2);
if (isset($ret[$substr])) {
$current = $substr;
$line = trim(substr($line, 2));
}
if ($current) $ret[$current] .= $line;
}
return $ret;
}
$ret = extract_parts(array('HD', 'BY', 'PD', 'LP', 'TD'), $str);
var_dump($ret);为什么不使用正则表达式?
由于php文档,特别是preg_*函数中的文档,建议不要使用正则表达式(如果不是强烈要求的话)。我想知道在这个问题的答案中,哪一个例子表现得最好。
这一结果让我感到惊讶:
Answer 1 by: hek2mgl 2.698 seconds (regexp)
Answer 2 by: Emo Mosley 2.38 seconds
Answer 3 by: anubhava 3.131 seconds (regexp)
Answer 4 by: jgb 1.448 seconds我本以为regexp变体是最快的。
嗯,在任何情况下不使用正则表达式并不是坏事。换句话说:一般来说,使用正则表达式并不是最好的解决方案。你必须逐案决定最好的解决方案。
您可以使用这个剧本重复测量。
编辑
下面是一个使用regexp模式的简短的、更优化的示例。仍然没有我上面的例子那么快,但是比其他基于regexp的例子更快。
输出格式可以优化(空格/换行)。
function extract_parts_regexp($str) {
$a=array();
preg_match_all('/(?<k>[A-Z]{2})(?<v>.*?)(?=\n[A-Z]{2}|$)/Ds', $str, $a);
return array_combine($a['k'], $a['v']);
}发布于 2013-08-30 10:35:38
代表简化、快速和可读的regex代码的请求!
(来自Pr0no in comments)您认为您可以简化regex或者介绍如何从php解决方案开始吗?是的,Pr0n0,我相信我可以简化regex。
我想说明一下,regex是目前为止最适合这项工作的工具,它不必像我们前面看到的那样可怕和不可读。我把这个功能分解成可以理解的部分。
我已经避免了复杂的regex特性,比如捕获组和通配符表达式,并且专注于生产一些简单的东西,在3个月后你会觉得很舒服。
我提议的函数(注释)
function headerSplit($input) {
// First, let's put our headers (any two consecutive uppercase characters at the start of a line) in an array
preg_match_all(
"/^[A-Z]{2}/m", /* Find 2 uppercase letters at start of a line */
$input, /* In the '$input' string */
$matches /* And store them in a $matches array */
);
// Next, let's split our string into an array, breaking on those headers
$split = preg_split(
"/^[A-Z]{2}/m", /* Find 2 uppercase letters at start of a line */
$input, /* In the '$input' string */
null, /* No maximum limit of matches */
PREG_SPLIT_NO_EMPTY /* Don't give us an empty first element */
);
// Finally, put our values into a new associative array
$result = array();
foreach($matches[0] as $key => $value) {
$result[$value] = str_replace(
"\r\n", /* Search for a new line character */
" ", /* And replace with a space */
trim($split[$key]) /* After trimming the string */
);
}
return $result;
}以及输出(注意:根据操作系统的不同,您可能需要在\r\n函数中用\n替换str_replace )。
array(5) {
["HD"]=> string(41) "Alcoa Earnings Soar; Outlook Stays Upbeat"
["BY"]=> string(35) "By James R. Hagerty and Matthew Day"
["PD"]=> string(12) "12 July 2011"
["LP"]=> string(172) "Alcoa Inc.'s profit more than doubled in the second quarter. The giant aluminum producer managed to meet analysts' forecasts. However, profits wereless than expected"
["TD"]=> string(59) "Licence this article via our website: http://example.com"
}删除Cleaner函数的注释
此功能的浓缩版本。它与上面完全相同,但是删除了注释:
function headerSplit($input) {
preg_match_all("/^[A-Z]{2}/m",$input,$matches);
$split = preg_split("/^[A-Z]{2}/m",$input,null,PREG_SPLIT_NO_EMPTY);
$result = array();
foreach($matches[0] as $key => $value) $result[$value] = str_replace("\r\n"," ",trim($split[$key]));
return $result;
}理论上说,在实际代码中使用哪一个并不重要,因为解析注释对性能的影响很小,所以使用您比较满意的那个。
正则表达式的分解()
函数中只有一个表达式(尽管使用了两次),为了简单起见,让我们将其分解如下:
"/^[A-Z]{2}/m"
/ - This is a delimiter, representing the start of the pattern.
^ - This means 'Match at the beginning of the text'.
[A-Z] - This means match any uppercase character.
{2} - This means match exactly two of the previous character (so exactly two uppercase characters).
/ - This is the second delimiter, meaning the pattern is over.
m - This is 'multi-line mode', telling regex to treat each line as a new string.这个微小的表达式强大到足以匹配HD,而不是行开头的HDM,而行中间的HD (例如,在Full HD中)不匹配。用非正则表达式选项很难实现这一点。
如果您想要两个或更多的连续大写字符(而不是确切的2个大写字符)来表示新的部分,请使用/^[A-Z]{2,}/m。
使用预定义的标头列表。
在阅读了您的最后一个问题之后,以及您在@jgb的帖子中的评论,您似乎想要使用一个预定义的标题列表。您可以通过用"/^(HD|BY|WC|PD|SN|SC|PG|LA|CY|LP|TD|CO|IN|NS|RE|IPC|PUB|AN)/m替换正则表达式来做到这一点--在正则表达式中,|被视为‘或’。
基准-可读性并不意味着缓慢的
在某种程度上,基准测试已经成为对话的一部分,尽管我认为它忽略了为您提供一个可读性和可维护性的解决方案,我重写了JGB的基准测试,向您展示了一些东西。
下面是我的结果,显示基于regex的代码是这里最快的选项(这些结果基于5,000次迭代):
SWEETIE BELLE'S SOLUTION (2 UPPERCASE IS A HEADER): 0.054 seconds
SWEETIE BELLE'S SOLUTION (2+ UPPERCASE IS A HEADER): 0.057 seconds
MATEWKA'S SOLUTION (MODIFIED, 2 UPPERCASE IS A HEADER): 0.069 seconds
BABA'S SOLUTION (2 UPPERCASE IS A HEADER): 0.075 seconds
SWEETIE BELLE'S SOLUTION (USES DEFINED LIST OF HEADERS): 0.086 seconds
JGB'S SOLUTION (USES DEFINED LIST OF HEADERS, MODIFIED): 0.107 seconds以及格式不正确的输出的解决方案基准:
MATEWKA'S SOLUTION: 0.056 seconds
JGB'S SOLUTION: 0.061 seconds
HEK2MGL'S SOLUTION: 0.106 seconds
ANUBHAVA'S SOLUTION: 0.167 seconds我之所以提供JGB函数的修改版本,是因为他的原始函数在将段落添加到输出数组之前没有删除换行符。小字符串操作在性能上有很大的差异,必须对其进行同等的基准测试,以获得公平的性能评估。
此外,使用jgb的函数,如果传入完整的标题列表,您将在数组中获得一组空值,作为在分配密钥之前,似乎不检查密钥是否存在。。如果您希望稍后循环这些值,这将导致另一个性能下降,因为您必须先检查empty。
发布于 2013-08-25 20:47:13
这里是一个没有regex的简单解决方案
$data = explode("\n", $str);
$output = array();
$key = null;
foreach($data as $text) {
$newKey = substr($text, 0, 2);
if (ctype_upper($newKey)) {
$key = $newKey;
$text = substr($text, 2);
}
$text = trim($text);
isset($output[$key]) ? $output[$key] .= $text : $output[$key] = $text;
}
print_r($output);输出
Array
(
[HD] => Alcoa Earnings Soar; Outlook Stays Upbeat
[BY] => By James R. Hagerty and Matthew Day
[PD] => 12 July 2011
[LP] => Alcoa Inc.'s profit more than doubled in the second quarter.The giant aluminum producer managed to meet analysts' forecasts.However, profits wereless than expected
[TD] => Licence this article via our website:http://example.com
)Note
您还可能希望执行以下操作:
HD|BY|PD|LP|TD$text = trim($text),以便在文本中保留新行https://stackoverflow.com/questions/18359729
复制相似问题