我正试图找到一个函数来索引字符的第n个实例。
例如,如果我有字符串ABABABBABSSSDDEE
,并且我想找到A
的第三个实例,我该怎么做?如果我想找到AB
的第四个实例怎么办
ABABABBABSSSDDEE
data HAVE;
input STRING $;
datalines;
ABABABBASSSDDEE
;
RUN;
以下是使用SAS find()函数查找SAS字符串中一组字符的第N个实例的简化实现:
data a;
s='AB bhdf +BA s Ab fs ABC Nfm AB ';
x='AB';
n=3;
/* from left to right */
p = 0;
do i=1 to n until(p=0);
p = find(s, x, p+1);
end;
put p=;
/* from right to left */
p = length(s) + 1;
do i=1 to n until(p=0);
p = find(s, x, -p+1);
end;
put p=;
run;
正如您所看到的,它同时允许从左到右和从右到左的搜索。
您可以将这两个组合成SAS用户定义函数(负n表示从右向左搜索,就像在查找函数中一样):
proc fcmp outlib=sasuser.functions.findnth;
function findnth(str $, sub $, n);
p = ifn(n>=0,0,length(str)+1);
do i=1 to abs(n) until(p=0);
p = find(str,sub,sign(n)*p+1);
end;
return (p);
endsub;
run;
请注意,上面使用FIND()和FINNTH()函数的解决方案假设搜索到的子字符串可以与其上一个实例重叠。例如,如果我们在字符串"ABAAAA"中搜索子字符串"AAA",则"AAA"的第一个实例将在位置3中找到,第二个实例-在位置4中找到。也就是说,第一个实例和第二个实例是重叠的。因此,当我们找到一个实例时,我们将位置p增加1(p+1),以开始搜索的下一次迭代(实例)。然而,如果这种重叠在您的搜索中不是有效的情况,并且您希望在上一个子字符串实例结束后继续搜索,那么我们应该将p不是增加1,而是增加子字符串x的长度。这将加快我们的搜索速度(子字符串x越长),因为我们在遍历字符串s时将跳过更多的字符。在这种情况下,在我们的搜索代码中,我们应该将p+1替换为p+w,其中w=长度(x)。
在我最近的SAS博客文章《在字符串中查找子字符串的第n个实例》中描述了对这个问题的详细讨论。我还发现,使用find()函数比在SAS中使用正则表达式函数要快得多。
我意识到我来这里参加聚会迟到了,但为了增加答案,以下是我的想法。
DATA test;
input = "ABABABBABSSSDDEE";
A_3 = find(prxchange("s/A/#/", 2, input), "A");
AB_4 = find(prxchange("s/AB/##/", 3, input), "AB");
RUN;
细分来看,prxchange()
只是做了一个模式匹配替换,但它的好处是你可以告诉它替换该模式多少次。因此,prxchange("s/A/#/", 2, input)
将input
中的前两个A替换为#。一旦替换了前两个A,就可以将其封装在find()
函数中,以找到"第一个A",它实际上是原始字符串的第三个A。
关于这种方法需要注意的一点是,理想情况下,替换字符串的长度应该与要替换的字符串的长度相同。例如,注意之间的差异
prxchange("s/AB/##/", 3, input) /* gives 8 (correct) */
和
prxchange("s/AB/#/", 3, input) /* gives 5 (incorrect) */
这是因为我们已经用长度为1的字符串替换了三次长度为2的字符串。换句话说:
(length("#") - length("AB")) * 3 = -3
因此CCD_ 8。
希望这能帮助到外面的人!
data _null_;
findThis = 'A'; *** substring to find;
findIn = 'ADABAACABAAE'; **** the string to search;
instanceOf=1; *** and the instance of the substring we want to find;
pos = 0;
len = 0;
startHere = 1;
endAt = length(findIn);
n = 0; *** count occurrences of the pattern;
pattern = '/' || findThis || '/';
rx = prxparse(pattern);
CALL PRXNEXT(rx, startHere, endAt, findIn, pos, len);
if pos le 0 then do;
put 'Could not find ' findThis ' in ' findIn;
end;
else do while (pos gt 0);
n+1;
if n eq instanceOf then leave;
CALL PRXNEXT(rx, startHere, endAt, findIn, pos, len);
end;
if n eq instanceOf then do;
put 'found ' instanceOf 'th instance of ' findThis ' at position ' pos ' in ' findIn;
end;
else do;
put 'No ' instanceOf 'th instance of ' findThis ' found';
end;
run;
这里有一个使用find()
函数和数据步骤中的do循环的解决方案。然后,我将该代码放入proc fcmp
过程中,创建自己的函数find_n()
。这将大大简化使用它的任何任务,并允许代码重用。
定义数据:
data have;
length string $50;
input string $;
datalines;
ABABABBABSSSDDEE
;
run;
Do循环解决方案:
data want;
set have;
search_term = 'AB';
nth_time = 4;
counter = 0;
last_find = 0;
start = 1;
pos = find(string,search_term,'',start);
do while (pos gt 0 and nth_time gt counter);
last_find = pos;
start = pos + 1;
counter = counter + 1;
pos = find(string,search_term,'',start+1);
end;
if nth_time eq counter then do;
put "The nth occurrence was found at position " last_find;
end;
else do;
put "Could not find the nth occurrence";
end;
run;
定义proc fcmp
函数:
注意:如果找不到第n次出现,则返回0。
options cmplib=work.temp.temp;
proc fcmp outlib=work.temp.temp;
function find_n(string $, search_term $, nth_time) ;
counter = 0;
last_find = 0;
start = 1;
pos = find(string,search_term,'',start);
do while (pos gt 0 and nth_time gt counter);
last_find = pos;
start = pos + 1;
counter = counter + 1;
pos = find(string,search_term,'',start+1);
end;
result = ifn(nth_time eq counter, last_find, 0);
return (result);
endsub;
run;
示例proc fcmp
用法:
请注意,这会调用函数两次。第一个示例显示了原始请求解决方案。第二个示例显示了当找不到匹配项时会发生什么。
data want;
set have;
nth_position = find_n(string, "AB", 4);
put nth_position =;
nth_position = find_n(string, "AB", 5);
put nth_position =;
run;