3

我通过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;

无论我对长度或attrib语句做什么来尝试显式声明返回的数据类型,它总是只返回最多 33 个字节的请求字符串,而不管实际返回值有多长。无论我正在搜索哪个属性,都会发生这种情况。数据步骤中的相同代码(硬编码)返回正确的结果,因此这与PROC FCMP.

这是我用来测试它的数据步骤(其中 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 - Release 2 后,这似乎可以正常工作

4

4 回答 4

3

我认为问题(虽然我不知道为什么)出在扫描函数中——它似乎正在截断来自 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;
于 2009-06-23T15:28:36.813 回答
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
*/
于 2009-08-20T18:09:07.847 回答
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 子例程有一个单独的“初始化”阶段,在此期间确定作为参数传递的值的长度,并将必须保存这些值的参数变量初始化为所需的长度。但是,只定义了变量的长度只有在运行时,当内存已经分配时,才能在子例程的主体中发现。因此,在运行之前(无论是在编译时还是我假设的“初始化”阶段),如果存在显式 LENGTH 语句,内存会分配给这些变量,否则会回退到默认的 33 字节。

现在真正有趣的是,PROC FCMP 在这方面非常聪明——在初始化/运行时阶段的严格分离中。如果在子例程的主体中,一个变量A有一个明确定义的 LENGTH,然后另一个未初始化的变量B被分配了一个函数A,则B设置为与 相同的长度A。考虑对上述函数的这种修改,其中的值BAR不是直接分配给BAZ,而是通过第三个变量QUX,它明确定义LENGTH了 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

这种“有用”的行为很可能是导致先前答案中的混淆和差异的原因。我想知道这种行为是否记录在案?

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

于 2019-08-16T07:34:22.243 回答
0

我最终放弃了使用 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;
于 2009-06-24T12:06:01.647 回答