在 PHP 中将大字符串拆分为具有相同行数的较小块?

Split a large string into smaller chunks with equal number of lines in PHP?

遗留 PHP 系统将巨大的日志文件 (~5gb) 直接读取到内存中的变量并进行一些处理。

编辑:关于读取 5GB 内存是极不推荐的,其他建议请相信,由于一些我们无法更改的遗留设计,这必须保持不变。

现在我需要通过另一个每次调用最多需要 1000 行的服务来处理数据。

我尝试了两种方法,但都有效。

1- 将新行 char 处的整个字符串拆分为一个数组,然后使用 array_chunk 将该数组拆分为子数组,然后取每个子数组并内爆生成一个字符串

$logFileStr; // a variable that already contains 5gb file as string
$logLines = explode(PHP_EOL, $logFileStr);
$lineGroups = array_chunk($logLines, 1000);
foreach($lineGroups as $lineGroup)
{
    $linesChunk = implode(PHP_EOL, $lineGroup);

    $archiveService->store($linesChunk);
}

优点:速度很快,因为一切都在内存中运行 缺点:工作量大且需要大量内存

2- 最初将字符串变量的内容写入本地临时文件。然后使用exec函数分割文件

split -l 1000 localfile 

生成大量文件,每个文件 1000 行。 然后我可以简单地递归读取文件并将每个文件作为单个字符串处理。

优点:更简单易维护

缺点:涉及到磁盘 I/O,这很慢并且有很多写入读取开销

我的问题是,因为我已经在内存中有一个包含整个字符串的变量,我如何以可迭代的方式从该变量中读取每行 1000 行的块,这样我就可以避免写入磁盘或生成新的数组和重新合并开销?

解决此问题的一种方法是使用以下步骤:

  1. 在循环中将字符串解析为字符数组。
  2. 计算换行符的个数。
  3. 对于每第 1000 个换行符,提取从前一个子字符串结束处开始到当前换行符结束的子字符串。

我按照上述步骤创建了示例 php 代码:

<?php
$str = "line1\nline2\nline3\nline4\nline5\n"; // Sample string
$max_new_lines = 2; // Max number of lines. Replace this with 1000
$str_length = strlen($str);
$new_line_count = 0;
$str_chunk = "";
$start = 0;

// Loop through every character of the string
for ($i = 0; $i < $str_length; ++$i) {
  if ($str[$i] == "\n") {
    ++$new_line_count;

    // If we reached the max number of newlines, extract the substring
    if (($new_line_count % $max_new_lines) == 0) {
      $str_chunk = substr($str, $start, $i - $start);
      $start = $i + 1;
      // echo "\n\nchunk:\n" . $str_chunk;
    }
  }
}

// Extract the remaining lines
$str_chunk = substr($str, $start, $i - $start);
// echo "\n\nchunk:\n" . $str_chunk;

经过更多研究后,我偶然发现了这个问题 php explode every third instance of character and after some modification to the answer posted there () 我想出了这个片段,目前比我以前的方法效果更好。

$logFileStr; // a variable that already contains 5gb file as string

$chunks = preg_split('/((?:[^\n]*\n){1000})/', $logFileStr, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

print_r($chunks);

在测试字符串上,结果如下所示(拆分为 3)

Array
(
 [0] => 13923
        27846
        311769

 [1] => 831384
        935307
        1039230

 [2] => 1558845
        1662768
        1766691

 [3] => 1870614

)

正则表达式解释如下

?: 将在不创建捕获组的情况下进行匹配

[^\n] 匹配任何不是新行的东西

the * Quantifier — 在零次和无限次之间匹配,尽可能多次,按需回馈(贪心)

{1000} 量词 — 恰好匹配 1000 次

flag PREG_SPLIT_DELIM_CAPTURE 也会在结果集中添加换行符。