为什么PROC FCMP功能总是返回33个字节而不再?

时间:2009-06-23 12:26:57

标签: 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 )||"=" );

       /*-- 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文件 - 完全引用):

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

input;

htmline = _INFILE_;

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

run;

更新:升级到SAS9.2 - 第2版后,这似乎正常工作

4 个答案:

答案 0 :(得分:2)

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

这是我跑的:

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;

答案 1 :(得分:2)

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

/* 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
*/

答案 2 :(得分:2)

似乎PROC 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(如DATA步骤)无法在运行时动态分配变量长度有关。但是,这有点令人困惑,因为它确实参数动态分配可变长度。我假设PROC FCMP子例程有一个单独的“初始化”阶段,在此阶段中,确定作为参数传递的值的长度,并将必须保留这些值的参数变量初始化为所需的长度。但是,只有在已经分配了内存的情况下,才可以在运行时发现子例程主体中 only 定义的变量的长度。因此,在运行时之前(无论是在编译时还是在我假设的“初始化”阶段),都使用显式LENGTH语句(如果存在)将内存分配给这些变量,否则回落到默认的33字节。

现在,真正有趣的是,PROC FCMP如此智能-在严格的初始化/运行阶段分隔内。如果在子例程的主体中,变量A具有明确定义的LENGTH,然后为另一个未初始化的变量B分配了函数A,则B为设置为与A相同的长度。考虑上述函数的这种修改,其中BAR的值不是直接分配给BAZ,而是通过第三个变量QUX,它具有明确定义的{{1} },共50个字节:

LENGTH

日志显示:

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;

这种“有帮助的”行为很可能是先前答案中造成混淆和差异的原因。我想知道是否记录了这种行为吗?

我将把它留给读者作为练习,以精确地研究如何智能SAS试图解决这一问题。例如,如果一个未初始化的变量被分配了另外两个具有明确分配的长度的串联值,那么它的长度是否设置为其他两个变量的总和?

答案 3 :(得分:1)

我最终退出了使用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;