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
简单起见,可参考该博客:
详细内容可参考python官方文档:3. 代码
I. C 代码:读取float所在的内存地址
/* *Filename: ftoc.c */ #define uchar unsigned char#define uint unsigned intvoid 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)
更加详细的介绍可参考:
- o : output,即要编译成目标文件的名字
fPIC: 地址无关代码(.so必须加此参数),详情可自行搜索
III. python 代码
#!/usr/bin/pythonimport osimport ctypeslib_name = './libftoc.so' #我自己的 c libfilename = "rd.txt"f1 = open(filename, 'r')f2 = open('result.txt', 'w+')#-----------------------------------#check the number is float or notdef 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. 运行结果
运行前的文档:运行后的结果输出: