
お疲れ様です。初投稿になります。
どうぞ宜しくお願い致します。
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で質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) マクロを教えてください。 7 2023/06/01 19:47
- Visual Basic(VBA) vba 等間隔の列に対しての計算 6 2022/05/17 20:15
- オープンソース csvデータのダブルクォーテーションで囲まれた文字内にあるカンマを削除したい 3 2022/09/02 15:17
- Excel(エクセル) エクセルデーターの並び替え 5 2022/08/06 09:59
- Excel(エクセル) 【vba】日付の形式が勝手に変わってしまう。 1 2022/09/29 10:54
- PHP PHP MySql 画像を取得 1 2022/06/04 14:05
- Visual Basic(VBA) ExcelのVBAコードについて教えてください。 2 2023/01/23 17:13
- Visual Basic(VBA) 【前回の続きです、ご教示ください】VBAの記述方法がわかりません。 2 2022/08/16 16:44
- Excel(エクセル) VBA : スクレイピングできない 4 2023/05/12 22:26
- Visual Basic(VBA) VBA 改行コードの取り方 1 2022/03/22 14:14
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ファイル名のみを返す関数は?
-
配列同士の足し算のループ処理
-
csvの内容を行単位で削除したい
-
PHPでCSVの一部の行を編集したい
-
自動生成するCSVの改行が上手く...
-
【PHP】csvファイルへの書き出...
-
連想配列で
-
doxygenを使用する環境について
-
CSVデータを正規表現で抜き出せ...
-
UTF-8のXMLがSJISのPHPで文字化け
-
外部ファイルの出力
-
PHPからCSVをアップロード後、m...
-
FortranのOPEN文
-
自動で番号を振りたい
-
rubyで複数列のデータを一行に...
-
CSVファイルの最終行のデー...
-
PHP SimpleXml unsetについて
-
pythonのファイルの並びでの読...
-
エラーメッセージ(無効な間接...
-
別ファイルの構造体の値を読み...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
別ファイルの構造体の値を読み...
-
C言語でCSVファイルの行数を読...
-
Resource id #3 と表示されま...
-
【PHP】csvファイルへの書き出...
-
CSVファイルの最終行のデー...
-
行数が30万件ほどあるCSVから、...
-
自動で番号を振りたい
-
doxygenを使用する環境について
-
PHP検索ボックス複数設置
-
stdClass Objectを連想配列のよ...
-
ヒアドキュメントの中のfor文
-
PHP SimpleXml unsetについて
-
複数行のデータのPOST処理に関して
-
エラーメッセージ(無効な間接...
-
PHPでCSVの一部の行を編集したい
-
ログファイルの古い日付の行を...
-
配列同士の足し算のループ処理
-
多次元配列の一次元目の最大値...
-
文字列の文字一文字ずつを解析...
-
複数ファイルで、それぞれの行...
おすすめ情報