1. Fonction Linux xxd -i
La commande Linux system xxd utilise le format binaire ou hexadécimal pour afficher le contenu du fichier. Si le paramètre outfile n'est pas spécifié, les résultats sont affichés sur l'écran du terminal ; sinon, les résultats sont sortis vers outfile. Pour une utilisation détaillée, veuillez vous référer à la commande Linux xxd.
Cet article se concentre principalement sur l'option -i de la commande xxd. Utilisez cette option pour générer une définition de tableau en langage C nommée inputfile. Par exemple, après avoir exécuté les commandes echo 12345 > test et xxd -i test, le résultat est :
unsigned char test[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x0a }; unsigned int test_len = 6;
Visible, le nom du tableau est le nom du fichier d'entrée (s'il y a un nom de suffixe, le point est remplacé par un trait de soulignement). Notez que 0x0a représente le caractère de nouvelle ligne LF, qui est « n ».
2. Utilisations courantes de xxd -i
Lorsque l'appareil ne dispose pas de système de fichiers ou ne prend pas en charge la mémoire dynamique gestion, le contenu des fichiers binaires (tels que le chargeur de démarrage et le micrologiciel) est parfois stocké dans des tableaux statiques de code C. À ce stade, le tableau des versions peut être généré automatiquement à l’aide de la commande xxd. Les exemples sont les suivants :
1) Utilisez la commande Linux xdd pour convertir le fichier binaire VdslBooter.bin en fichier hexadécimal DslBooter.txt :
xxd -i < DslBooter.txt
Parmi eux, l'option '-i' indique que la sortie est dans le style de fichier include C (mode tableau). Le symbole de redirection '<' redirige le contenu du fichier VdslBooter.bin vers l'entrée standard. Ce processus peut éliminer les déclarations de tableau et les définitions de variables de longueur, de sorte que la sortie ne contienne que des valeurs hexadécimales.
2) Définir le tableau statique correspondant dans le fichier source du code C :
static const uint8 bootImageArray[] = { #include " ../../DslBooter.txt" }; TargetImage bootImage = { (uint8 *) bootImageArray, sizeof(bootImageArray) / sizeof(bootImageArray[0]) };
Lors de la compilation du code source, le DslBooter. txt fichier Le contenu se développera automatiquement dans le tableau ci-dessus. En utilisant intelligemment la directive de prétraitement #include, vous pouvez éviter d'avoir à copier manuellement le contenu du tableau.
3. Implémentation Python des fonctions de type xxd -i
Cette section utilisera le langage Python2.7 pour implémenter xxd - j'aime les fonctions fonction.
Étant donné que l'auteur est en phase d'apprentissage, de nombreux endroits dans le code sont écrits différemment mais ont des fonctions identiques ou similaires. Nous visons à fournir des références de syntaxe différentes.
Tout d'abord, veuillez jeter un œil à un programme court mais complet (enregistrer sous xddi.py) :
#!/usr/bin/python #coding=utf-8 #判断是否C语言关键字 CKeywords = ("auto", "break", "case", "char", "const", "continue", "default", "do","double","else","enum","extern","float","for", "goto","if","int","long","register","return","short", "signed","static","sizeof","struct","switch","typedef","union", "unsigned","void","volatile","while", "_Bool") #_Bool为C99新关键字 def IsCKeywords(name): for x in CKeywords: if cmp(x, name) == 0: return True return False if __name__ == '__main__': print IsCKeywords('const') #Xxdi()
Ceci le code détermine si la chaîne spécifiée est un mot-clé du langage C. Entrez E:PyTest>python xxdi.py à l'invite de commande cmd du système Windows et le résultat de l'exécution est True.
L'extrait de code suivant omettra les déclarations de script et d'encodage en tête, ainsi que la section « principale » à la fin.
Avant de générer un tableau C, assurez-vous que le nom du tableau est légal. Les identifiants du langage C ne peuvent être constitués que de lettres, de chiffres et de traits de soulignement, et ne peuvent pas commencer par un chiffre. De plus, les mots-clés ne peuvent pas être utilisés comme identifiants. Tous les caractères illégaux doivent être traités. Pour les règles, veuillez vous référer aux commentaires du code :
import re def GenerateCArrayName(inFile): #字母数字下划线以外的字符均转为下划线 #'int $=5;'的定义在Gcc 4.1.2可编译通过,但此处仍视为非法标识符 inFile = re.sub('[^0-9a-zA-Z\_]', '_', inFile) #'_'改为''可剔除非法字符 #数字开头加双下划线 if inFile[0].isdigit() == True: inFile = '__' + inFile #若输入文件名为C语言关键字,则将其大写并加下划线后缀作为数组名 #不能仅仅大写或加下划线前,否则易于用户自定义名冲突 if IsCKeywords(inFile) is True: inFile = '%s_' %inFile.upper() return inFile
pour imprimer GenerateCArrayName('1a$if1#. 1_4.txt') Une fois exécutée, la chaîne du paramètre d'entrée sera convertie en __1a_if1_1_4_txt. De même, _Bool est converti en _BOOL_.
Afin de simuler autant que possible le style de commande Linux, des options et des paramètres de ligne de commande doivent être fournis. Le module d'analyse utilise optionparser. Pour plus de détails sur son utilisation, voir Analyse de la ligne de commande Python. L'implémentation en ligne de commande de la fonction xxd -i-like est la suivante :
#def ParseOption(base, cols, strip, inFile, outFile): def ParseOption(base = 16, cols = 12, strip = False, inFile = '', outFile = None): from optparse import OptionParser custUsage = '\n xxdi(.py) [options] inFile [outFile]' parser = OptionParser(usage=custUsage) parser.add_option('-b', '--base', dest='base', help='represent values according to BASE(default:16)') parser.add_option('-c', '--column', dest='col', help='COL octets per line(default:12)') parser.add_option('-s', '--strip', action='store_true', dest='strip', help='only output C array elements') (options, args) = parser.parse_args() if options.base is not None: base = int(options.base) if options.col is not None: cols = int(options.col) if options.strip is not None: strip = True if len(args) == 0: print 'No argument, at least one(inFile)!\nUsage:%s' %custUsage if len(args) >= 1: inFile = args[0] if len(args) >= 2: outFile = args[1] return ([base, cols, strip], [inFile, outFile])
Le def ParseOption(...) commenté était initialement appelé de la manière suivante :
base = 16; cols = 12; strip = False; inFile = ''; outFile = '' ([base, cols, strip], [inFile, outFile]) = ParseOption(base, cols, strip, inFile, outFile)
L'intention est de modifier la base, les cols, la bande et les autres valeurs de paramètres en même temps. Mais cette façon d'écrire est très délicate. Utilisez plutôt la méthode de définition de fonction avec les paramètres par défaut. Vous n'avez besoin d'écrire ParseOption() que lors de l'appel. Si les lecteurs connaissent une meilleure façon de l’écrire, n’hésitez pas à m’éclairer.
Utilisez l'option -h pour appeler l'invite de commande, qui est très proche du style Linux :
E:\PyTest>python xxdi.py -h Usage: xxdi(.py) [options] inFile [outFile] Options: -h, --help show this help message and exit -b BASE, --base=BASE represent values according to BASE(default:16) -c COL, --column=COL COL octets per line(default:12) -s, --strip only output C array elements
Basé sur les exercices ci-dessus, puis complétez l'article Points forts :
def Xxdi(): #解析命令行选项及参数 ([base, cols, strip], [inFile, outFile]) = ParseOption() import os if os.path.isfile(inFile) is False: print ''''%s' is not a file!''' %inFile return with open(inFile, 'rb') as file: #必须以'b'模式访问二进制文件 #file = open(inFile, 'rb') #Python2.5以下版本不支持with...as语法 #if True: #不用for line in file或readline(s),以免遇'0x0a'换行 content = file.read() #将文件内容"打散"为字节数组 if base is 16: #Hexadecimal content = map(lambda x: hex(ord(x)), content) elif base is 10: #Decimal content = map(lambda x: str(ord(x)), content) elif base is 8: #Octal content = map(lambda x: oct(ord(x)), content) else: print '[%s]: Invalid base or radix for C language!' %base return #构造数组定义头及长度变量 cArrayName = GenerateCArrayName(inFile) if strip is False: cArrayHeader = 'unsigned char %s[] = {' %cArrayName else: cArrayHeader = '' cArrayTailer = '};\nunsigned int %s_len = %d;' %(cArrayName, len(content)) if strip is True: cArrayTailer = '' #print会在每行输出后自动换行 if outFile is None: print cArrayHeader for i in range(0, len(content), cols): line = ', '.join(content[i:i+cols]) print ' ' + line + ',' print cArrayTailer return with open(outFile, 'w') as file: #file = open(outFile, 'w') #Python2.5以下版本不支持with...as语法 #if True: file.write(cArrayHeader + '\n') for i in range(0, len(content), cols): line = reduce(lambda x,y: ', '.join([x,y]), content[i:i+cols]) file.write(' %s,\n' %line) file.flush() file.write(cArrayTailer)
Les versions inférieures à Python2.5 ne supportent pas la syntaxe with...as, et Linux Le système utilisé par l'auteur pour le débogage a uniquement Python2.4.3 installé. Par conséquent, pour exécuter xddi.py dans un système Linux, vous ne pouvez écrire que file = open(.... Mais cela nécessite de gérer la fermeture et l'exception du fichier. Pour plus de détails, voir Comprendre le with...as... syntaxe en Python. Notez que Python2. Lorsque vous utilisez la syntaxe with...as dans 5, vous devez déclarer from __future__ import with_statement
pour obtenir le numéro de version de Python via platform.python_version() Par exemple. :
import platform #判断Python是否为major.minor及以上版本 def IsForwardPyVersion(major, minor): #python_version()返回'major.minor.patchlevel',如'2.7.11' ver = platform.python_version().split('.') if int(ver[0]) >= major and int(ver[1]) >= minor: return True return False
E:\PyTest>python xxdi.py -c 5 -b 2 -s 123456789ABCDEF.txt [2]: Invalid base or radix for C language! E:\Pytest>python xxdi.py -c 5 -b 10 -s 123456789ABCDEF.txt 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, E:\PyTest>python xxdi.py -c 5 -b 10 123456789ABCDEF.txt unsigned char __123456789ABCDEF_txt[] = { 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, }; unsigned int __123456789ABCDEF_txt_len = 15; E:\PyTest>python xxdi.py -c 5 -b 8 123456789ABCDEF.txt unsigned char __123456789ABCDEF_txt[] = { 061, 062, 063, 064, 065, 066, 067, 070, 071, 0101, 0102, 0103, 0104, 0105, 0106, }; unsigned int __123456789ABCDEF_txt_len = 15; E:\PyTest>python xxdi.py 123456789ABCDEF.txt unsigned char __123456789ABCDEF_txt[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, }; unsigned int __123456789ABCDEF_txt_len = 15;
unsigned char VdslBooter_bin[] = { 0xff, 0x31, 0x0, 0xb, 0xff, 0x3, 0x1f, 0x5a, 0x0, 0x0, 0x0, 0x0, //... ... ... ... 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }; unsigned int VdslBooter_bin_len = 53588;
En résumé, on constate que le module xxdi implémenté par l'auteur est très proche des fonctions de Linux xxd -i, et chacun a ses propres avantages et inconvénients. L'avantage de xxdi est qu'il a une vérification plus complète de la validité des noms de tableaux (vérification des mots clés) et que l'expression du contenu du tableau est plus riche (octale et décimale). L'inconvénient est qu'il ne prend pas en charge la redirection et la largeur des valeurs ; n'est pas corrigé (comme 0xb et 0xff). Bien entendu, ces défauts ne sont pas difficiles à éliminer. Par exemple, utilisez '0x x'%val au lieu de hex(val) pour contrôler la largeur de bit de sortie. Cependant, des améliorations supplémentaires augmenteront inévitablement la complexité du code, ce qui peut entraîner une réduction de moitié de l'effort.
Ce qui précède est l'implémentation Python de la fonction Linux command xxd -i introduite par l'éditeur. J'espère qu'elle sera utile à tout le monde !
Pour en savoir plus sur l'implémentation Python de la commande Linux xxd -i, introduction à la fonction et articles connexes, veuillez faire attention au site Web PHP chinois !