1

ネストされたブラケット式内の区切り記号を無視しながら、区切り記号で文字列をトークン化する XQuery 関数を作成しようとしています。

tokenizeOutsideBrackets("1,(2,3)" , ",")         => ( "1" , "(2,3)" ) 
tokenizeOutsideBrackets("1,(2,(3,4))" , ",")     => ( "1" , "(2,(3,4))" )
tokenizeOutsideBrackets("1,(2,(3,(4,5)))" , ",") => ( "1" , "(2,(3,(4,5)))" )
tokenizeOutsideBrackets("1,(2,(3,4),5),6" , ",") => ( "1" , "(2,(3,4),5)" , "6" )

再帰的な正規表現または命令型言語があれば、これはかなり些細なことですが、XQuery でこれを行う簡単で簡単な方法を見つけるのに苦労しています。

ありがとう!

4

3 に答える 3

1

この XQuery 式:

tokenize(replace('1,(2,(3,4),5),6','([0123456789]+|\(.*\))(,)?','$1;'),';')

出力:

1 (2,(3,4),5) 6

更新: のような文字列がある場合は'1,(2,3),(4,5),6'、この文法のパーサーが必要になります。

exp ::= term ( ',' term ) *

term ::= num | '(' exp ')'

num ::= ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ) +
于 2011-04-05T15:36:12.670 に答える
0

これを行う 1 つの方法は、最初に分割してから、バランスの取れていない括弧を使用してトークンを右側の隣接トークンに結合することです。

以下のコードは、目的の結果を取得します。fn:tokenize を使用して分割し、(末尾) 再帰的に結果トークンを処理し、前のトークンに一致しないカウント "(" と ")" がある場合は連結します。このアプローチにはいくつかの欠陥があります。つまり、左右の括弧が適切に一致しないことと、$delimiter をパターンとリテラルの両方として扱うことです。適切に処理するには、さらにコーディングが必要ですが、アイデアは得られるかもしれません。

declare function local:tokenizeOutsideBrackets($string, $delimiter)
{
  local:joinBrackets(tokenize($string, $delimiter), $delimiter, ())
};

declare function local:joinBrackets($tokens, $delimiter, $result)
{
  if (empty($tokens)) then
    $result
  else 
    let $last := $result[last()]
    let $new-result :=
      if (string-length(translate($last, "(", "")) 
        = string-length(translate($last, ")", ""))) then
       ($result, $tokens[1])
      else
       ($result[position() < last()], concat($last, $delimiter, $tokens[1]))
    return local:joinBrackets($tokens[position() > 1], $delimiter, $new-result)
};
于 2011-04-05T11:41:24.393 に答える
0

遊んでいて、以下の機能が機能しているようですが、もっと簡単な方法があると思わずにはいられません。

このコードは、 functx:index-of-string 関数を使用して、すべての区切り記号のインデックスを見つけます。次に、左側のすべてに同じ数の開きかっこと閉じかっこがある最初の区切り文字をそれぞれが見つけようとします。これが見つかった後、この区切り文字の右側にあるすべてのものでこれが繰り返されます。

declare function local:tokenizeOutsideBrackets(
  $arg as xs:string?,
  $delimiter as xs:string) as xs:string*
{
  if (contains($arg, $delimiter))
  then
    (:find positions of all the delimiters:)
    let $delimiterPositions := (
      functx:index-of-string($arg,$delimiter),
      string-length($arg)+1 (:Add in end of string too:)
    )

    (:strip out all the fragments that have matching
      brackets to the left of each delimiter:)
    let $fragments :=
      for $endPos in $delimiterPositions
      let $candidateString := substring($arg,1,$endPos - 1)
      return
        if (local:hasMatchedBrackets($candidateString))
        then $candidateString
        else ()
    let $firstFragment := $fragments[1]
    let $endPos := string-length($firstFragment)

    (:recursively return the first matching fragment,
      plus the fragments in the remaining string:)
    return
    (
      $firstFragment,
      local:tokenizeOutsideBrackets(
        substring(
          $arg,
          $endPos+string-length($delimiter)+1,
          string-length($arg) - $endPos -(string-length($delimiter))
        ),
        $delimiter
      )
    )
  else if ($arg='') then () else ($arg)
};

declare function local:hasMatchedBrackets($arg as xs:string) as xs:boolean 
{
  count(tokenize($arg,'\(')) = count(tokenize($arg,'\)'))
};
于 2011-04-05T16:27:02.787 に答える