C语言读取Java的字节码文件
最近学习C语言,想用C语言写一个字节码解析器,而需要解析字节码文件,首先需要了解C语言是怎么以二进制形式读取文件的。
一、先上示例代码
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* file;
int i;
char* filepath = "/home/csdn/IdeaProjects/ziyu-learn-jvm-java/target/classes/com/ziyu/example/HelloWorld.class";
long filesize;
char* buffer;
if (NULL == (file= fopen(filepath, "rb+"))) //以返回值fpl判断是否打开成功,如果为NULL表示失败
{
printf ("Failed to open the file !\n");
exit (1) ; //终止程序,stdlib .h 头文件中
}
printf("Succeed to open the file.\n");
// 统计文件字节数
fseek(file, 0, SEEK_END);
filesize = ftell(file);
printf("File size is %ld.\n", filesize);
// 申请内存
buffer = calloc(filesize, sizeof(char));
// 读取文件到内存
fseek(file, 0, SEEK_SET);
fread(buffer, sizeof(char), filesize, file);
//在屏幕上显示数组buffer的字节
unsigned char elem;// 声明为无符号字符,这样前四个字节不会被打印为0xffffffca这样的格式
for (i=0; i < filesize; i++) {
elem = buffer[i];
printf("%02x ", elem);
if (i > 0 && i % 64 == 0) {
printf("\n");
}
}
fclose(file);
return 0;
}
二、代码解析
2.1 stdio.h
读取和写入文件,以及从控制台获取数据和用控制台打印信息,都需要先引入 stdio.h 这个头文件:
#include <stdio.h>
所以,以下函数都来自该头文件:
- fopen:C语言打开文件详解
- fclose:关闭文件
- fread:C语言以数据块的形式读写文件
- ftell:C 库函数 - ftell()
- fseek: C 库函数 - fseek()
2.2 stdlib.h
在这个例子中,之所以要引用 C语言标准库,主要是因为用到了 exit
函数。这个函数就是来自于 stdlib.h
头文件。
2.3 fopen
它的用法为:
FILE *fopen(char *filename, char *mode);
- 函数的第一个参数
filename
为文件名(包括文件路径); - 函数的第二个参数
mode
为打开方式;
最基本的文件打开方式有以下几种:
打开方式 | 描述 | 如果文件不存在时 | 如果文件存在时 |
---|---|---|---|
"r" | 以“只读”方式打开文件 | 打开失败 | 打开成功,但是只允许读取,不允许写入 |
"w" | 以“写入”方式打开文件 | 创建一个新文件 | 清空原有的文件内容,写入的数据从头开始加入文件 |
"a" | 以“追加”方式打开文件 | 创建一个新文件 | 保留原有的文件内容,写入的数据追加到文件的末尾 |
如果加上 +
之后呢?
打开方式 | 变化 | 如果文件不存在时 | 如果文件已存在时 |
---|---|---|---|
"r+" | 支持写入 | 打开失败 | 打开成功,允许读取和写入 |
"w+" | 支持读取 | 创建一个新文件 | 清空原有的文件内容,写入的数据从头开始加入文件 |
"a+" | 支持读取 | 创建一个新文件 | 保留原有的文件内容,写入的数据追加到文件的末尾 |
观察对比之后发现:
- 当文件存在或者不存在时,
"r+"/"w+"/"a+"
的处理方式和对应的"r"/"w"/"a"
保持一致; +
相当于让"r"/"w"/"a"
同时支持读和写;
fopen 返回 NULL 时,就表示文件打开失败了。
控制读写方式的字符串(可以不写)
打开方式 | 说明 |
---|---|
"t" | 文本文件。如果不写,默认为"t"。 |
"b" | 二进制文件。 |
调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为"t")。
2.4 fread
fread() 函数用来从指定文件中读取块数据。
fread() 的原型为:
size_t fread ( void *dstBuf, size_t elemSize, size_t count, FILE *fp );
我们来分析一下四个参数:
参数名称 | 说明 | 描述 |
---|---|---|
dstBuf | 目标内存区块的指针 | 它可以是数组、变量、结构体等。fread() 中的 dstBuf 用来存放读取到的数据 |
elemSize | 表示每个数据块的字节数 | 单位大小。如果dstBuf是数组,elemSize就是数组元素的字节数 |
count | 表示要读写的数据块的块数 | 数量 |
fp | 表示文件指针 | 就是 fopen 打开的文件指针 |
2.5 fclose
文件一旦使用完毕,应该用 fclose() 函数把文件关闭,以释放相关资源,避免数据丢失。fclose() 的用法为:
int fclose(FILE *fp);
- 函数参数
fp
为文件指针 - 文件正常关闭时,fclose() 的返回值为0,如果返回非零值则表示有错误发生。
所以正常的使用应该遵循:
// 打开文件
FILE* fp = fopen(...);
// 读取或写入文件
fread(fp);
fwrite(..., fp);
// 关闭文件
fclose(fp);
2.6 ftell
返回给定流 stream 的当前文件位置。
2.7 fseek
设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
int fseek(FILE *stream, long int offset, int whence)
参数 | 说明 | 备注 |
---|---|---|
stream | 这是指向 FILE 对象的指针,该 FILE 对象标识了流。 | fopen函数得到的对象 |
offset | 这是相对 whence 的偏移量,以字节为单位。 | |
whence | 这是表示开始添加偏移 offset 的位置。 |
whence 一般指定为下列常量之一:
常量 | 描述 |
---|---|
SEEK_SET | 文件的开头 |
SEEK_CUR | 文件指针的当前位置 |
SEEK_END | 文件的末尾 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)