1. Linux xxd -i 関数
Linux システムの xxd コマンドは、ファイルの内容をバイナリまたは 16 進形式で表示します。 outfile パラメーターが指定されていない場合、結果は端末画面に表示されます。それ以外の場合、結果は outfile に出力されます。詳しい使い方はlinuxコマンドxxdを参照してください。
この記事では主に xxd コマンドの -i オプションに焦点を当てます。このオプションを使用して、inputfile という名前の C 言語配列定義を出力します。たとえば、 echo 12345 > test および xxd -i test コマンドを実行すると、出力は次のようになります:
unsigned char test[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x0a }; unsigned int test_len = 6;
表示され、配列名は入力ファイル名になります (サフィックスがある場合は、ドットが置き換えられます)アンダースコア付き)。 0x0a は改行文字 LF、つまり「n」を表すことに注意してください。
II. xxd -i
の一般的な使用法 デバイスにファイル システムがない場合、または動的メモリ管理をサポートしていない場合、バイナリ ファイル (ブートローダーやファームウェアなど) の内容が保存されることがあります。 C コードの静的配列。現時点では、xxd コマンドを使用してバージョン配列を自動的に生成できます。例:
1) Linux コマンド xdd を使用して、バイナリ ファイル VdslBooter.bin を 16 進ファイル DslBooter.txt に変換します。
xxd -i < VdslBooter.bin > ここで、 '- i ' オプションは、出力が C インクルード ファイル スタイル (配列モード) であることを示します。リダイレクト記号「<」は、VdslBooter.bin ファイルの内容を標準入力にリダイレクトします。このプロセスにより、配列宣言と長さ変数の定義が削除され、出力には 16 進値のみが含まれます。
static const uint8 bootImageArray[] = { #include " ../../DslBooter.txt" }; TargetImage bootImage = { (uint8 *) bootImageArray, sizeof(bootImageArray) / sizeof(bootImageArray[0]) };
このセクションでは、Python2.7 言語を使用して xxd -i のような関数を実装します。
#!/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()
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
#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])
base = 16; cols = 12; strip = False; inFile = ''; outFile = '' ([base, cols, strip], [inFile, outFile]) = ParseOption(base, cols, strip, inFile, outFile)
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
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)
Python 2.5 より前のバージョンは with..as 構文をサポートしておらず、作者がデバッグに使用している Linux システムには Python2.4.3 のみがインストールされています。したがって、Linux システムで xddi.py を実行するには、 file = open(.... と記述するだけです。ただし、これには、ファイルのクローズと例外の処理が必要です。詳細については、「with...as... の理解」を参照してください。 Python の構文。5 で with...as 構文を使用する場合は、__future__ import with_statement を宣言する必要があります。例: 。
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;
例として、少し大きいセカンダリ ファイルを使用して、python xxdi.py を実行します。 VdslBooter.bin booter.c の後に、booter.c ファイルの内容は次のようになります (最初と最後の部分は切り詰められています):
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;
要約すると、作者が実装した xxdi モジュールは Linux xxd -i の機能に非常に近く、それぞれに独自の長所と短所があることがわかります。 xxdi の利点は、配列名の有効性をより完全に検証できること (キーワード チェック)、配列の内容の表現が豊富であること (8 進数と 10 進数)、欠点はリダイレクトと値の幅をサポートしていないことです。固定ではありません (0xb や 0xff など)。もちろん、これらの欠点を解消するのは難しいことではありません。たとえば、出力ビット幅を制御するには、hex(val) の代わりに '0x%02x'%val を使用します。ただし、追加の改善によりコードの複雑さは必然的に増加し、その結果、労力が半分で済む可能性があります。
上記は編集者が紹介したLinuxコマンドxxd -i関数のPython実装です。皆さんの参考になれば幸いです。
Linux コマンド xxd -i 関数の Python 実装の紹介に関するその他の関連記事については、PHP 中国語 Web サイトに注目してください。