텍스트 파일의 줄 수를 효율적으로 계산합니다. (200MB 이상)
내 스크립트에서 치명적인 오류가 발생한다는 사실을 방금 발견했습니다.
Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109
그 라인은 다음과 같습니다.
$lines = count(file($path)) - 1;
그래서 파일을 메모리에로드하고 줄 수를 세는 데 어려움이 있다고 생각하는데, 메모리 문제없이이 작업을 수행 할 수있는 더 효율적인 방법이 있습니까?
2MB에서 500MB까지 줄 수를 세는 데 필요한 텍스트 파일입니다. 가끔 공연 일 수도 있습니다.
도움을 주셔서 감사합니다.
전체 파일을 메모리에로드하지 않기 때문에 메모리를 덜 사용합니다.
$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
$line = fgets($handle);
$linecount++;
}
fclose($handle);
echo $linecount;
fgets
한 줄을 메모리에로드합니다 (두 번째 인수 $length
가 생략되면 우리가 원하는 줄의 끝에 도달 할 때까지 스트림에서 계속 읽습니다). 벽 시간과 메모리 사용량에 관심이 있다면 PHP가 아닌 다른 것을 사용하는 것만 큼 빠르지 않을 것입니다.
이것의 유일한 위험은 어떤 줄이 특히 긴 경우입니다 (줄 바꿈이없는 2GB 파일을 만나면 어떨까요?). 어떤 경우에는 덩어리로 슬러 핑하고 줄 끝 문자를 세는 것이 좋습니다.
$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
$line = fgets($handle, 4096);
$linecount = $linecount + substr_count($line, PHP_EOL);
}
fclose($handle);
echo $linecount;
fgets()
호출 루프를 사용하는 것이 좋은 솔루션이며 작성하기 가장 간단합니다.
내부적으로 8192 바이트의 버퍼를 사용하여 파일을 읽더라도 코드는 여전히 각 행에 대해 해당 함수를 호출해야합니다.
이진 파일을 읽는 경우 기술적으로 한 줄이 사용 가능한 메모리보다 클 수 있습니다.
이 코드는 각각 8kB의 청크 단위로 파일을 읽은 다음 해당 청크 내의 줄 바꿈 수를 계산합니다.
function getLines($file)
{
$f = fopen($file, 'rb');
$lines = 0;
while (!feof($f)) {
$lines += substr_count(fread($f, 8192), "\n");
}
fclose($f);
return $lines;
}
각 라인의 평균 길이가 최대 4kB이면 이미 함수 호출 비용을 절약하기 시작하고 대용량 파일을 처리 할 때 추가 될 수 있습니다.
기준
1GB 파일로 테스트를 실행했습니다. 결과는 다음과 같습니다.
+-------------+------------------+---------+
| This answer | Dominic's answer | wc -l |
+------------+-------------+------------------+---------+
| Lines | 3550388 | 3550389 | 3550388 |
+------------+-------------+------------------+---------+
| Runtime | 1.055 | 4.297 | 0.587 |
+------------+-------------+------------------+---------+
시간은 실시간으로 초 단위로 측정됩니다. 여기에서 실제 의미를 확인 하세요.
단순 지향 객체 솔루션
$file = new \SplFileObject('file.extension');
while($file->valid()) $file->fgets();
var_dump($file->key());
최신 정보
이것을 만드는 또 다른 방법은 PHP_INT_MAX
in SplFileObject::seek
method입니다.
$file = new \SplFileObject('file.extension', 'r');
$file->seek(PHP_INT_MAX);
echo $file->key() + 1;
Linux / Unix 호스트에서 exec()
이를 실행하는 경우 가장 쉬운 해결책은 명령 을 사용 하거나 이와 유사한 명령을 실행하는 것 wc -l $path
입니다. $path
먼저 "/ path / to / file; rm -rf /"와 같은 것이 아닌지 확인하기 위해 먼저 삭제 했는지 확인하십시오.
전체 파일을 반복 할 필요가없는 더 빠른 방법이 있습니다.
* nix 시스템에서만 Windows에서도 비슷한 방법이있을 수 있습니다.
$file = '/path/to/your.file';
//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));
PHP 5.5를 사용하는 경우 생성기를 사용할 수 있습니다 . 이것은 5.5 이전의 PHP 버전에서는 작동 하지 않습니다 . php.net에서 :
"Generator는 Iterator 인터페이스를 구현하는 클래스를 구현하는 오버 헤드 나 복잡성없이 간단한 반복기를 구현하는 쉬운 방법을 제공합니다."
// This function implements a generator to load individual lines of a large file
function getLines($file) {
$f = fopen($file, 'r');
// read each line of the file without loading the whole file to memory
while ($line = fgets($f)) {
yield $line;
}
}
// Since generators implement simple iterators, I can quickly count the number
// of lines using the iterator_count() function.
$file = '/path/to/file.txt';
$lineCount = iterator_count(getLines($file)); // the number of lines in the file
이것은 Wallace de Souza의 솔루션에 추가되었습니다.
또한 계산하는 동안 빈 줄을 건너 뜁니다.
function getLines($file)
{
$file = new \SplFileObject($file, 'r');
$file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY |
SplFileObject::DROP_NEW_LINE);
$file->seek(PHP_INT_MAX);
return $file->key() + 1;
}
Linux를 사용하는 경우 간단히 다음을 수행 할 수 있습니다.
number_of_lines = intval(trim(shell_exec("wc -l ".$file_name." | awk '{print $1}'")));
다른 OS를 사용하는 경우 올바른 명령을 찾아야합니다.
문안 인사
private static function lineCount($file) {
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
if (fgets($handle) !== false) {
$linecount++;
}
}
fclose($handle);
return $linecount;
}
위의 기능에 약간의 수정을 추가하고 싶었습니다.
'테스트'라는 단어가 포함 된 파일이있는 특정 예에서 함수가 결과로 2를 반환했습니다. 그래서 fgets가 false를 반환했는지 여부를 확인해야했습니다. :)
재미있다 :)
Counting the number of lines can be done by following codes:
<?php
$fp= fopen("myfile.txt", "r");
$count=0;
while($line = fgetss($fp)) // fgetss() is used to get a line from a file ignoring html tags
$count++;
echo "Total number of lines are ".$count;
fclose($fp);
?>
You have several options. The first is to increase the availble memory allowed, which is probably not the best way to do things given that you state the file can get very large. The other way is to use fgets to read the file line by line and increment a counter, which should not cause any memory issues at all as only the current line is in memory at any one time.
There is another answer that I thought might be a good addition to this list.
If you have perl
installed and are able to run things from the shell in PHP:
$lines = exec('perl -pe \'s/\r\n|\n|\r/\n/g\' ' . escapeshellarg('largetextfile.txt') . ' | wc -l');
This should handle most line breaks whether from Unix or Windows created files.
TWO downsides (at least):
1) It is not a great idea to have your script so dependent upon the system its running on ( it may not be safe to assume Perl and wc are available )
2) Just a small mistake in escaping and you have handed over access to a shell on your machine.
As with most things I know (or think I know) about coding, I got this info from somewhere else:
public function quickAndDirtyLineCounter()
{
echo "<table>";
$folders = ['C:\wamp\www\qa\abcfolder\',
];
foreach ($folders as $folder) {
$files = scandir($folder);
foreach ($files as $file) {
if($file == '.' || $file == '..' || !file_exists($folder.'\\'.$file)){
continue;
}
$handle = fopen($folder.'/'.$file, "r");
$linecount = 0;
while(!feof($handle)){
if(is_bool($handle)){break;}
$line = fgets($handle);
$linecount++;
}
fclose($handle);
echo "<tr><td>" . $folder . "</td><td>" . $file . "</td><td>" . $linecount . "</td></tr>";
}
}
echo "</table>";
}
Based on dominic Rodger's solution, here is what I use (it uses wc if available, otherwise fallbacks to dominic Rodger's solution).
class FileTool
{
public static function getNbLines($file)
{
$linecount = 0;
$m = exec('which wc');
if ('' !== $m) {
$cmd = 'wc -l < "' . str_replace('"', '\\"', $file) . '"';
$n = exec($cmd);
return (int)$n + 1;
}
$handle = fopen($file, "r");
while (!feof($handle)) {
$line = fgets($handle);
$linecount++;
}
fclose($handle);
return $linecount;
}
}
https://github.com/lingtalfi/Bat/blob/master/FileTool.php
The most succinct cross-platform solution that only buffers one line at a time.
$file = new \SplFileObject(__FILE__);
$file->setFlags($file::READ_AHEAD);
$lines = iterator_count($file);
Unfortunately, we have to set the READ_AHEAD
flag otherwise iterator_count
blocks indefinitely. Otherwise, this would be a one-liner.
For just counting the lines use:
$handle = fopen("file","r");
static $b = 0;
while($a = fgets($handle)) {
$b++;
}
echo $b;
I use this method for purely counting how many lines in a file. What is the downside of doing this verses the other answers. I'm seeing many lines as opposed to my two line solution. I'm guessing there's a reason nobody does this.
$lines = count(file('your.file'));
echo $lines;
'developer tip' 카테고리의 다른 글
날짜를 확인하는 PHP Regex는 YYYY-MM-DD 형식입니다. (0) | 2020.10.06 |
---|---|
오류 : zip 파일을 열지 못했습니다. (0) | 2020.10.06 |
reloadItemsAtIndexPaths 후 UICollectionView 애니메이션 방지 (0) | 2020.10.06 |
라이브 데이터베이스를 조심하는 # 1 방법은 무엇입니까? (0) | 2020.10.06 |
C # 정수 산술에서 a / b / c는 항상 a / (b * c)와 같습니까? (0) | 2020.10.05 |