重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

【GOLF me!】初月無料お試し

PHP初心者です。

CSVデータを活用してサイトを作ろうとしているのですが、
フリーワードで検索すると下記のようなエラーが出てしまいます。

Fatal error: Allowed memory size of 52428800 bytes exhausted
(tried to allocate 77287300 bytes) in

レンタルサーバー(ロリポップ)に
memory_limitについて問い合わせたところ、
消費メモリを削減して対処してほしいと言われました。

10件だけ表示するようにすれば解決するかと思い、
自分なりにあれこれいじってみたのですが、
うまくいきませんでした。

10件だけ表示する方法やメモリ消費を抑える方法を
お分かりになる方がいらっしゃいましたら、
ぜひご教授お願いいたします。

-index.php-トップページ
<form action="search.php" method="get"><input type="text" name="key" size="90">
<input type="submit" name="submit" value="検索"></form>

-seach.php-検索結果表示ページ
<?php
if($_GET["key"]==""){
print"キーワードを入力してください";
}else{
$KeyWord=$_GET["key"];
$KeyWord=htmlspecialchars($KeyWord);
$KeyWord=mb_convert_encoding($KeyWord,"Shift_JIS");
$KeyWord=mb_convert_kana($KeyWord,s);
$ArrKeyword=explode(" ",$KeyWord);

$Result=array();
$Data=file("item.csv");
for($i=0;$i<sizeof($Result);$i++){
$lines=strip_tags($Data[$i]);
$Match=true;
for($n=0;$n<sizeof($ArrKeyword);$n++){
if(!eregi($ArrKeyword[$n],$lines)){
$Match=false;
break;
}
}
if($Match==true){
array_push($Result,$Data[$i]);
}
}
?>

<?php
$n=sizeof($Result);
if($n==0){
print"見つかりませんでした";
}else{
print"{$n}件見つかりました";
?>
<ul>
<?php
for($i=0;$i<sizeof($Result);$i++){
$line=explode(",",$Result[$i]);
?>
<li><a href="item.php?id=<?=$line[0]?>"><?=$line[3]?></a></li>
<?php
}
}
}
?>


下記の行がエラーのようです。
for($i=0;$i<sizeof($Result);$i++){

A 回答 (6件)

fopen関数に変更したものを書いてみました。


<?php
$ArrKeywordLength = sizeof($ArrKeyword);

$fp = fopen('item.csv', "r");
$Result=array();

while(!feof($fp)){
$ret = fgets($fp, 4096);
$lines = strip_tags($ret);
$Match=true;
for($n=0; $n<$ArrKeywordLength;$n++){
if(!eregi($ArrKeyword[$n], $lines)){
$Match = false;
break;
}
}
if($Match === true){
$Result[] = $ret;
}
}
?>

処理速度はだいたい一緒ですが、メモリの最大使用量が控えめです。
file関数は、テキストの内容を改行区切りで一気に配列に代入するという動作を行っているため、
その時点で一気にメモリ使用量が増えてしまいます。
fopen関数でファイルを開いて一行ずつ読み出し、という方法を行えば、
一気にデータを取得しないし配列に展開しないため、メモリ消費量が抑えられます。
また、for文中に「sizeof(又はcount)関数」で配列の長さを取得すると
1回ループごとに再計算を行いますので、処理が遅くなります(うろ覚えですが)
出来れば、for文の外側で配列の長さを取得してから変数で与えてやったほうがよりよいです。
array_push関数に関しては、
$var[] = $str;
この記述方法でまったく同じ動作をし、なおかつ若干動作が速いです。
eregi関数に関しては、要件が分からなかったのでそのままにしましたが、
もし、単純に文字列が含まれているかを確認するのであれば、前の回答者さんが仰っているようにstrposを利用したほうが早いです。
また、eregi関数は、正規表現を利用することが出来ます。もし検索ワードに正規表現が含まれている場合は、正規表現でマッチングを行ってしまいますが、それでは問題があるのであれば、前述したようにstrposをご利用したほうが良いです。
<?php
if(strpos($lines, $ArrKeyword[$n]) === false){ //もし適合しなかったらという条件

}

?>
    • good
    • 0
この回答へのお礼

わざわざプログラムを作って書いていただきありがとうございます。

実行してみたところ、一行目の
$ArrKeywordLength = sizeof($ArrKeyword);
の部分でエラーが出たのですが、一行目を削除したら、
おっしゃる通りメモリの使用が少なくなり、
無事表示することができました。

本当にありがとうございます。

一行目は削除しても問題なかったのでしょうか?

お礼日時:2009/12/25 15:47

記述したのは該当ロジック部分のみになりますので、


$KeyWordの内容を$ArrKeyWordに保存しなければnullのため、エラーになってしまいます。
一度、質問者さんが記述したプログラムに当て込んで見てください。


また、別の回答者さんが仰っているようにデータベースに移行することも検討してみてください。
最初はSQLiteのようなファイルベースの簡易データベースから触れると、労力も少なくて良いと思います。
    • good
    • 0
この回答へのお礼

そうなんですか…。

今確認したところ、apache上ではできたのですが、
ロリポップにアップロードしたらメモリオーバーしました(汗)

SQLiteも調べてみます。

本当にどうもありがとうございました。

お礼日時:2009/12/25 20:53

データが数万あるならば、データベースの使用を検討したほうがいいかもしれません。

この回答への補足

アドバイスありがとうございます。

本当はそうした方がいいのかもしれませんね。

ただ、データベースの使用となると、
また覚えることが増えるので躊躇してしまいます…。

補足日時:2009/12/25 15:42
    • good
    • 0

fopen関数に変更したものを書いてみました。


<?php
$ArrKeywordLength = sizeof($ArrKeyword);

$fp = fopen('item.csv', "r");
$Result=array();

while(!feof($fp)){
$ret = fgets($fp, 4096);
$lines = strip_tags($ret);
$Match=true;
for($n=0; $n<$ArrKeywordLength;$n++){
if(!eregi($ArrKeyword[$n], $lines)){
$Match = false;
break;
}
}
if($Match === true){
$Result[] = $ret;
}
}
?>

処理速度はだいたい一緒ですが、メモリの最大使用量が控えめです。
file関数は、テキストの内容を改行区切りで一気に配列に代入するという動作を行っているため、
その時点で一気にメモリ使用量が増えてしまいます。
fopen関数でファイルを開いて一行ずつ読み出し、という方法を行えば、
一気にデータを取得しないし配列に展開しないため、メモリ消費量が抑えられます。
また、for文中に「sizeof(又はcount)関数」で配列の長さを取得すると
1回ループごとに再計算を行いますので、処理が遅くなります(うろ覚えですが)
出来れば、for文の外側で配列の長さを取得してから変数で与えてやったほうがよりよいです。
array_push関数に関しては、
$var[] = $str;
この記述方法でまったく同じ動作をし、なおかつ若干動作が速いです。
eregi関数に関しては、要件が分からなかったのでそのままにしましたが、
もし、単純に文字列が含まれているかを確認するのであれば、前の回答者さんが仰っているようにstrposを利用したほうが早いです。
また、eregi関数は、正規表現を利用することが出来ます。もし検索ワードに正規表現が含まれている場合は、正規表現でマッチングを行ってしまいますが、それでは問題があるのであれば、前述したようにstrposをご利用したほうが良いです。
<?php
if(strpos($lines, $ArrKeyword[$n]) === false){ //もし適合しなかったらという条件

}

?>
    • good
    • 0

eregiよりはstrposを使用してみてはどうでしょう。


$Result=array();
$Data=file("item.csv");
foreach ($Data as $lines) {
$Match=true;
foreach($ArrKeyword as $key) {
if(!strpos($lines,$key)){
$Match = false;
break;
}
}
if($Match==true){
array_push($Result,$lines);
}
}
    • good
    • 0
この回答へのお礼

アドバイスありがとうございます。

やってみたところ、やはり同じエラーが出ました。
行が数万あるのでcsvファイルが大きすぎなのでしょうか…。

さらにいろいろいじってるうちに
なぜか結果表示までできなくなってしまいました(涙)

お礼日時:2009/12/22 19:48

file()なんかで処理せずにきちんとfopenしてfgetcsv()などで


処理してみてはどうでしょうか?

http://www.php.net/manual/ja/function.fgetcsv.php
    • good
    • 0
この回答へのお礼

解答ありがとうございます。

お恥ずかしい話ですが、
ネットでいろいろ検索していて
fgetcsvというものがあるのはわかったのですが、
初心者なので具体的にどうプログラムに
書けばいいのかわからないので諦めていました。

なので、下記のサイトを参考にして
プログラムを作成しようと思っているんです。
http://affiliate.aki-f.com/prog/page/35.html

重ねて質問をしてとても申し訳ないのですが、
どのように記述すればよろしいのでしょうか?

お礼日時:2009/12/21 20:48

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!