【发布时间】:2016-03-15 17:24:11
【问题描述】:
我有一个输入文本文件,如下所示:
BEGIN
#1
#2
#3
#4
#5
#6
1 2015-05-31 2001-11-24 'Name Surname' ID_1 0
2 2011-04-01 ? ? ID_2 1
2 2013-02-24 ? ? ID_3 1
2 2014-02-28 ? 'Name Surname' ID_4 2
END
#7 'value 1'
#8 'value 2'
#9 'value 3'
#10 'value 4'
END
当文本文件中有BEGIN时,从那里开始一个循环,其中以#开头的每一行都是一个键,而相对值是每个后续行的列,直到END ,生成如下数组:
Array ( [#1] => Array ( [0] => 1 [1] => 2 [2] => 2 [3] => 2 ) [#2] => Array ( [0] => 2015-05-31 [1] => 2011-04-01 [2] => 2013-02-24 [3] => 2014-02-28 ) [#3] => Array ( [0] => 2001-11-24 [1] => ? [2] => ? [3] => ? ) [#4] => Array ( [0] => 'Name Surname' [1] => ? [2] => ? [3] => 'Name Surname' ) [#5] => Array ( [0] => ID_1 [1] => ID_2 [2] => ID_3 [3] => ID_4 ) [#6] => Array ( [0] => 0 [1] => 1 [2] => 1 [3] => 2 ) )
否则,如果文本文件中没有BEGIN,但你发现有一行以#开头,它的相对值是单引号之间的值,生成如下数组:
Array ( [#7] => 'value 1' [#8] => 'value 2' [#9] => 'value 3' [#10] => 'value 4' )
这是我想要得到的,我当前的代码如下:
<?php
$time = microtime();
$time = explode(' ', $time);
$time = $time[1] + $time[0];
$start = $time;
ini_set("max_execution_time", 300); // 300 seconds = 5 minutes
ini_set("pcre.backtrack_limit", "100000000"); // default 100k = "100000"
ini_set("memory_limit", "1024M");
$txt_path = "./test_2.txt";
$txt_data = @file_get_contents($txt_path) or die("Could not access file: $txt_path");
//echo $txt_data;
/* BEGIN ARRAY FOR LOOP ENTRIES */
$loop_pattern = "/BEGIN(.*?)END/s";
preg_match_all($loop_pattern, $txt_data, $matches);
$loops = $matches[0];
$loops_count = count($loops);
//echo("<br><br>".$loops_count."<br><br>");
foreach ($loops as $key => $value) {
$value = trim($value);
$pattern = array("/BEGIN(.*?)/", "/END(.*?)/", "/[[:blank:]]+/");
$replacement = array("", "", " ");
$value = preg_replace($pattern, $replacement, $value);
//echo $value."<br><br>";
preg_match_all( '/^#\d+/m', $value, $matches );
$keys = $matches[0];
//print_r($keys);
//echo "<br><br>";
$value = preg_replace( '/^#\d+\s*/m', '', $value );
$value = str_replace( "\n", " ", $value );
$pattern = '/'.str_repeat( "('[^']+'|\S+)\s+", count( $keys ) ).'/';
preg_match_all( $pattern, $value, $matches );
//print_r($matches);
//echo "<br><br>";
$loop_dic = array_combine( $keys, array_slice( $matches, 1 ) );
print_r( $loop_dic );
echo("<br><br>");
}
/* END ARRAY FOR LOOP ENTRIES */
/* BEGIN ARRAY FOR NO LOOP ENTRIES */
$txt_data_without_loops = preg_replace( "/BEGIN(.*?)END/s", "", $txt_data );
//echo $txt_data_without_loops;
$pattern = array("/END(.*?)/", "/[[:blank:]]+/");
$replacement = array("", " ");
$txt_data_without_loops_clean = preg_replace($pattern, $replacement, $txt_data_without_loops);
//echo $txt_data_without_loops_clean;
preg_match_all( '/^#(.*?)\S+/m', $txt_data_without_loops_clean, $matches );
$keys = $matches[0];
//print_r($keys);
$txt_data_without_loops_clean = preg_replace( '/^#(.*?)\S+\s*/m', '', $txt_data_without_loops_clean );
//print_r($txt_data_without_loops_clean);
$txt_data_without_loops_clean_no_newline = str_replace( "\n", " ", $txt_data_without_loops_clean );
//print_r($txt_data_without_loops_clean_no_newline);
$pattern = '/'.str_repeat( "('[^']+'|\S+)\s+", 1 ).'/';
preg_match_all( $pattern, $txt_data_without_loops_clean_no_newline, $matches );
//print_r( $matches[0] );
$no_loop_dic = array_combine( $keys, $matches[0] );
print_r( $no_loop_dic );
echo("<br><br>");
/* END ARRAY FOR NO LOOP ENTRIES */
$time = microtime();
$time = explode(' ', $time);
$time = $time[1] + $time[0];
$finish = $time;
$total_time = round(($finish - $start), 4);
echo '<br><br><b>Page generated in '.$total_time.' seconds.</b><br><br>';
?>
作为第一种方法,为了获得 BEGIN-END 循环和相关数组,我读取输入文件:
$txt_path = "./input.txt";
$txt_data = @file_get_contents($txt_path) or die("<b>Could not access file: $txt_path</b><br><br>");
这适用于小文件,但是对于大输入文件,它会在浏览器中生成无响应时间(我正在 Firefox 上进行测试),可能是因为 RAM 饱和以解析整个大文件(我的笔记本电脑有 3GB 的 RAM)。
我在 php 文件中尝试了以下设置:
ini_set("max_execution_time", 300); // 300 seconds = 5 minutes
ini_set("pcre.backtrack_limit", "100000000"); // default 100k = "100000"
ini_set("memory_limit", "1024M");
这似乎解决了一些文件不是那么大的问题,而对于大文件,这个过程已经完成而没有错误,只是没有同时使用很多资源......所以,那不是最好的解决方案。
在网上搜索,我找到了this page我在哪里阅读:
如果您正在阅读文件,请逐行阅读,而不是读入 完整的文件进入内存。看看fgets 和 SplFileObject::fgets.
所以我决定使用 fgets 来读取和解析整个输入文件。 为所有行生成一个数组后,我需要从每个循环中提取它,将其添加到 loops_array,而我会将其他 no_loop 键值对添加到另一个数组。
我的想法,似乎很快,是找到每个BEGIN的索引,这样:
$txt_path = "./test.txt";
$txt_data = @fopen($txt_path, "rb") or die("<b>Could not access file: $txt_path</b><br/><br/>");
$lines = array();
while ( !feof($txt_data) ) {
$line = fgets($txt_data, 1024);
//echo($line."<br/><br/>");
array_push($lines, trim($line));
}
$lines = array_filter($lines);
//print_r($lines);
//echo("<br/><br/>");
$begins = array_keys($lines, "BEGIN");
//echo("<b>Begins:</b><br/><br/>");
//print_r($begins);
//echo("<br/><br/>");
但现在我需要在 $begins 数组中的每个元素之后找到第一个 END 的索引...如果我这样做:
$ends = array_keys($lines, "END");
//echo("<b>Ends:</b><br/><br/>");
//print_r($ends);
//echo("<br/><br/>");
它还考虑输入文件的 no_loop 区域中的 END 字符串,而我应该在每个 BEGIN 之后找到 END 字符串的第一个匹配项的索引,然后将它们与:
$begins_ends = array_combine($begins, $ends);
并使用array_slice 提取所有循环,最后将每个$loop 添加到一个新数组$loops,就像这样:
$i = 0;
$loops = array();
foreach ($begins_ends as $key => $value) {
$begin = trim($key);
$end = trim($value);
$loop = array_slice( $lines, $begin, ($end - $begin), false );
$this_loop = array();
for ($el=$begin; $el < $end+1; $el++) {
array_push($this_loop, $lines[$el]);
unset($lines[$el]);
}
array_push($loops, $this_loop);
$loop = array_values($lines);
//echo("<b>Loops Dictionary $i:</b><br/><br/>");
//print_r($loop);
//echo("<br/><br/>");
$i++;
}
//print_r($loops);
//echo("<br/><br/>");
问题是获取正确的$ends数组,不考虑输入文件中no_loop zone的END字符串,获取之前的输出:
Array ( [#1] => Array ( [0] => 1 [1] => 2 [2] => 2 [3] => 2 ) [#2] => Array ( [0] => 2015-05-31 [1] => 2011-04-01 [2] => 2013-02-24 [3] => 2014-02-28 ) [#3] => Array ( [0] => 2001-11-24 [1] => ? [2] => ? [3] => ? ) [#4] => Array ( [0] => 'Name Surname' [1] => ? [2] => ? [3] => 'Name Surname' ) [#5] => Array ( [0] => ID_1 [1] => ID_2 [2] => ID_3 [3] => ID_4 ) [#6] => Array ( [0] => 0 [1] => 1 [2] => 1 [3] => 2 ) )
Array ( [#7] => 'value 1' [#8] => 'value 2' [#9] => 'value 3' [#10] => 'value 4' )
以最快的方式和最低的内存使用,解决大文件浏览器无响应的问题。
谢谢
【问题讨论】:
-
您需要明确您的问题到底是什么,并将您的代码缩减为minimal reproducible example。此外,如果您想逐行处理文本文件,您可能需要查看
file()命令。 -
简单的否决票,但会更好地阅读并找到解决方案。我正在寻找已发布问题的答案:什么不清楚?