お世話になります。
textの列の中で半角の数字が3つ以上並んだ部分のみを全角に変換したいと考えており、以下サイトを参考に半角数字が3つ並んだ部分の有無については検出に成功しました。
ですが、置換のやり方がわからないです。
http://tetchi-kun.hatenablog.com/entry/2015/01/13/183205
PRXCHANGE関数で行けるかと思い、色々試していますがうまくいきません。
よろしくお願いいたします。
そこだけ
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'));
end; else
do;
modified=cats(modified,temp);
end;
と修正すればいいかと思います。
PRXCHANGEは使ったことがないのですが、マッチした検索文字列をTRANWRDで置換すればいいと思いますがいかがでしょうか。
DATA _null_;
length modified $200;/* 全角にするのでoriginalの2倍確保したい */
original = '東京都品川区広町2丁目1-306-F111棟505号室';
modified = original;
/* 正規表現識別子 */
prxid=PRXPARSE('/[0-9]{3,}/');
idx = PRXMATCH(prxid, modified);
do while(idx>0);
before = PRXPOSN(prxid, 0, modified);
alter = KPROPCASE(before,'HALF-ALPHABET, FULL-ALPHABET');
modified = TRANWRD(modified,trim(before),trim(alter));
putlog modified=;
idx = PRXMATCH(prxid, modified);
end;
RUN;
流石にそのデータ量でも半日はかかりすぎな気もするので、データセットオプションで(obs=2)くらいを指定して
処理がループしていないかどうかを確認されてはいかがでしょうか。
(10秒くらいで終わらなければ処理を中断してログを確認する)
また、先のプログラムに誤りがありました。
TRANWRDを使うとヒットする文字すべてを置き換えてしまうため、
「ABC222DEF22222」
のような場合、最初に「222」が変換キーワードになり
「ABC222DEF22222」
と後ろの「22」が変換されずに残ってしまいます。
ちょっと古典的ではありますが、substr(subpad)で文字列を再構築する方法でやってみました。
/* サンプルデータ作成 */
data sample(keep=original modified);
length original $4000 /* 4000バイト */
modified $8000 /* 全角変換するので倍 */
rand0 8 /* 数値、アルファベットの処理フラグ */
rand1 8 /* 連続させる文字数 */
rand2 8; /* ランダムな英数字 */
modified='';
seed=int(datetime());
do i=1 to 20000;
original='';
do until(length(original)>3999);
rand0=mod(int(100*ranuni(seed)+1),4);
rand1=int(10*ranuni(seed)+0);
select (rand0);
when(1) rand2=int( 9*ranuni(seed)+48);
when(2) rand2=int(25*ranuni(seed)+65);
when(3) rand2=int(25*ranuni(seed)+97);
otherwise;
end;
original=cats(original,repeat(byte(rand2),rand1));
end;
output;
end;
run;
/* 変換する */
data converted(keep=original modified);
set sample;
modified = original;
/* 正規表現識別子 */
prxid=PRXPARSE('/[0-9]{3,}/');
idx = PRXMATCH(prxid, modified);
do while(idx>0);
before = PRXPOSN(prxid, 0, modified);
after = KPROPCASE(before,'HALF-ALPHABET, FULL-ALPHABET');
modified= subpad(modified,1,index(modified,strip(before))-1)||trim(after)||substr(modified,index(modified,strip(before))+length(before));
idx = PRXMATCH(prxid, modified);
end;
run;
20000obs、4000バイトでも40秒程度で処理が終わりました。
コードの書き換えありがとうございます。確認させていただきます。
もう一度回しましたが、やはり追加したコードのところで動かなくなります。
試しにobs=2にしたら、上から2サンプルのテキストが空だったので最後まで回りましたが、obs=200にした場合、文字が必ず入ってくるので今までと同じところで止まりました。
ちなみにデータセットの上から20サンプルはテキスト部分が空であり、21サンプル目からがテキストが入っているようですが、強制終了したところ21サンプルで停止しました、とのWARNINGがでるのでやはり原因は追加したコードのようです。
ちなみに今回は全角から半角の変換です。
'/[0-9]{3,}/'とし、FULLとHALFを修正しているので問題はないと思われます。
なにか他に原因が疑われるところがありましたら、ご助言いただけますと幸いです。
最初は
textの列の中で半角の数字が3つ以上並んだ部分のみを全角に変換したいと考えており、。
とおっしゃっていましたので従前のコードをご提示しましたが、
ちなみに今回は全角から半角の変換です。
ということだと話が変わります。このコードでは動きません。
PRXPOSN関数もPRXMATCH関数もDBCSには対応しておらず、全角文字を検出できないためです。
思いつくのは、kindexcで0-9を検出して、検出した文字以降を
1文字ずつチェックして3桁以上の場合は変換、という感じでしょうか。
ロジックが面倒かつ処理時間も掛かりそうですが…
length temp $4000
char $2;
do i=1 to klength(original);
char=ksubstr(original,i,1);
if char in ('0','1','2','3','4','5','6','7','8','9') then do;
cnt=cnt+1;
temp=cats(temp,char);
if i=klength(original) then do;
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'),char);
end; else
do;
modified=cats(modified,char);
end;
end;
end; else
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'),char);
cnt=0;
temp='';
end; else
if 0<cnt<3 then do;
modified=cats(modified,temp,char);
cnt=0;
temp='';
end; else
do;
modified=cats(modified,char);
end;
end;
10分くらいはかかると思いますが参考まで。
doループの開始をkindexcで調べた値にすると少し改善できると思います。
ありがとうございます。拝見いたしました。
頂いたコードを少しいじって試してみたのですが、うまく行かなかったです。。。
DATA a;
length original $100 modified $200;/* 全角にするのでoriginalの2倍確保したい */
original = '東京都品川区広町2丁目1-306-F11棟5050号室';
modified = original;
run;data b;
set a;
/* 正規表現識別子 */
length temp $4000
char $2;
do i=1 to klength(original);
char=ksubstr(original,i,1);
if char in ('0','1','2','3','4','5','6','7','8','9') then do;
cnt=cnt+1;
temp=cats(temp,char);
if i=klength(original) then do;
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'),char);
end; else
do;
modified=cats(modified,char);
end;
end;
end; else
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'),char);
cnt=0;
temp='';
end; else
if 0<cnt<3 then do;
modified=cats(modified,temp,char);
cnt=0;
temp='';
end; else
do;
modified=cats(modified,char);
end;
end;
RUN;
実行後データセットを開けてみると、cntがブランクになっているのが気になりますが問題はないでしょうか。
また、そもそもロジックとして全角→半角の前に、半角文字を全角にしてその後3桁以上のものを全角に戻しているのですが、その際に3桁以上のものは全角に変換しない、というロジックのほうが楽だったりしますでしょうか。
以下にサンプル作成から変換までフルコード載せています。(OBS数は200に制限しています)
これをこのまま実行して、想定通りか確認してみてください。
その後、ご自身のデータをsetして動くか確認してください。
/* サンプルデータ作成 */
data sample2(keep=original);
array zen{36} $2 ('a','b','c','d','e','f','g','h','i','j'
,'k','l','m','n','o','p','q','r','s','t'
,'u','v','w','x','y','z'
,'1','2','3','4','5','6','7','8','9','0'
);
length original $4000 /* 4000バイト */
rand0 8 /* 数値、アルファベットの処理フラグ */
rand1 8 /* 連続させる文字数 */
rand2 $2; /* ランダムな英数字 */
seed=int(datetime());
do i=1 to 20000;
original='';
do until(length(original)>3999);
rand0=mod(int(100*ranuni(seed)+1),4);
rand1=int(10*ranuni(seed)+0);
select (rand0);
when(0) rand2=zen {int(35*ranuni(seed)+ 1)};
when(1) rand2=byte(int( 9*ranuni(seed)+48));
when(2) rand2=byte(int(25*ranuni(seed)+65));
when(3) rand2=byte(int(25*ranuni(seed)+97));
otherwise;
end;
original=cats(original,repeat(strip(rand2),rand1));
end;
output;
end;
run;
/* 半角(数字3桁以上)から全角に変換 */
data H2Z_converted(keep=original modified);
set sample2(obs=100);
length modified $8000;
modified = original;
/* 正規表現識別子 */
prxid=PRXPARSE('/[0-9]{3,}/');
idx = PRXMATCH(prxid, trim(modified));
do while(idx>0);
before = PRXPOSN(prxid, 0, modified);
after = KPROPCASE(before,'HALF-ALPHABET, FULL-ALPHABET');
modified= subpad(modified,1,index(modified,strip(before))-1)||trim(after)||substr(modified,index(modified,strip(before))+length(before));
idx = PRXMATCH(prxid, trim(modified));
end;
run;
/* 全角(数字3桁以上)から半角に変換 */
data Z2H_converted(keep=original modified);
set sample2(obs=100);
length modified $4000
temp $4000 /* 全角文字列 */
char $2; /* 処理対象文字 */
idx=kindexc(original,'0123456789');
cnt=0;
temp='';
if idx=0 then do;
modified=original;
end; else
do;
if idx=1 then do;
modified='';
end; else
if idx>1 then do;
modified=ksubstr(original,1,idx-1);
end;
do i=idx to klength(original);
/* 全角数値ならキープ */
char=ksubstr(original,i,1);
if char in ('0','1','2','3','4','5','6','7','8','9') then do;
cnt=cnt+1;
temp=cats(temp,char);
/* 最後だけ全角数値でも処理を行う */
if i=klength(original) then do;
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'),char);
end; else
do;
modified=cats(modified,char);
end;
end;
end; else
/* 全角数値でない、3連続以上全角数値だったら置換 */
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'),char);
cnt=0;
temp='';
end; else
/* 全角数値でない、3連続未満全角数値だったらそのまま */
if 0<cnt<3 then do;
modified=cats(modified,temp,char);
cnt=0;
temp='';
end; else
/* 全角数値でない、直前が全角数値以外だったらそのまま */
do;
modified=cats(modified,char);
end;
end;
end;
run;
プログラムありがとうございます。
こちらの説明があいまいで申し訳ございません。コメントまでつけてくださり、非常に助かります。
一点気になったのですが、
/* 全角(数字3桁以上)から半角に変換 */
の部分ですが、文字列の最後が変換後、半角数字で終わってしまうものについて、0830→08300のようになってしまいます。
/* 最後だけ全角数値でも処理を行う */の部分で対応されているのでしょうか?(うまく行っていない?)
どうぞよろしくお願いいたします。
そこだけ
if cnt>=3 then do;
modified=cats(modified,KPROPCASE(temp,'FULL-ALPHABET, HALF-ALPHABET'));
end; else
do;
modified=cats(modified,temp);
end;
と修正すればいいかと思います。
他の回答と同じことを言っているかもしれませんが。ご参考まで。
PRXCHANGEや、PRXMATCH関数を使わなくなって久しいので、すぐに試して確認した結果をお伝え出来ず恐縮ですが、これらSASの正規表現の関数を使う場合、引数上でTRIMなどで後ろの空白を削除するようにすることでパフォーマンス向上が期待できたと記憶しています。
例)RC = PRXCHANGE(prxid, trim(modified))
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!