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にアップグレードした後は正しく機能するようです-リリース2

4

4 に答える 4

3

問題は(理由はわかりませんが)スキャン関数にあると思います-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;
于 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

これは、DATAステップと同様に、PROCFCMPが実行時に可変長を動的に割り当てることができないという事実に関連している可能性があります。ただし、パラメータに可変長を動的に割り当てるため、少し混乱します。PROC FCMPサブルーチンには別の「初期化」フェーズがあり、その間に引数として渡される値の長さが決定され、それらの値を保持する必要のあるパラメーター変数が必要な長さに初期化されると想定しています。ただし、定義された変数の長さのみサブルーチンの本体内は、メモリがすでに割り当てられている実行時にのみ検出できます。したがって、実行前(コンパイル時または仮想の「初期化」フェーズ)に、メモリは明示的なLENGTHステートメントが存在する場合はこれらの変数に割り当てられ、それ以外の場合はデフォルトの33バイトにフォールバックします。

ここで本当に興味深いのは、PROC FCMPが、初期化/実行時の段階を厳密に分離した範囲内で、これについて可能な限り賢いことです。サブルーチンの本体で、変数に明示的に定義されたLENGTHがあり、次にA別の初期化されていない変数Bに関数が割り当てられている場合、はと同じ長さに設定されます。上記の関数のこの変更について考えてみます。ここでは、の値がに直接割り当てられるのではなく、明示的に50バイトが定義されている3番目の変数を介して割り当てられます。ABABARBAZQUXLENGTH

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

この「役立つ」行動が、以前の回答との混乱や違いの原因である可能性があります。この動作が文書化されているのだろうか?

スマートS​​ASがこれをどのように実現しようとしているのかを正確に調査するための演習として、読者に任せます。たとえば、初期化されていない変数に、明示的に割り当てられた長さを持つ他の2つの変数の連結値が割り当てられた場合、その長さは他の2つの変数の合計に設定されますか?

于 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 に答える