2010-03-28 4 views
1

Je suis chargé de lire un fichier binaire mal formaté et de prendre en compte les variables. Bien que je doive le faire en C++ (ROOT, spécifiquement), j'ai décidé de le faire en python parce que python a du sens pour moi, mais mon plan est de le faire fonctionner en python et ensuite de réécrire en C++, l'utilisation de modules python faciles à utiliser ne m'amènera pas trop loin plus tard.Convertir des informations binaires en un type de données normal sans modules externes en python

En fait, je fais ceci:

In [5]: some_value 
Out[5]: '\x00I' 

In [6]: ''.join([str(ord(i)) for i in some_value]) 
Out[6]: '073' 

In [7]: int(''.join([str(ord(i)) for i in some_value])) 
Out[7]: 73 

Et je sais qu'il a être une meilleure façon. Qu'est-ce que tu penses?

EDIT:

Un peu d'informations sur le format binaire.

alt text http://grab.by/3njm alt text http://grab.by/3njv alt text http://grab.by/3nkL

C'est le test endian J'utilise:

# Read a uint32 for endianess 
endian_test = rq1_file.read(uint32) 
if endian_test == '\x04\x03\x02\x01': 
    print "Endian test: \\x04\\x03\\x02\\x01" 
    swapbits = True 
elif endian_test == '\x01\x02\x03\x04': 
    print "Endian test: \\x01\\x02\\x03\\x04" 
    swapbits = False 

Répondre

2

Votre int(''.join([str(ord(i)) for i in some_value])) ne fonctionne que lorsque tous les octets sauf le dernier octet sont nuls. Exemples:
'\x01I' doit être 1 * 256 + 73 == 329; vous obtenez 173
'\x01\x02' devrait être 1 * 256 + 2 == 258; vous obtenez 12
'\x01\x00' devrait être 1 * 256 + 0 == 256; vous obtenez 10

Il repose également sur l'hypothèse que les entiers sont stockés en mode bigendian; Avez-vous vérifié cette hypothèse? Êtes-vous sûr que '\x00I' représente l'entier 73, et non l'entier 73 * 256 + 0 == 18688 (ou quelque chose d'autre)? Laissez-nous vous aider à vérifier cette hypothèse en nous indiquant quelle marque et quel modèle d'ordinateur et quel système d'exploitation ont été utilisés pour créer les données.

Comment sont représentés les entiers négatifs?

Avez-vous besoin de composer numéros à virgule flottante?

L'exigence de l'écrire en C++ est-elle immuable? Que signifie "(ROOT, spécifiquement)"?

Si le seul diktat est le bon sens, l'ordre de préférence serait:

  1. Ecrire en Python à l'aide du module struct. Ecrivez-le en C++ mais utilisez des routines de bibliothèque C++ (en particulier si le point flottant est impliqué). Ne réinventez pas la roue. Lancez vos propres routines de conversion en C++. Vous pourriez snarf une copie de the C source for the Python struct module.

Mise à jour

Commentaires après les détails du format de fichier ont été affichés:

  1. Le marqueur boutisme est évidemment facultative, sauf au début d'un fichier. C'est douteux; il repose sur le fait que s'il n'y est pas, les 3ème et 4ème octets du bloc sont les 2 premiers octets de la chaîne d'en-tête, et ni '\x03\x04' ni '\x02\x01' ne peut valablement démarrer une chaîne d'en-tête. La chose intelligente à faire serait de lire SIX octets - si les 4 premiers sont le marqueur d'endian, les deux suivants sont la longueur de l'en-tête, et votre prochaine lecture est pour la chaîne d'en-tête; sinon chercher en arrière 4 octets puis lire la chaîne d'en-tête.

  2. Ce qui précède est dans la catégorie des nuisances. Les tailles négatives sont un réel souci, en ce sens qu'elles spécifient une longueur MAXIMALE, et il n'y a aucune mention de la façon dont la longueur RÉELLE est déterminée. Il dit "La taille réelle de l'entrée est alors donnée ligne par ligne". Comment? Il n'y a pas de documentation sur ce à quoi ressemble une "ligne de données". La description mentionne des "lignes" plusieurs fois; Ces lignes sont-elles terminées par un retour chariot et/ou un saut de ligne? Si oui, comment peut-on faire la différence entre un octet de saut de ligne et le premier octet de dire a uint16 qui appartient à la "ligne" de données actuelle? Si aucun saut de ligne ou autre, comment sait-on quand la ligne de données actuelle est terminée? Y a-t-il une taille d'uintNN devant chaque variable ou tranche de celle-ci? Ensuite, il est dit que (2) ci-dessus (taille négative) s'applique également à la chaîne d'en-tête. L'esprit boggle. Avez-vous des exemples (dans la documentation de la disposition du fichier, ou dans les fichiers réels) de "taille négative" de (a) en-tête de chaîne (b) de données "ligne"?

  3. Ce "format décidé" est-il publiquement disponible, par ex. documentation sur le web? Est-ce que le format a un nom consultable? Êtes-vous sûr que vous êtes la première personne au monde à vouloir lire ce format? La lecture de ce format de fichier, même avec une spécification complète, n'est pas un exercice trivial, même pour une personne expérimentée en format binaire qui a aussi de l'expérience avec Python (BTW n'a pas de float128). Combien d'heures-personnes avez-vous été affectées à cette tâche? Quelles sont les pénalités pour (a) retard (b) échec? Votre question initiale impliquait de fixer votre manière intéressante d'essayer d'analyser uint16 - faire beaucoup plus est en dehors de la portée/de l'intention de ce que sont les questions de SO.

+0

Merci pour la réponse John. Un test endian est fait et si besoin est, la valeur est octet permutée avant cet algorithme. Je ne pense pas qu'il y ait des ints négatifs, mais s'il y en a, je ne connais pas leur représentation. Il y a des nombres à virgule flottante. ROOT est un framework CERN utilisé pour l'analyse en High Energy Physics, écrit en C++. – physicsmichael

+0

J'analyse des données provenant d'un système d'acquisition de données distant, donc je ne peux pas vous en dire beaucoup sur le format ou l'ordinateur, ce qui fait partie du défi. Le lecteur que je fais mélange un peu de connaissance de ce que contient la sortie binaire, mais doit être capable de gérer une structure binaire variable en même temps. Fondamentalement, cette chose est une douleur et je suis sous qualifié. – physicsmichael

+0

@ vgm64: S'il vous plaît expliquer "un test endian est fait et si besoin [être]". Est fait par qui/quoi? Le DAQ met-il un marqueur d'ordre d'octets dans le (s) fichier (s)? Si vous testez l'ordre des octets de l'ordinateur de conversion, ne le faites pas; c'est une complication inutile. S'il vous plaît répondez aux questions sur l'endianisme littéralement. Nous devons savoir ce que vous savez, pas ce que vous faites. Veuillez répondre à la question "must-use-C++". Est-ce un travail ponctuel sur une collection de données? Si oui, ne pouvez-vous pas utiliser Python pour convertir les fichiers en une meilleure structure à lire par le logiciel C++ supposé existant? –

1

L'équivalent au module Python struct est un C struct et/ou union, donc avoir peur d'utiliser C'est idiot.

+0

OTOH avoir peur de l'ingénierie inverse C et Unions qui ont été tout simplement jetés dans un fichier ... –

2

Vous êtes essentiellement le calcul d'un « nombre en base 256 », qui est un polynôme, donc, par la méthode de Horner:

>>> v = 0 
>>> for c in someval: v = v * 256 + ord(c) 

Plus typique serait d'utiliser de bits des opérations équivalentes plutôt que arithmétique - l'équivalent de ce qui suit:

>>> v = 0 
>>> for c in someval: v = v << 8 | ord(c) 
0

Je ne suis pas sûr de savoir comment le format des données est que vous voulez extraire, mais peut-être mieux vous écrire juste quelques fonctions utilitaires génériques pour extraire les différents types de données vous avez besoin de:

def int1b(data, i): 
    return ord(data[i]) 

def int2b(data, i): 
    return (int1b(data, i) << 8) + int1b(data, i+1) 

def int4b(data, i): 
    return (int2b(data, i) << 16) + int2b(data, i+2) 

Avec ces fonctions, vous pouvez facilement extraire des valeurs à partir des données et ils peuvent aussi se traduire assez facilement à C.

+0

Travailler avec binaire est pas mon fort. Je vous serais reconnaissant si vous pouviez élaborer sur l'opération au niveau du bit que vous faites. Mais j'aime ça jusqu'à présent. – physicsmichael

+0

@ vgm64: Le '<<' est l'opérateur de décalage de gauche, 'x << 8' décale les bits dans l'entier' x' huit positions vers la gauche. Donc, si 'x' et' y' utilisent chacun huit bits, dans une valeur de 16 bits '(x << 8) + y' les huit bits les plus significatifs seront occupés par' x' et les huit bits restants seront mis à 'y'. – sth

+0

@ vgm64: Vous avez affaire à des données binaires brutes de toute façon. Prenez votre temps pour apprendre les opérations de bits. –

2
import struct 
result, = struct.unpack('>H', some_value) 
+0

+1 mais voir la réponse d'Ignacio Vazquez-Abrams. – MatrixFrog