Perl:CSV形式の配列変数から値を検索(抽出)する

 以前作成した&csv_load()でCSVファイルから読み込んだデータからキーワードが含まれる行を抽出します。

使い方
・前回と同様に配列変数「@csv_line」に対して操作するので、グローバル変数とするかローカル変数の宣言をして下さい。
・検索対象としない列は「@csv_search_hide」の同じ配列に数列「-1」をセットして下さい。これもグローバル変数・ローカル変数として下さい。
・&csv_search(検索ワード,単語数制限);
 検索ワードは(半角・全角)スペース区切りで、全ての単語が含まれる行を抽出します。
 単語数制限は3とすれば「単語1 単語2 単語3 単語4」と検索した時、4単語あるの以上は無視します。
・キーワードが空値の時は@csv_lineが空になります。

検索ワードについて
・一般的な検索サイトと同様に単語のスペース区切りで検索できます。
・制御文字(下記)により AND OR NOT などの検索ができます。
・キーワード英字は小文字大文字関係無しです。

制御文字
・* + – ” ‘ スペース(全半角) が使えます。
・「A B C」とするとAとBとCの全てが含まれた行のみ出力します(AND)
・「A*B*C」も同上です
・「A+B+C」とするとAとBとCの1つでも含まれた行のみ出力します(OR)
・「-A-B-C」とするとAとBとCの全てが含まれない行のみ出力します(NotAND)
(実際の動作はA はNotOR して B と C だけNotANDです)
・「-A*-B*-C」も同上です
・「-A+-B+-C」とするとAとBとCのどれかが含まれない行のみ出力します(NotOR)
※マイナスをつける位置は単語の前だけです。記号の前につけても無視されます。
※記号の前後のスペースは無視されます。(「A + B * – D E」と「A+B*-D E」は同じ)
・シングル・ダブルクオーテーション で括ると、括られた中で * + – スペースを検索対象にできます。
「”A A” + “***” – “—“」
・またダブルクオーテーション内ではシングルクォーテーションが、シングルクォーテーション内ではダブルクオーテーションが検索対象にできます。
「””'”」
※ダブルクオーテーションやシングルクォーテーションが1つか無い場合は検索文字として処理されます。

注意点
・キーワードが空値の時は@csv_lineが空になります。

備忘録
プログラムが長すぎて何をやってるか忘れそうなので覚書
・前半のの検索キーワードの整頓では、1文字ずつ文字を拾い ” ‘ * + – の記号によって処理を分岐
・後半は該当する行にフラグを立て、and or 比較して出力しています。

プログラム

#-------------------------------------------------
# CSV形式の配列変数(@csv_line)からキーワードが含まれる行を抽出する。
#-------------------------------------------------
sub csv_search{	#0=keyword 1=limit 2=mode
	my($k)=$_[0]." ";#※処理上の半角スペースを足している。
	my($i,$j);
	#キーワード抽出用変数
	my($start)=0;
	my($mode)="0";	#1=and 2=or  3=not-and 4=not-or
	my($count)=0;
	my($utf8_space,@keys,$utf);
	#データ抽出用変数
	my(@csv,@line,@index,$v);

	#- - - - - - - - - - - - - - - - - - - - -
	# 検索キーワードの整頓
	#- - - - - - - - - - - - - - - - - - - - -
	$k =~ s/\x0D//g;				#検索キーワードで使えない文字を除外する
	$k =~ s/\x0A//g;
	$k = &csv_format_save($k);		#←検索ワードをセーブ形式に・・配列側で逐次表示形式に変換すると時間がかかるため
	utf8::decode($k);				#utf8時にカウントがおかしくなる対策
	$utf8_space=" ";				#そのままだとdecodeした値と比較演算できないので
	utf8::decode($utf8_space);
	for ($i = 0; $i <= length($k)-1; $i++){
		if (substr($k,$i,1) eq '"'){				#ダブルクオーテーションで括られている時の処理
			for ($j = $i+1; $j <= length($k)-1; $j++){
				if (substr($k,$j,1) eq '"'){
					if ($start != $i){
						$mode="1";
					}
					if (substr($k,$i+1,$j-($i+1)) ne ""){
						$utf=substr($k,$i+1,$j-($i+1));
						utf8::encode($utf);
						$keys[$#keys+1]="$mode,$utf";
						$count++;
					}
					$i=$j;
					$start=$j+1;
					$mode="1";
					last;
				}
			}
		}elsif (substr($k,$i,1) eq "'"){				#シングルクオーテーションで括られている時の処理
			for ($j = $i+1; $j <= length($k)-1; $j++){
				if (substr($k,$j,1) eq "'"){
					if ($start != $i){
						$mode="1";
					}
					if (substr($k,$i+1,$j-($i+1)) ne ""){
						$utf=substr($k,$i+1,$j-($i+1));
						utf8::encode($utf);
						$keys[$#keys+1]="$mode,$utf";
						$count++;
					}
					$i=$j;
					$start=$j+1;
					$mode="1";
					last;
				}
			}
		}else{											#括られていない時の処理
			if (substr($k,$i,1) eq " "){
				if ($start != $i){
					$utf=substr($k,$start,$i-($start));
					utf8::encode($utf);
					$keys[$#keys+1]="$mode,$utf";
					$count++;
					$mode="1";
				}
				$start=$i+1;
			}elsif (substr($k,$i,1) eq $utf8_space ){	#全角スペース
				if ($start != $i){
					$utf=substr($k,$start,$i-($start));
					utf8::encode($utf);
					$keys[$#keys+1]="$mode,$utf";
					$count++;
					$mode="1";
				}
				$start=$i+1;
			}elsif (substr($k,$i,1) eq "*"){
				if ($start != $i){
					$utf=substr($k,$start,$i-($start));
					utf8::encode($utf);
					$keys[$#keys+1]="$mode,$utf";
					$count++;
				}
				$mode="1";
				$start=$i+1;
			}elsif (substr($k,$i,1) eq "+"){
				if ($start != $i){
					$utf=substr($k,$start,$i-($start));
					utf8::encode($utf);
					$keys[$#keys+1]="$mode,$utf";
					$count++;
				}
				$mode="2";
				$start=$i+1;
			}elsif (substr($k,$i,1) eq "-"){
				if ($start != $i){
					$utf=substr($k,$start,$i-($start));
					utf8::encode($utf);
					$keys[$#keys+1]="$mode,$utf";
					$count++;
				}
				if ($mode eq "0"){
					if ($#keys==-1){
						$mode="4";
					}else{
						$mode="3";
					}
				}elsif ($mode eq "2"){
					$mode="4";
				}else{
					$mode="3";		#3と4の時 はNot-AND
				}
				$start=$i+1;
			}
		}
		#リミッター($_[0]が0以上の正数なら$_[0]に従い、それ以外の時は5ワードで終了)
		if ($_[1] > 0 ){
			if ($_[1] <= $count ){last;}
		}else{
			if (5 <= $count ){last;}
		}
	}
	#- - - - - - - - - - - - - - - - - - - - -
	#検索非対象を除外した検索用の配列(@line)を作成
	#- - - - - - - - - - - - - - - - - - - - -
	for ($i = 0; $i <= $#csv_line; $i++){
		@csv=split(/,/,$csv_line[$i]);	#←検索ワード側を変換済みのためcsv_splitを使用してない
		for ($j = 0; $j <= $#csv; $j++){
			if ($csv_search_hide[$j] != -1){
				$line[$i].="$csv[$j],";
			}
		}
	}
	#- - - - - - - - - - - - - - - - - - - - -
	# 抽出と出力
	#- - - - - - - - - - - - - - - - - - - - -
	for ($j = 0; $j <= $#keys; $j++){
		$v = $keys[$j];
		$v =~ s/\\/\\\\/g;				#正規表現でエラーの出る文字を置換する。
		$v =~ s/\+/\\+/g;
		$v =~ s/\*/\\*/g;
		$v =~ s/\?/\\?/g;
		$v =~ s/\./\\./g;
		$v =~ s/\(/\\(/g;
		$v =~ s/\)/\\)/g;
		$v =~ s/\[/\\[/g;
		$v =~ s/\]/\\]/g;
		$v =~ s/\{/\\{/g;
		$v =~ s/\}/\\}/g;
		$v =~ s/\|/\\|/g;
		$v =~ s/\^/\\^/g;
		$v =~ s/^[0-9]+,//;
		if ($keys[$j] =~ /^1,/){		#and
			for ($i = 0; $i <= $#csv_line; $i++){
				if ($index[$i] == 1){
					if ($line[$i] !~ /$v/i){$index[$i]=-1};
				}
			}
		}elsif($keys[$j] =~ /^3,/){	#not-and
			for ($i = 0; $i <= $#csv_line; $i++){
				if ($index[$i] == 1){
					if ($line[$i] =~ /$v/i){$index[$i]=-1};
				}
			}
		}elsif($keys[$j] =~ /^2,/){	#or
			for ($i = 0; $i <= $#csv_line; $i++){
				if ($line[$i] =~ /$v/i){ $index[$i]=1 };
			}
		}elsif($keys[$j] =~ /^4,/){	#not-or
			for ($i = 0; $i <= $#csv_line; $i++){
				if ($line[$i] !~ /$v/i){ $index[$i]=1 };
			}
		}else{
			for ($i = 0; $i <= $#csv_line; $i++){
				if ($line[$i] =~ /$v/i){ $index[$i]=1 };
			}
		}
	}

	#@csv_lineに出力
	@line=();
	for ($i = 0; $i <= $#csv_line; $i++){
		if ($index[$i] == 1){
			$line[$#line+1]=$csv_line[$i];
		}
	}
	@csv_line=@line;
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA