1. 問題引出
最近遇到了一個小問題,即:
讀取文本文件的內容,然后將文件中出現的數字(包括double, int, float等)轉化為16進制0x存儲
原本以為非常簡單的內容,然后就着手去寫了python,但是寫着寫着發現不對:
python貌似沒辦法直接讀取內存數據;
因此不得不借助於C語言,這樣又引出了python如何調用C lib
開始寫c發現又有問題了:
int 類型的數據和float/double數據在內存中的存儲方式是不同的
因此花了一些力氣解決了這些問題,成功得將數字轉化為了16進制0x的存儲類型,特記錄一下,以備后續查詢,也可以讓有需要的童鞋有個參考。
2. 基本知識
完成本實驗前,你必須具備以下的基礎知識:
1). float/double在內存中的存儲方式
浮點數在內存中的存儲形式為二進制的科學計數法,即


其中,S為符號,P為階碼,F為尾數
其長度如下表所示:
| 總長度 | 符號 | 階碼 | 尾數 | |
|---|---|---|---|---|
| float | 32 bit | 1 | 8 | 23 |
| double | 64 bit | 1 | 11 | 52 |
> **符號位 S:** 0代表正數,1代表負數 > **階碼位 P:** 為unsigned, 計算時候,需要將實際尾數減去`7F`, 即實際計算用的`P=eb-0x7F` > **尾數位 F:** 用二進制科學計算法表示后,去掉前面的恆定`1`,只保留小數點后的二進制數據
例1:32bit二進制 0x42 0xOA 0x1A 0xA0 轉化為十進制浮點數
符號位:S=0,0x42的最高位為0
階碼位:0x42<<1=0x84, 0x84-0x7F = 5
尾數位:0x0A1AA0為換算為十進制然后小數點前加1得到1.0789375
計算:1.0789375*2^5 = 34.526
例2:將十進制數50.265轉化為32位規格化的浮點數
N = 50.265
S = 0
N/2^P = 1.xxx, 因此,P=5
F=N/2^P=50.265/32=1.57078125
由以上可知:
符號位S=0
eb = P+0x7F=0x84
尾數d[2]d[1]d[0]= 0x490F5C
因此,最終結果為:0x42490F5C (記住eb需要移位哦)
2). python如何調用C lib
簡單起見,可參考該博客:http://blog.csdn.net/golden1314521/article/details/44055523
詳細內容可參考python官方文檔:https://docs.python.org/2/library/ctypes.html
3. 代碼
I. C 代碼:讀取float所在的內存地址
/*
*Filename: ftoc.c
*/
#define uchar unsigned char
#define uint unsigned int
void ftoc(float fl, uchar arr[])
{
void *pf;
pf = &fl;
uchar i;
for(i=0; i<4; i++)
{
arr[i] = *((uchar *)pf+i);
}
return ;
}
II. 編譯此代碼為libftoc.so
gcc -shared -Wl,-soname,libftoc -o libftoc.so -fPIC ftoc.c
- shared: 表示需要編譯為動態庫(.so)
- Wl: 告訴編譯器將后面的參數傳遞給鏈接器
- soname: 指定了動態庫的soname(簡單共享名,Short for shared object name)
更加詳細的介紹可參考:http://blog.csdn.net/zhoujiaxq/article/details/10213655
- o : output,即要編譯成目標文件的名字
- fPIC: 地址無關代碼(.so必須加此參數),詳情可自行搜索
III. python 代碼
#!/usr/bin/python
import os
import ctypes
lib_name = './libftoc.so' #我自己的 c lib
filename = "rd.txt"
f1 = open(filename, 'r')
f2 = open('result.txt', 'w+')
#-----------------------------------
#check the number is float or not
def is_float(s):
try:
float(s)
return True
except ValueError:
pass
#-----------------------------------
def ftoc(num):
number = ctypes.c_float(num) #covert the python type to c type
arr = ctypes.c_ubyte * 4
parr = arr(ctypes.c_ubyte(), ctypes.c_ubyte(), ctypes.c_ubyte(), ctypes.c_ubyte()) #create a c-type(unsigned char) array
#access the c lib
lib_ftoc = ctypes.CDLL(lib_name)
#call the c lib function!!!
#after this function, parr contains float's dec_number*4
lib_ftoc.ftoc(ctypes.c_float(num), parr)
lst=[]
for i in range(4):
lst.append(parr[i])
lst[i] = hex(lst[i])[2:] #get rid of '0x'
if(len(lst[i]) < 2):
lst[i] = '0'+lst[i] #make data 8-bit
string = lst[3]+lst[2]+lst[1]+lst[0]
string = string.upper() #uppercase the characters
return string
#============================================
# main flow
#===========================================
lst = []
line = f1.readline()
while line:
line.strip('\n')
lst = line.split()
for i in range(len(lst)):
#if the number is digit
if lst[i].isdigit():
lst[i] = hex(int(lst[i]))
lst[i] = lst[i][2:] #get rid of '0x'
lst[i] = lst[i].upper()
if(len(lst[i]) < 8):
lst[i] = '0'*(8-len(lst[i])) + lst[i]
#if the number is float
else:
if is_float(lst[i]):
lst[i] = ftoc(float(lst[i]))
for i in range(len(lst)):
f2.write(lst[i])
f2.write(' ')
f2.write('\n')
line = f1.readline()
f2.write('\n')
f1.close()
f2.close()
VI. 運行結果
運行前的文檔:

運行后的結果輸出:

