2010-04-21 10 views
0

Au travail aujourd'hui, nous avons jeté ensemble cette tentative:Comment convertir un entier 8 bits en une chaîne binaire dans xquery?

xquery version "1.0"; 
declare option saxon:output    "omit-xml-declaration=yes"; 
declare variable $x := 99; 

string-join(
    for $b in (128,64,32,16,8,4,2,1) 
    let $xm := $x mod ($b*2) 
    return 
     if ($xm >= $b) then "1" else "0" 
, "") 

Avez-vous une meilleure façon?

Prenant la réponse d'Oliver, j'ai fait la fonction inverse.

declare function local:bin-byte($x as xs:string) as xs:unsignedByte 
{ 
    let $binary-nibbles := ("0000", "0001", "0010", "0011", 
          "0100", "0101", "0110", "0111", 
          "1000", "1001", "1010", "1011", 
          "1100", "1101", "1110", "1111") 
    return xs:unsignedByte(
    (index-of($binary-nibbles, substring($x,1,4))-1) * 16 
    + (index-of($binary-nibbles, substring($x,5,4))-1) 
    ) 
}; 

Répondre

2

Comme une note mineure, si vous texte retournerez, pas XML, alors vous êtes probablement mieux que la mise method=text plutôt omit-xml-declaration=yes, bien que dans ce cas, il ne fait aucune différence.

Une solution alternative est d'utiliser une table de consultation:

declare function local:binary($x as xs:unsignedByte) as xs:string 
{ 
    let $binary-nibbles := ("0000", "0001", "0010", "0011", 
          "0100", "0101", "0110", "0111", 
          "1000", "1001", "1010", "1011", 
          "1100", "1101", "1110", "1111") 
    return concat($binary-nibbles[$x idiv 16 + 1], 
       $binary-nibbles[$x mod 16 + 1]) 
}; 
+0

Nous avons réfléchi à ce sujet, mais je n'ai pas essayé. Magnifiquement simple et rapide aussi je soupçonne (ce qui pourrait être important pour nous). Notre sortie pour notre application sera XML ou texte. Dans ce cas, il s'agit probablement de XML. – philcolbourn

+0

devrait xs: byte être xs: unsignedByte? – philcolbourn

+0

vous avez raison; J'ai fait ce changement –

1

fonctions récursives sont claires, si plus lente:

declare function local:decimal-to-binary ($d as xs:integer) as xs:string { 
if ($d > 0) 
then concat(local:decimal-to-binary(floor($d div 2)),$d mod 2) 
else "" 
}; 

par exemple

local:decimal-to-binary(42) 

avec inverse:

declare function local:binary-to-decimal($b as xs:string) as xs:integer { 
if ($b ne "") 
then local:binary-to-decimal(substring($b, 1, string-length($b)- 1)) * 2 
     + number(substring ($b, string-length($b),1)) 
else 0 

};

locale: binaire à décimal (local: décimal à binaire (42))

+0

J'aime la récursivité. J'ai dû envelopper la fonction floor() dans un xs: integer() pour que saxonb (?) Xquery l'exécute. J'ai aussi dû changer de numéro en xs: entier. Ce serait bien si vous vouliez seulement la représentation binaire de longueur minimale du nombre. – philcolbourn

+0

J'ai testé le code dans le sandbox eXist http://demo.exist-db.org/exist/sandbox/sandbox.xql - saxon doit être plus stricte - pourrait simplement dire xs: integer ($ d div 2). Pour obtenir une chaîne de longueur fixe, utilisez le pavé de chaînes: déclarez la fonction local: decimal-to-binary ($ d comme xs: integer, $ length comme xs: integer) { let $ binary: = local: decimal -to-binary ($ d) retour concat (string-pad ("0", $ longueur - string-length ($ binaire)), $ binaire) }; local: decimal-to-binary (42,8) –

+0

La fonction ci-dessus est valide XQuery - les règles de conversion de l'argument de fonction doivent signifier que les arguments sont transtypés en xs: entiers (car c'est ce que l'argument attend). Je suis surpris que saxon n'a pas aimé. –

1

La façon la plus efficace que je peux penser à faire l'inverse (au moins en XQSharp) est:

declare function local:binary-string-to-integer($binary as xs:string) 
        as xs:integer 
{ 
    local:binary-codepoints-to-integer(string-to-codepoints($binary), 1, 0) 
}; 

declare function local:binary-codepoints-to-integer(
        $codepoints as xs:integer*, 
        $current-index as xs:integer, 
        $result as xs:integer) 
        as xs:integer 
{ 
    let $current-codepoint := $codepoints[$current-index] 
    return 
    if (empty($current-codepoint)) 
    then $result 
    else local:binary-codepoints-to-integer(
      $codepoints, 
      $current-index + 1, 
      2 * $result + $current-codepoint - string-to-codepoints("0")) 
}; 

Un test de performance rapide montre que les deux méthodes fonctionnent à peu près de la même manière lorsque la requête est interprétée, mais cette méthode est environ 50% plus rapide lorsque la requête est compilée. La méthode de la fonction récursive a également l'avantage de ne pas être limitée aux octets non signés.

Dans les deux cas, le temps d'exécution est d'environ 10 microsecondes, donc il n'y a rien à craindre.

Questions connexes