文件切割和文件合并
文件切割思想:
首先计算出一个文件的大小,根据需要切割的份数计算出每份的大小,假如文件的长度为size,需要分割成n份。那么会有2种情况:
- 当size能被n整除时,即size%n = 0,每份的大小为:size/n
- 当size不能被n整除时,那么我们计算的思想是这样的,首先让前面(n-1)份进行均分,每份大小为size/(n-1),剩下的没分割玩的留给最后一份,最后一份的大小即size%(n-1),也即size-size*(n-1)
说了这么多废话,还是有点绕,不是很好理解,举个例子帮助理解一下:
比如文件的长度为101,需要切割成4分,显然101%4 = 1,那么就先均分3份
前面3份每份大小为101/3 =33,余数会在转换是抹掉,最后一份的大小就为2
文件合并就没啥说的了,把之前切割的字文件拼接起来组成一个完整的文件
准备工作,在手机sd卡根目录放一个mp3我这里放了一个cd.mp3,至于动态授权就需要自己去申请,不然会报错。
理解了原理之后直接上代码:
java中定义一个类FileUtils,并声明两个native方法
package com.cool.ndktest2;
/**
* Created by cool on 2017/8/17.
*/
public class FileUtils {
/**
* 文件切割
* @param path 要切割文件的路径
* @param pattern 文件切割后名字的占位符
* @param num 文件切割的数量
*/
public native void diff(String path,String pattern,int num);
/**
* 文件合并
* @param patchPath 合并后的路径
* @param pattern 文件切割后名字的占位符
* @param num 之前切割的数量
*/
public native void patch(String patchPath,String pattern,int num);
}
java中使用
...
省略so库加载代码
...
private String path = Environment.getExternalStorageDirectory().getPath();
//文件分割方法
public void diff(View view){
FileUtils fileUtils = new FileUtils();
String mp3Path = path + "/cd.mp3";
String path_pattern = path + "/cd_%d.mp3";
fileUtils.diff(mp3Path,path_pattern,4);
}
//文件合并方法
public void patch(View view){
FileUtils fileUtils = new FileUtils();
String mp3Path = path + "/cdpatch.mp3";
String path_pattern = path + "/cd_%d.mp3";
fileUtils.patch(mp3Path,path_pattern,4);
}
文件的分割合并的代码都是在c中实现,接下来把目光转向c中,里面每行的注释都非常的清楚
//
// Created by cool on 2017/8/16.
//
#include "com_cool_ndktest2_MainActivity.h"
#include <string.h>
#include <android/log.h>
#include <assert.h>
#include <malloc.h>
#define TAG "399"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
//返回文件的大小
long getFileLenth(char *filePath){
FILE *fp = fopen(filePath,"rb");
fseek(fp,0L,SEEK_END);
long lenth = ftell(fp);
fclose(fp);
return lenth;
}
//文件切割
JNIEXPORT void JNICALL native_diff
(JNIEnv *env, jclass clazz, jstring jpath, jstring jpattern_path, jint jfile_num)
{
LOGE("文件开始切割");
const char* path = (*env)->GetStringUTFChars(env,jpath,NULL);
const char* pattern = (*env)->GetStringUTFChars(env,jpattern_path,NULL);
//申请二维数组存放切割后的文件名字
char** patchs = (char**)malloc(sizeof(char*)*jfile_num);
//分配内存
for(int i = 0;i<jfile_num;i++){
patchs[i] = malloc(sizeof(char)*100);
sprintf(patchs[i],pattern,i);
LOGE("每段文件的名字:%s",patchs[i]);
}
//计算文件长度
long length = getFileLenth(path);
LOGE("文件长度:%ld",length);
//以读的权限打开文件流
FILE* fp = fopen(path,"rb");
//文件总长度对要切割的分数取模运算
int size = length%jfile_num;
if(size ==0){//刚好能够被整除
//计算切分的每部分大小
int patchSize = length/jfile_num;
for (int i = 0; i < jfile_num; ++i) {
//打开之前要切割的文件,以写入的方式打开文件
FILE *patch = fopen(patchs[i],"wb");//若文件已经存在 就删除,只运行写
for (int j = 0; j < patchSize; j++) {
char ch = fgetc(fp);
fputc(ch,patch);
}
fclose(patch);
}
} else{//不能被整除
//计算切分的每部分大小
int patchSize = length/(jfile_num-1);
for (int i = 0; i < jfile_num - 1; i++) {
//打开之前要切割的文件,以写入的方式打开文件
FILE *patch = fopen(patchs[i],"wb");//若文件已经存在 就删除,只运行写
for (int j = 0; j < patchSize; j++) {
char ch = fgetc(fp);
fputc(ch,patch);
}
fclose(patch);
}
FILE *patch = fopen(patchs[jfile_num-1],"wb");
for (int i = 0; i < length % (jfile_num-1); i++) {
char ch = fgetc(fp);
fputc(ch,patch);
}
fclose(patch);
}
fclose(fp);
for (int i = 0; i < jfile_num; ++i) {
free(patchs[i]);
}
free(patchs);
patchs =NULL;
LOGE("文件切割完成");
(*env)->ReleaseStringChars(env,jpath,path);
(*env)->ReleaseStringChars(env,jpattern_path,pattern);
}
//文件合并
JNIEXPORT void JNICALL native_patch
(JNIEnv *env, jclass clazz, jstring jpatchPath, jstring jpatternPath, jint jfile_num){
LOGE("文件开始合并");
char *patchPath = (*env)->GetStringUTFChars(env,jpatchPath,NULL);
char *patternPath = (*env)->GetStringUTFChars(env,jpatternPath,NULL);
//申请二维数组存放切割后的文件名字
char** patchs = (char**)malloc(sizeof(char*)*jfile_num);
//分配内存
for(int i = 0;i<jfile_num;i++){
patchs[i] = malloc(sizeof(char)*100);
sprintf(patchs[i],patternPath,i);
LOGE("每段文件的名字:%s",patchs[i]);
}
FILE *fp = fopen(patchPath,"wb");
for(int i=0;i<jfile_num;i++){
int patchLength = getFileLenth(patchs[i]);
FILE *patch = fopen(patchs[i],"rb");
for (int j = 0; j < patchLength; j++) {
fputc(fgetc(patch),fp);
}
fclose(patch);
}
fclose(fp);
for (int i = 0; i < jfile_num; ++i) {
free(patchs[i]);
}
free(patchs);
patchs =NULL;
LOGE("文件合并完成");
(*env)->ReleaseStringChars(env,jpatchPath,patchPath);
(*env)->ReleaseStringChars(env,jpatternPath,patternPath);
}
//动态注册代码
static const JNINativeMethod gMethods[] = {
{
"diff","(Ljava/lang/String;Ljava/lang/String;I)V",(void*)native_diff
},
{
"patch","(Ljava/lang/String;Ljava/lang/String;I)V",(void*)native_patch
}
};
static int registerNatives(JNIEnv* engv)
{
LOGE("registerNatives begin");
jclass clazz;
clazz = (*engv) -> FindClass(engv, "com/cool/ndktest2/FileUtils");
if (clazz == NULL) {
LOGE("clazz is null");
return JNI_FALSE;
}
if ((*engv) ->RegisterNatives(engv, clazz, gMethods, NELEM(gMethods)) < 0) {
LOGE("RegisterNatives error");
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
LOGE("jni_OnLoad begin");
JNIEnv* env = NULL;
jint result = -1;
if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
return -1;
}
assert(env != NULL);
registerNatives(env);
return JNI_VERSION_1_4;
}