Sas 为什么PROC FCMP函数总是返回33个字节而不是更多?

Sas 为什么PROC FCMP函数总是返回33个字节而不是更多?,sas,fcmp,Sas,Fcmp,我通过PROC FCMP定义了以下函数。代码的要点应该非常明显并且相对简单。我从XHTML的一行返回属性的值。代码如下: proc fcmp outlib=library.funcs.crawl; function getAttr(htmline $, Attribute $) $; /*-- Find the position of the match --*/ Pos = index( htmline , strip( Attribute )||"=" );

我通过
PROC FCMP
定义了以下函数。代码的要点应该非常明显并且相对简单。我从XHTML的一行返回属性的值。代码如下:

proc fcmp outlib=library.funcs.crawl;
    function getAttr(htmline $, Attribute $) $;

       /*-- Find the position of the match --*/
    Pos = index( htmline , strip( Attribute )||"=" );

       /*-- Now do something about it --*/
       if pos > 0 then do;
          Value = scan( substr( htmline, Pos + length( Attribute ) + 2), 1, '"');
       end;
       else Value = "";
       return( Value);
    endsub;
run;
无论我如何使用length或
attrib
语句来显式声明返回的数据类型,它总是只返回请求字符串的最多33个字节,而不管实际返回值的长度。无论我搜索哪个属性,都会发生这种情况。数据步骤中的相同代码(硬编码)返回正确的结果,因此这与
PROC FCMP
有关

下面是我用来测试它的datastep(其中PageSource.html是任何具有xhtml兼容属性的html文件——完全引用):

更新:升级到SAS9.2-Release 2后,这似乎可以正常工作

我认为问题(尽管我不知道为什么)出在扫描函数中-它似乎正在截断substr()的输入。如果将substr函数从scan()中拉出,将substr函数的结果赋给一个新变量,然后将该变量传递给scan,则该函数似乎有效

以下是我的跑步记录:

proc fcmp outlib=work.funcs.crawl;
    function getAttr(htmline $, Attribute $) $;
    length y $200;
       /*-- Find the position of the match --*/
    Pos = index( htmline , strip( Attribute )||"=" );

       /*-- Now do something about it --*/
       if pos > 0 then do;
          y=substr( htmline, Pos + length( Attribute ) + 2);
          Value = scan( y, 1, '"');       
       end;
       else Value = "";
       return( Value);
    endsub;
run;

options cmplib=work.funcs;

data TEST;
length href $200;
infile "PageSource.html";

input;

htmline = _INFILE_;
href = getAttr( htmline, "href");
x = length(href);
run;

我最终放弃使用FCMP定义的数据步函数。我认为他们还没有准备好迎接黄金时间。我不仅不能解决33字节的返回问题,而且它开始定期崩溃SAS

所以回到过去(几十年前)的宏技术。这项工作:

/*********************************/
/*= Macro to extract Attribute  =*/
/*= from XHTML string           =*/
/*********************************/
%macro getAttr( htmline, Attribute, NewVar );
   if index( &htmline , strip( &Attribute )||"=" ) > 0 then do;
      &NewVar = scan( substr( &htmline, index( &htmline , strip( &Attribute )||"=" ) + length( &Attribute ) + 2), 1, '"' );
   end;
%mend;

在这种情况下,输入指针控件就足够了。希望这有帮助

/* create a test input file */
data _null_;
  file "f:\pageSource.html";
  input;
  put _infile_;
cards4;
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="w3.org/StyleSheets/TR/W3C-REC.css"; type="text/css"?>
;;;;
run;

/* extract the href attribute value, if any.                          */
/* assuming that the value and the attribute name occurs in one line. */
/* and max length is 200 chars.                                       */
data one;
  infile "f:\pageSource.html" missover;
  input @("href=") href :$200.;
  href = scan(href, 1, '"'); /* unquote */
run;

/* check */
proc print data=one;
run;
/* on lst
Obs                  href
 1
 2     w3.org/StyleSheets/TR/W3C-REC.css
*/
/*创建一个测试输入文件*/
数据为空;
文件“f:\pageSource.html”;
输入;
把"填入";;
卡片4;
;;;;
跑
/*提取href属性值(如果有)*/
/*假设值和属性名称出现在一行中*/
/*最大长度为200个字符*/
数据一;
填充“f:\pageSource.html”misshover;
输入@(“href=”)href:$200。;
href=scan(href,1,“”);/*unquote*/
跑
/*检查*/
proc打印数据=一个;
跑
/*在lst上
Obs href
1.
2 w3.org/StyleSheets/TR/W3C-REC.css
*/

>P>似乎在PRC FCMP中未初始化的变量得到默认长度为33字节。考虑下面的演示代码:

OPTIONS INSERT = (CMPLIB = WORK.FCMP);

PROC FCMP
    OUTLIB = WORK.FCMP.FOO
;

    FUNCTION FOO(
        BAR $
    );

        * Assign the value of BAR to the uninitialised variable BAZ;
        BAZ = BAR;

        * Diagnostics;
        PUT 'BAR IS ' BAR;
        PUT 'BAZ IS ' BAZ;  

        * Return error code;
        IF
            LENGTH(BAZ) NE LENGTH(BAR)
        THEN
            RETURN(0)
        ; ELSE
            RETURN(1)
        ;

    ENDSUB;

RUN;

DATA _NULL_;

    X = 'shortstring';
    Y = 'exactly 33 characters long string';
    Z = 'this string is somewhat longer than 33 characters';

    ARRAY STRINGS{*} _CHARACTER_;
    ARRAY RC{3} 8 _TEMPORARY_;

    DO I = 1 TO DIM(STRINGS);

        RC[I] = FOO(STRINGS[I]);

    END;

RUN;
在我的站点安装(Base SAS 9.4 M2)后,它会将以下行打印到日志中:

BAR IS  shortstring
BAZ IS  shortstring
BAR IS  exactly 33 characters long string
BAZ IS  exactly 33 characters long string
BAR IS  this string is somewhat longer than 33 characters
BAZ IS  this string is somewhat longer th
这可能与以下事实有关:PROC FCMP与数据步骤一样,无法在运行时动态分配可变长度。然而,这有点令人困惑,因为它确实为参数动态分配可变长度。我假设存在单独的“初始化”“PROC FCMP子例程的阶段,在此期间确定作为参数传递的值的长度,并将必须保存这些值的参数变量初始化为所需的长度。但是,只有在已分配内存的情况下,才能在运行时发现仅在子例程主体内定义的变量长度。因此,在运行之前(无论是在编译时还是在我假设的“初始化”阶段),内存都会通过显式长度语句(如果存在)分配给这些变量,否则会返回到默认值33字节

现在真正有趣的是,PROC FCMP在这方面非常聪明——在初始化/运行时阶段的严格分离范围内。如果在子例程主体中,一个变量
a
具有明确定义的长度,然后另一个未初始化变量
B
被分配了
a
的函数,则
B
被设置为与
a
相同的长度。考虑上述函数的修改,其中<代码> Bar <代码>的值没有直接分配给<代码> BAZ,而是通过第三个变量<代码> Qux,它有明确定义的<代码>长度<代码> 50字节:

OPTIONS INSERT = (CMPLIB = WORK.FCMP);

PROC FCMP
    OUTLIB = WORK.FCMP.FOO
;

    FUNCTION FOO(
        BAR $
    );


        LENGTH QUX $ 50;
        QUX = BAR;
        * Assign the value of BAR to the uninitialised variable BAZ;
        BAZ = QUX;

        * Diagnostics;
        PUT 'BAR IS ' BAR;
        PUT 'BAZ IS ' BAZ;  

        * Return error code;
        IF
            LENGTH(BAZ) NE LENGTH(BAR)
        THEN
            RETURN(0)
        ; ELSE
            RETURN(1)
        ;

    ENDSUB;

RUN;

DATA _NULL_;

    X = 'shortstring';
    Y = 'exactly 33 characters long string';
    Z = 'this string is somewhat longer than 33 characters';

    ARRAY STRINGS{*} _CHARACTER_;
    ARRAY RC{3} 8 _TEMPORARY_;

    DO I = 1 TO DIM(STRINGS);

        RC[I] = FOO(STRINGS[I]);

    END;

RUN;
日志显示:

BAR IS  shortstring
BAZ IS  shortstring
BAR IS  exactly 33 characters long string
BAZ IS  exactly 33 characters long string
BAR IS  this string is somewhat longer than 33 characters
BAZ IS  this string is somewhat longer than 33 characters
这种“有益”的行为很可能是导致之前答案混乱和差异的原因。我想知道这种行为是否有记录


我将把它作为一个练习留给读者,让他们研究smart SAS到底是如何做到这一点的。例如,如果一个未初始化的变量被分配了另外两个具有显式分配长度的变量的串联值,那么它的长度是否设置为其他两个变量的长度之和?

我没有SAS 9.2来测试这一点,但是您是否尝试过为值变量使用显式长度语句?是的。仍然只返回33个字符。还在函数语句中的返回类型上尝试了一个明确的$150。刚刚进入第2阶段,将在这里再次尝试。棒极了!疯狂的疯子认为它是这样做的,但它现在起作用了。根据我对您之前SAS答案的评估,接受您的答案。今晚晚些时候到家时我将进行测试(这台机器上没有SAS)。谢谢也许这实际上与这样一个事实有关,即如果变量从函数“返回”,则无法对其进行尺寸标注?抱歉@cmjohns。这对我不起作用。它不仅继续返回33个字节,而且通过尝试访问该函数,我可以再次硬崩溃sas.exe。我不认为PROC FCMP是数据步骤使用的发布质量。您使用的是哪个版本的SAS?您是否为y添加了长度语句?我在9.2上,我发誓它对我有用。这里是test.sas7bdat Obs href 1 Obs htmline 1 Obs x 1 44的第一行
BAR IS  shortstring
BAZ IS  shortstring
BAR IS  exactly 33 characters long string
BAZ IS  exactly 33 characters long string
BAR IS  this string is somewhat longer than 33 characters
BAZ IS  this string is somewhat longer than 33 characters