У меня есть входной текстовый файл, подобный следующему:
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), возможно, для насыщения оперативной памяти, чтобы проанализировать весь большой файл (мой ноутбук имеет 3 ГБ БАРАН).
Я попробовал следующую настройку в файле 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");
Это, кажется, решает проблему с некоторыми файлами, не такими большими по размеру, в то время как с большими файлами процесс завершился без ошибок, только без использования большого количества ресурсов в одно и то же время … Так что это не лучшее решение ,
Поискав в сети, я нашел эта страница где я читаю:
Если вы читаете файлы, читайте их построчно, а не читайте в
полный файл в память. смотреть на 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/>");
но теперь мне нужно найти индекс первого END
после каждого элемента в $begins
массив … Если я сделаю:
$ends = array_keys($lines, "END");
//echo("<b>Ends:</b><br/><br/>");
//print_r($ends);
//echo("<br/><br/>");
он также учитывает END
строка в зонах no_loop входного файла, в то время как я должен найти индекс первого совпадения для END
строка после каждого BEGIN
, комбинируя затем их с:
$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
массив, не учитывая END
строка зон no_loop во входном файле, получая предыдущий вывод:
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' )
с самым быстрым подходом и самым низким использованием памяти, чтобы решить время отсутствия ответа в браузере с файлами большого размера.
Спасибо
Было просто полезно сказать, что не нужно было использовать fgets (), но Fread (); источник информации Вот!
Как вы можете прочитать там, файл() очень похож на ранее использованный file_get_contents (), так что это не должно иметь значение.
Предыдущий рабочий код должен быть адаптирован так просто:
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
#11
#12
#13
#14
#15
#16
1 2015-05-31 2001-11-24 'Name Surname' ID_5 0
2 2011-04-01 ? ? ID_6 1
2 2013-02-24 ? ? ID_7 1
2 2014-02-28 ? 'Name Surname' ID_8 2
END
BEGIN
#17
#18
#19
#20
#21
#22
1 2015-05-31 2001-11-24 'Name Surname' ID_9 0
2 2011-04-01 ? ? ID_10 1
2 2013-02-24 ? ? ID_11 1
2 2014-02-28 ? 'Name Surname' ID_12 2
END
<?php
$time = microtime();
$time = explode(" ", $time);
$time = $time[1] + $time[0];
$start = $time;
$filename = "./test_2.txt";
$handle = fopen($filename, "rb") or die("<b>Could not access file: $filename</b><br/><br/>");
$contents = fread($handle, filesize($filename));
fclose($handle);
//echo($contents."<br><br>");
$loop_pattern = "/BEGIN(.*?)END/s";
preg_match_all($loop_pattern, $contents, $matches);
$loops = $matches[0];
//print_r($loops);
//echo("<br><br>");
$loops_count = count($loops);
//print_r($loops_count);
//echo "<br><br>";
foreach ($loops as $key => $value) {
$value = trim($value);
//echo($value."<br><br>");
$pattern = array("/[[:blank:]]+/", "/BEGIN(.*)/", "/END(.*)/");
$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>";
$values = array_combine( $keys, array_slice( $matches, 1, count( $keys ), false ) );
print_r( $values );
echo "<br><br>";
}
$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/>");
?>
Я тоже убрал @
, пишу:
fopen($filename, "rb") or die("<b>Could not access file: $filename</b><br/><br/>");
вместо предыдущего:
@fopen($txt_path, "rb") or die("<b>Could not access file: $txt_path</b><br/><br/>");
как предложено Вот.
РЕДАКТИРОВАТЬ 1
Другой подход заключается в следующем:
$txt_path = "./test_2.txt";
$handle = new SplFileObject($txt_path);
// Loop until we reach the end of the file.
$lines_array = array();
while ( !$handle->eof() ) {
$line = $handle->fgets();
//echo($line."<br/><br/>"); // Echo one line from the file.
array_push($lines_array, trim($line));
}
// Unset the file to call __destruct(), closing the file handle.
$handle = null;
$lines_array = array_filter($lines_array);
//print_r($lines_array);
//echo("<br/><br/>");
$lines_joined = implode("\n", $lines_array);
//echo($lines_joined."<br/><br/>");
Других решений пока нет …