お疲れ様です。初投稿になります。
どうぞ宜しくお願い致します。
CVSデータ(囲い文字:"(ダブルコーテーション))を正規表現でマッチングさせ、抜き出そうと考えています。
エスケープ文字を以下のように設定しています。
・""(ダブルコーテーション * 2) ⇒ "(ダブルコーテーション)
・\"(円記号 + ダブルコーテーション) ⇒ "(ダブルコーテーション)
・\\(円記号 * 2) ⇒ \(円記号)
例えば・・・
"A",""",BB,"",\",CC,\\,DD""","EEEE","",
ですと、「"A"」「""",BB,"",\",CC,\\,DD"""」「"EEEE"」「""」と取れる想定になります。
特に2番目の「""",BB,"",\",CC,\\,DD"""」は、
「"""(←escape),BB,""(←escape),\"(←escape),CC,\\(←escape),DD""(←escape)"」
という想定です。
PHP関数ではエスケープ文字の問題により取得しきれず、自力での取得を行っております。
私が考えた正規表現は「"[^"]*((""|\\")[^"]*)*[^\\]",|"",」となりますが、カンマが入り乱れる上記パターンを満たすことができません。
これは、正規表現での解決は不可能でしょうか?
何日も解決できず、困っています・・・。
是非、皆様のお力添えのほど、宜しくお願い致します。
PS.
このサイトで動作確認を行っておりました。
参考になれば幸いです。
http://www.rider-n.sakura.ne.jp/regexp/regexp.php
No.2ベストアンサー
- 回答日時:
> 正規表現で
(?:"((?:""|\\\"|[^"])*)"|([^,]*))(?:,|$)(?:"((?:""|\\\\"|[^"])*)"|((?:[^,]|\\\\,)*))(?:,|$)
# 全て検証したわけじゃないので抜けがあるかもしれない。
ちなみに、PHP には fgetcsv あるいは str_getcsv(PHP 5.3+) が用意されている。ただし、これら関数は、解析エラーを吐き出さないため、自前で解析というのも懸命な判断かもしれない。
ご回答ありがとうございます。
ご提示いただきました正規表現を試してみましたところ、一部取得できない箇所がありました。
しかし、私自身正規表現に対しての知識が乏しいためにまだ理解できていない部分もあり、今回ご提示いただきました正規表現は大変価値あるものだと思っております。
内容の理解に励み、次に活かしたいと思います。
私の意図を汲んでいただいた上でのご回答でしたので、こちらをベストアンサーとさせていただきます。
本当にありがとうございました。
No.5
- 回答日時:
yuu_xさんに指摘されたので、追記。
fgetcsv関数に関することはすでに、No.1と、No.2で回答がされてますので、触れませんでした。
当然、fgetcsvが使用可能であれば、それを使って処理した方がいいです。
(fgetcsvを使って文字化けがする等、環境やPHPのバージョンによって問題が現れる可能性がありますが。)
で、それを使用したくない何らかの理由がある場合に、ユーザ関数等つくってやってみると思いますが、
そこに複雑な一つの正規表現で表現するのであれば、PHPスクリプトで書いたほうがやりやすいのではないか、
というのが回答の意図です。
No.4
- 回答日時:
> 3
いあ、それ使うくらいなら、既存の関数のほうが断然いい。(早い遅いの問題でなく)
既存の関数は、csv の正確さまでは検証しないといいたかっただけ。
加えて、この点は質問者も同じ事をしているが、エスケープなるものを導入したせいで扱いにくくもある(しかし、エスケープ文字が省略できないのには納得がいかないよな。phpMyAdmin も同じ事をしてくれているため結局手書きで流し込むことになる)。RFC4180 にはそんなものは存在しない。
No.3
- 回答日時:
正規表現を使わなくても自前でパースするのはそこまで大変ではないと思いますよ。
<?php
$fp = fopen('test.txt', 'r');
while($row = my_fgetcsv($fp)){
var_dump($row);
}
fclose($fp);
function my_fgetcsv($fp, $size=4096){
$result = "";
$cnt = 0;
while($row = fgets($fp, $size)){
$result .= $row;
$buff = str_replace('\\"', '', $row);
$buff = str_replace('""', '', $row);
$cnt += substr_count($buff, '"');
if($cnt % 2 == 0){
break;
}
}
return my_csv_explode($result);
}
function my_csv_explode($line){
$length = strlen($line);
$in_block = false;
$before_escape = 0;
$data = "";
for($i=0; $i<$length; $i++){
$check = $line[$i];
if($in_block === false && $check == '"'){
$in_block = true;
continue;
}
if($before_escape === 0){
if($check == '"'){
$before_escape = 1;
continue;
}elseif($check == '\\'){
$before_escape = 2;
continue;
}
}
if($before_escape === 1 || $before_escape === 2){
if($check == '"'){
$data .= '"';
}elseif($before_escape === 2){
if($check == '\\'){
$data .= $check;
}else{
$data .= '\\' . $check;
}
}elseif($before_escape === 1 && $check === ','){
$results[] = $data;
$data = "";
$in_block = false;
$before_escape = 0;
continue;
}else{
$data .= $check;
}
$before_escape = 0;
continue;
}
if($in_block === true && $check == '"'){
if($line[++$i] === ','){
$results[] = $data;
$data = "";
$in_block = false;
continue;
}else{
trigger_error('csv syntax error. line data:'.$line, E_USER_ERROR);
}
}
if($in_block === false && $check == ','){
$results[] = $data;
$data = "";
continue;
}
if($in_block === false && $check == "\n"){
$results[] = $data;
$data = "";
continue;
}
if($in_block === false && $check == "\r"){
$results[] = $data;
$data = "";
if($line[++$i] != "\n"){
$i--;
}
continue;
}
$data .= $check;
}
if(!empty($data)){
$results[] = $data;
}
return $results;
}
?>
速度は保証しませんが。
ご回答ありがとうございます。
このmy_fgetcsv関数はhogehoge78さんがコーディングされたのでしょうか。
そうであれば感謝の限りです。ありがとうございます。
今回のCSVデータは、お客様が入力されることもあるデータでしたので、入力ミスを出来る限り検知したいという背景がありました。
自力でのパース処理も考えたのは考えたのですが、テスト工数が大きくなる、バグを内包する可能性が高くなるという判断で、出来れば既存の関数を利用しようと考えておりました。
調べたところfgetcsv関数もC言語でパース処理を行っていましたので、方法としてはアリだったのかなと今となっては思っております。
ご提示いただきましたパース処理の関数は、自分で纏めているナレッジに含めさせていただこうと思います。
本当にありがとうございました。
No.1
- 回答日時:
仰っている意味がよくわからないのですが…。
PHPの標準関数(fgetcsv)で以下のような出力が得られましたが、それとは違うのでしょうか。
<?php
$fp = fopen("log.csv", "r");
while($array = fgetcsv($fp)){
var_dump($array);
}
?>
array(5) {
[0]=>
string(1) "A"
[1]=>
string(19) "",BB,",\",CC,\\,DD""
[2]=>
string(4) "EEEE"
[3]=>
string(0) ""
[4]=>
string(0) ""
}
ご回答ありがとうございます。
fgetcsvを使用しなかった理由としまして
・文字化けが発生し得る
・囲い文字が欠けている場合によるエラーが発生しない
・行末の囲い文字が存在しないと次行を含んで読み込んでしまう
などがあるかと思っておりました。(実際にやってみたところいくつか発生しました)
なので今回は自前でのチェックにて解析エラー出力を行うため、fgetcsv_regというネット上にあるユーザ定義関数で対応・応用しようとした背景があります。
(fgetcsv_regは正規表現にてCSVデータを取得しています)
私の説明が不足しており、申し訳ございません。お詫び申し上げます。
今のところ、正規表現での取得が厳しそうなので、str_getcsvの利用でどうにかならないか思案中です。
何か分かれば補足いたしますね。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・チョコミントアイス
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・あなたの習慣について教えてください!!
- ・ハマっている「お菓子」を教えて!
- ・高校三年生の合唱祭で何を歌いましたか?
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・家の中でのこだわりスペースはどこですか?
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
別ファイルの構造体の値を読み...
-
Resource id #3 と表示されま...
-
phpメールフォームから送信され...
-
PHPでサーバー上の書き換えたht...
-
PHP:2つの置換文字列を一気に...
-
$_SESSIONに二次元配列を使える...
-
pdfファイルの複数添付 引数の型
-
配列の要素(value)に、変数を...
-
リストボックス複数選択抽出方法
-
再帰関数を用いて配列の合計を...
-
VB.NET で 二次元のハッシュは...
-
array_combineがうまくいかない...
-
C言語の配列をPush(追加)する...
-
emptyとcount
-
Perlで重複行を削除したい
-
php 省略可能な引数配列を持つ...
-
【PHP】 重複した値を取り除く...
-
OCI で、SELECT結果行数を取得...
-
ファイルの逆読みってできますか?
-
多次元配列の重複削除
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
別ファイルの構造体の値を読み...
-
Resource id #3 と表示されま...
-
CSVファイルの最終行のデー...
-
複数行のデータのPOST処理に関して
-
ヒアドキュメントの中のfor文
-
バイナリファイルの内容を、そ...
-
FortranのOPEN文
-
CSVデータの行数カウントをした...
-
C言語でCSVファイルの行数を読...
-
stdClass Objectを連想配列のよ...
-
エラーメッセージ(無効な間接...
-
csvの内容を行単位で削除したい
-
【PHP】csvファイルへの書き出...
-
UTF-8のXMLがSJISのPHPで文字化け
-
PHP5でCSVの指定行データだけを...
-
PHPで、CSVファイルを、指定し...
-
doxygenを使用する環境について
-
PHPでCSVの一部の行を編集したい
-
PHP 別ドメインへのファイル保存法
-
curlをPHPで書く方法
おすすめ情報