slice — Как извлечь блоки из текстового файла, прочитав его построчно

У меня есть входной текстовый файл, подобный следующему:

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' )

с самым быстрым подходом и самым низким использованием памяти, чтобы решить время отсутствия ответа в браузере с файлами большого размера.

Спасибо

-1

Решение

Было просто полезно сказать, что не нужно было использовать fgets (), но Fread (); источник информации Вот!

Как вы можете прочитать там, файл() очень похож на ранее использованный file_get_contents (), так что это не должно иметь значение.

Предыдущий рабочий код должен быть адаптирован так просто:

  • Содержимое файла test_2.txt:

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:

<?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/>");
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]