ついに夏本番!さぁ、家族でキャンプに行くぞ! >>

初心者です。
PHP5を始めて約50時間程度です。
テキストファイルの処理についての質問です。
fgets,file_put_contents,str_replace,array等の関数を使い処理するのだと思います。
構文が組み立てられません。
参考になるサイト等アドバイスいただけると幸いです。
よろしくお願いします。

下記の様な元になるテキストファイルがあります。
1.最初の文字があるまで(=改行だけの行)は削除
2.最初の文字列の最後に”,”を入れ改行をとる
3.次の行と次の次の行は削除
4.次の行は、例:7月4日(土)を7,4,土に
5.次の行は、例:10:00~13:00を10:00,13:00に
6.次の行と次の次の行は削除して改行
7.次の行と次の次の行は削除
8.前述1から7までの繰り返し
*最終的には後述のようなテキストファイルになります

----元になるテキストファイル



植物の光合成
理科

相田ももこ
7月4日(土)
10:00~13:00
30名
5,000円


鎌倉時代
社会

土田正
7月4日(土)
14:00~17:00
30名
5,000円


生物の進化
理科

長谷川浩
7月4日(土)
10:00~17:00*会場は大阪です
16名
10,000円

----元になるテキストファイル以上


---完成後のファイル
植物の光合成, 理科,7,4,土,10:00,13:00
鎌倉時代,社会,7,4,土,14:00,17:00
生物の進化,理科,7,4,土,10:00,17:00
---完成後のファイル以上

このQ&Aに関連する最新のQ&A

A 回答 (4件)

えっと…、ごめんなさい。


改めて試したところ、#2のコードにミスがあることがわかりました。

× $array = split("[\n{2,}]",$file);

\n{2,} としなければ空行にマッチできませんし、何よりShift_JISやUTF-8だと改行は \r\n でした。
#2では \nで区切られてしまうため、CSVになるときの行単位ではなく、セル単位で配列化してしまっています。
このため、以降のコードが全て正常に動かないのです…。
余計な回り道をさせてしまって申し訳ないです。

以下にサンプルコードを作りましたので、参考にしてください。(今度は検証したので動くはずです…)

<?php
$file = file_get_contents('test01.txt');
$file = mb_convert_encoding($file, "UTF-8", "auto");// preg用にUTF-8に文字コード変換(preg系はUTF-8にすると全角文字を扱える正規表現として機能します)

/* 行単位の配列に (1次元配列) */
$array = preg_split("/(?:\r\n){2,}/m", $file);// 空行区切りで分割

/* セル単位の配列に (2次元配列) */
foreach($array as &$value){
$value = preg_split("/\r\n/", $value);// CSVのセルデータに分割 (配列の値を更に配列に。ここで2次元配列になります)
}

/* CSV形式に変換 */
foreach($array as &$value){
$value = preg_replace('/"/', '""', $value);// ダブルクオートをエスケープ
$value = preg_replace('/^.*$/', '"$0"', $value);// セルデータ全体をダブルクオートで括る
$value = implode(',', $value);// 2次元配列→1次元配列
}

$output = implode("\r\n", $array);// 1次元配列→文字列
$output = mb_convert_encoding($output, "Shift_JIS", "auto");// Shift_JIS文字コードに

header('Content-Type: text/plain; charset=Shift_JIS'); // Content-Typeヘッダ出力
echo $output;// 出力
?>

> *”,”だけの行が削除できない。
"," は区切り文字なので、値に持ちたい場合はエスケープする(安全な文字に変換する)必要がありますね。
この場合CSVを読み込むプログラムによってエスケープ方法が変わります。
念のため確認しますが、完全なCSV形式でいいのですか?
つまり、読み込むプログラムはExcelと思ってよいですか?
上のコードはExcelで読む前提で書いています。
PHPで読むなどしていて "," のエスケープ方法が特殊だとそれに準拠した形にする必要があります。
ちなみに、掲示板で使われるCSVでは改行は <br> に、"," はHTML文字実体参照にすることが多いですね。

CSVファイルフォーマットの解説:CodeZine
http://codezine.jp/article/detail/2364

> *”円”があったら新しいテキストファイル(test02.txt)上で改行
> (質問の7.次の行と次の次の行は削除)
配列にしてしまえば後は簡単です。

- 配列の値をpreg_replaceしてNULL(空データ)にする
- preg_matchでチェックしてから unset()

のいずれかで対応してください。

あと、サンプルでは省略した「セルの順番変更」ですが、2次元配列にする以前にやった方がわかりやすいと思います。
echo preg_replace('/^(\d{4})(\d{2})(\d{2})$/', '$1/$2/$3', "20090520");// 出力

こんな感じで括弧を使って必要な部分をキャプチャし、変数で並び替えてみてください。
    • good
    • 0

#3の補足です。



「"," をエスケープする必要がある」と書きましたが、#3では「全データをダブルクオートで括る」形式にしています。
ダブルクオートで括ることで、「"," をエスケープする」のではなく「'"' をエスケープする」に変化します。
Excelで出力するCSVは「改行を含む」または「カンマを含む」データ限定でダブルクオートで括っていますが、
全データをダブルクオートで括ってもExcelで読み込むことが出来ます。(それでもCSVフォーマットに準拠しているので)

この回答への補足

出来ました。
ありがとうございました。

2行目と21行目に下記メッセージが出ました。
Warning: mb_convert_encoding() [function.mb-convert-encoding]: Unable to detect character encoding

$file = mb_convert_encoding($file, "UTF-8", "auto");→
$file = mb_convert_encoding($file, "UTF-8", "Shift_JIS");

header('Content-Type: text/plain; charset=Shift_JIS'); →
//header('Content-Type: text/plain; charset=Shift_JIS');
で解決しました。

教えていただいたプログラムは、一行ずつ意味を解釈し勉強してみます。
私はVB6で育った世代で、配列の概念が極めて希薄だと教えていただいた中で感じています。
*VBだと1行ずつ読んでその中で処理しworkファイルに落とす、ような感じになると思います。

重ね重ねありがとうございました。
深謝しています。

最後に完成したソースを貼っておきます。
<?php
$file = file_get_contents('test01.txt');
$file = mb_convert_encoding($file, "UTF-8", "Shift_JIS"); // preg用にUTF-8に文字コード変換(preg系はUTF-8にすると全角文字を扱える正規表現として機能します)

/* 行単位の配列に (1次元配列) */
$array = preg_split("/(?:\r\n){2,}/m", $file); // 空行区切りで分割

/* セル単位の配列に (2次元配列) */
foreach($array as &$value){
$value = preg_split("/\r\n/", $value); // CSVのセルデータに分割 (配列の値を更に配列に。ここで2次元配列になります)
}

/* CSV形式に変換 */
foreach($array as &$value){
$value = preg_replace('/"/', '""', $value); // ダブルクオートをエスケープ
$value = preg_replace('/^.*$/', '"$0"', $value); // セルデータ全体をダブルクオートで括る
$value = implode(',', $value); // 2次元配列→1次元配列
}

$output = implode("\r\n", $array); // 1次元配列→文字列
$output = mb_convert_encoding($output, "Shift_JIS", "auto"); // Shift_JIS文字コードに

//header('Content-Type: text/plain; charset=Shift_JIS'); // Content-Typeヘッダ出力
echo $output; // 出力

file_put_contents("test02.txt","$output");
?>
---完成したソース

<?php
$file = file_get_contents('test01.txt');
$file = mb_convert_encoding($file, "UTF-8", "auto"); // preg用にUTF-8に文字コード変換(preg系はUTF-8にすると全角文字を扱える正規表現として機能します)

/* 行単位の配列に (1次元配列) */
$array = preg_split("/(?:\r\n){2,}/m", $file); // 空行区切りで分割

/* セル単位の配列に (2次元配列) */
foreach($array as &$value){
$value = preg_split("/\r\n/", $value); // CSVのセルデータに分割 (配列の値を更に配列に。ここで2次元配列になります)
}

/* CSV形式に変換 */
foreach($array as &$value){
$value = preg_replace('/"/', '""', $value); // ダブルクオートをエスケープ
$value = preg_replace('/^.*$/', '"$0"', $value); // セルデータ全体をダブルクオートで括る
$value = implode(',', $value); // 2次元配列→1次元配列
}

$output = implode("\r\n", $array); // 1次元配列→文字列
$output = mb_convert_encoding($output, "Shift_JIS", "auto"); // Shift_JIS文字コードに

header('Content-Type: text/plain; charset=Shift_JIS'); // Content-Typeヘッダ出力
echo $output; // 出力
?>

補足日時:2009/05/22 07:45
    • good
    • 0
この回答へのお礼

何から何まで、本当にすいません。
感謝にたえません。
今度こそ出来るよう頑張ります。
取り急ぎ御礼まで。

お礼日時:2009/05/21 18:07

初心者であることを卑下することはないと思いますよ。


わからないなりに工夫して試した跡が見られるので、私は逆に向上心のある初心者さんだと思っています。

> $array = preg_replace("/^(\s)*(\r|\n|\r\n)/m", ",", $array);;
恥ずかしながら preg_replace が配列を引数に持てるということを知りませんでした。これならforeachが不要になります。
すみません、余計に混乱させてしまいましたね。
私の環境では下記コードでほぼ期待通りの結果になりました。

<?php
$file = file_get_contents('test01.txt');
$array = split("[\n{2,}]",$file);
$array = preg_replace("/(?:\r\n|[\r\n])/", ",", $array);

header('Content-Type: text/plain; charset=UTF-8');// Content-Typeヘッダ出力
print_r($array);
?>

# header() は「テキストとして扱うため + 文字コード指定で文字化けを防ぐため」です。
# 省略できるなら省略しちゃっても構いません。

この回答への補足

下記ソースを書きました。
---下記ソース
<?php
$file = file_get_contents('test01.txt');
$array = split("[\n{2,}]",$file);
$array = preg_replace("/(?:\r\n|[\r\n])/", ",", $array);
//header('Content-Type: text/plain; charset=sjis'); // Content-Typeヘッダ出力
$text_work = implode("\r\n", $array);
//echo $text_work;
file_put_contents("test02.txt","$text_work");
?>
---下記ソース以上
最終行の"test02.txt"には、最初の'test01.txt'と同ほぼじものが出力されてしまいます。
(10,000円が10改行000円改行が違うだけ)

*”各配列の値に対してpreg_replace を使って1行のCSVに変換し、配列の値を上書きします。”
→これは、出来ているのだと思います。
*”,”だけの行が削除できない。
*”円”があったら新しいテキストファイル(test02.txt)上で改行
(質問の7.次の行と次の次の行は削除)

何かアドバイスいただけると幸いです。
度々すいません。

補足日時:2009/05/20 18:50
    • good
    • 0
この回答へのお礼

>私は逆に向上心のある初心者さんだと思っています。
教えていただいた上に、励ましの言葉まで頂き、ありがとうございます。
深謝しています。

お礼日時:2009/05/20 18:09

「テキスト→CSV」の変換を行いたい、ということですね。



> fgets,file_put_contents,str_replace,array等の関数を使い処理するのだと思います。
必要な関数はわかっているようですので、ポイントだけ説明します。
未検証につき細かなミスがあるかもしれませんが、考え方(アルゴリズム)だけ受け取ってください。

1. テキストファイル取得
file_get_contents で受け取り、変数に格納します。
この際、trimで前後の空行を取り除くと良いと思います。

2. 空行で区切り、配列に分割格納する
CSV変換後の1行は空行区切りのブロックと捉えて良さそうです。
split で空行(\n{2,})を区切り文字に指定して、各ブロックを配列に分割格納します。

3. preg_replace でCSV形式に変換する
2. で作成した配列を foreach で処理をループさせます。
各配列の値に対してpreg_replace を使って1行のCSVに変換し、配列の値を上書きします。

4. implodeで配列から文字列に戻す
implode で区切り文字に改行(\n)を指定して、文字列に戻します。

5. file_put_contents で書き込み
完成です。

PHP: file_get_contents - Manual
http://jp.php.net/manual/ja/function.file-get-co …
PHP: file_put_contents - Manual
http://jp.php.net/manual/ja/function.file-put-co …
PHP: trim - Manual
http://jp.php.net/manual/ja/function.trim.php
PHP: split - Manual
http://jp.php.net/manual/ja/function.split.php
PHP: foreach - Manual
http://jp.php.net/manual/ja/control-structures.f …
PHP: preg_replace - Manual
http://jp.php.net/manual/ja/function.preg-replac …
PHP: implode - Manual
http://jp.php.net/manual/ja/function.implode.php

# file_get_contentsを知っていたのに、file_put_contentsを知らなかった…。
# PHP5からの関数だから見逃していたのかな。

この回答への補足

初心者ですいません。

>3. preg_replace でCSV形式に変換する
>2. で作成した配列を foreach で処理をループさせます。
>各配列の値に対してpreg_replace を使って1行のCSVに変換し、配列の値を上書きします。
>
>4. implodeで配列から文字列に戻す
>implode で区切り文字に改行(\n)を指定して、文字列に戻します。
>
>5. file_put_contents で書き込み
>完成です。

上記3の部分でうまくいきません。
「preg_replace を使って1行のCSVに変換し」が組み立てられないのだと思います。
3,4,5の参考例文のようなもの、アドバイス、いただけると幸いです。

---私のソース
<?php
$file = file_get_contents(('test01.txt'));
//$file = preg_replace("/^(\s)*(\r|\n|\r\n)/m", "", $file);
$array = split("[\n{2,}]",$file);

foreach ($array as $key => $value) {
$array = preg_replace("/^(\s)*(\r|\n|\r\n)/m", ",", $array);;
}
print_r($array);
?>
---私のソース以上
---print_rの表示の一部
Array ( [0] => , [1] => , [2] => , [3] => , [4] => 植物の光合成 [5] => 理科 [6] => 3 [7] => 相田ももこ [8] => 7月4日(土) [9] => 10:00~13:00 [10] => 30名
---print_rの表示の一部以上

test01.txtは、最初に投稿したものと同じです。

補足日時:2009/05/20 10:15
    • good
    • 0
この回答へのお礼

早速参考マニュアルサイトまで書いていただきありがとうございます。
私のスキルからすると、かなり高度な事をやっているようです。
大変感謝しています。取り急ぎ御礼まで。

お礼日時:2009/05/20 10:14

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


人気Q&Aランキング