大家好,我目前已自学了4个月的C语言,现在遇到一个问题需要大家的解答,我试过搜索类似出现过的问题但一无所获,可能是我还不会搜关键字吧。
我在练习如何使用自定义函数来实现复制文件内容的功能时,设置了两种复制文件的方式,在运行时发现程序没有任何问题,可以正常运行,但两种复制文件的结果让我不能理解。第一种复制文件的方式是复制文件A的所有内容,保存到缓冲区中,然后写入到文件B,复制的结果是正常的,文件B打开和文件A一摸一样,无论是中文还是英文内容复制到文件B后打开都是可读;第二种复制文件的方式是只复制文件A的指定的内容长度,保存到缓冲区中,然后写入到文件B,但是这种方式复制的结果是文件B打开后的内容如果是英文内容就是可读的,如果是中文内容则显示为乱码,我完全搞不懂为什么会是这种情况,请大家解答一下我的困惑,谢谢!我使用的是 Windows 64 系统下的 visual studio code 2022 来运行以下这段代码的。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<locale.h>
#define N 100
long fCopy(FILE *, long, long, FILE *, long);
int main(){
setlocale(LC_ALL, "chs"); //环境设置为中文简体
long length; //定义要复制的内容长度,-1为所有内容,以字节为大小
long offsetfRd, offsetfWt; //定义要复制的文件和目标文件的偏移量,不得小于0,不得超过文件长度
char fileRead[N], fileWrite[N]; //定义文件的保存路径
FILE *fileRd, *fileWt; //定义文件指针指向复制文件和目标文件
//输入要复制的内容长度,以及复制文件和目标文件的保存路径和偏移量
wprintf(L"\n要复制的文件: ");
scanf("%s", fileRead);
wprintf(L"将文件复制到: ");
scanf("%s", fileWrite);
wprintf(L"(-1复制所有内容。其他整数要求不超过文件内容长度)\n");
wprintf(L"要复制的内容长度: ");
scanf("%ld", &length);
wprintf(L"输入要复制的文件的偏移量: ");
scanf("%ld", &offsetfRd);
wprintf(L"输入目标文件的偏移量:");
scanf("%ld", &offsetfWt);
//判断文件打开是否正确
if((fileRd=fopen(fileRead, "rb"))==NULL||(fileWt=fopen(fileWrite, "wb"))==NULL){
puts("Fail to open");
exit(0);}
//调用fCopy()进行复制操作,将fileRead的所有数据复制到fileWrite
//返回值>0,成功复制数据,否则复制失败
if(fCopy(fileRd, offsetfRd, length, fileWt, offsetfWt)) wprintf(L"文件复制成功");
else wprintf(L"文件复制失败");
//文件操作结束后关闭文件
fclose(fileRd);
fclose(fileWt);
return 0;
}
/**
* 文件复制函数fCopy()
* @param fSource 要复制的原文件的保存路径
* @param offsetSource 原文件相对于文件开头的位置偏移,即从何处开始复制
* @param len 要复制的内容长度,小于0表示复制offsetSource后边的所有内容
* @param fTarget 目标文件的保存路径
* @param offsetTarget 目标文件相对于文件开头的位置偏移,即复制到目标文件的哪个位置
* @return 返回成功复制的字节数
**/
long fCopy(FILE *fSource, long offsetSource, long len, FILE *fTarget, long offsetTarget){
int bufferLen = 1024*4; //定义缓冲区长度为4Kb,即1024*4=4096个字节长度
char *buffer = (char*)malloc(bufferLen); //定义bufferLen个字节大小的缓冲区并返回指针buffer
int readCount; //定义调用fread()时实际读取的字节数
long nBytes = 0; //定义总共复制的字节数
int n = 0; //定义需要调用fread()的次数
int i = 0; //定义循环控制变量
//将文件内部的位置指针定位到离文件开头的指定偏移处
fseek(fSource, offsetSource, SEEK_SET);
fseek(fTarget, offsetTarget, SEEK_SET);
//大部分磁盘的扇区都是4Kb对其的,每次循环读取的数据为4Kb的整数倍,即可保证不跨扇区读取数据,避免效率降低
//当内容长度<0,复制fSource所有数据
if(len<0){
//读取fSource的数据并输入到缓冲区buffer,暂不清楚数据大小是否大于4Kb
//因此,设要读写的数据块数为bufferLen个,每个数据块的字节数为1个字节
//每次循环只读取(1*bufferLen=4096)个字节的数据
printf("\n");
while((readCount=fread(buffer, 1, bufferLen, fSource))>0){
wprintf(L"[%2d] 读取到 %d 个字节的数据, ", ++i, readCount);
//将buffer的数据写入到fTarget,可以肯定数据大小最大不超过4Kb
//因此,设要写入的数据块数为1个,每个数据块的字节数为readCount个字节
//每次循环都写入(readCount*1)个字节的数据
fwrite(buffer, readCount, 1, fTarget);
//计算最终一共读取的字节数
nBytes+=readCount;
wprintf(L"总共读取到 %ld 个字节\n", nBytes);}}
//当内容长度>=0,复制从fSource指定的开头到len个字节处的内容
else{
//不读取全部数据,需要知道调用fread()的次数来读取指定的内容长度
//通过计算指定的内容长度/缓冲区长度,向上取整求得调用fread()的次数
//指定的内容长度<=缓冲区长度,只调用一次即可读取完毕
//指定的内容长度>缓冲区长度,多次调用才能读取完毕
n=(int)ceil((double)((double)len/bufferLen));
wprintf(L"一共循环读取%d次\n", n);
for(i=0; i<n; i++){
//每次循环如果(指定的内容长度-已读取的长度=未读取的长度)>缓冲区长度,不修改缓冲区长度
//直到最后一次循环,未读取的长度必然小于缓冲长度,修改缓冲区长度
//保证最后一次循环只读取到指定但还未读取的数据的最后一个字节,而不是缓冲区长度的最后一个字节
if((len-nBytes)<bufferLen) bufferLen=len-nBytes;
wprintf(L"缓冲区长度为 %d 个字节, ", bufferLen);
readCount=fread(buffer, 1, bufferLen, fSource);
wprintf(L"[%2d] 读取到 %d 个字节的数据, ", i+1, readCount);
fwrite(buffer, readCount, 1, fTarget);
nBytes+=readCount;
wprintf(L"总共读取到 %ld 个字节\n", nBytes);}}
fflush(fSource);
fflush(fTarget);
//复制操作结束后释放缓冲区
free(buffer);
//返回成功读取的字节数
return nBytes;
}
运行结果【一】要复制的文件和目标文件都没有偏移量,复制所有内容,结果正常
运行结果【二】要复制的文件和目标文件都设有偏移量,复制所有内容,结果正常
运行结果【三】要复制的文件设有偏移量,目标文件没有偏移量,复制所有内容,结果正常
运行结果【四】要复制的文件没有偏移量,目标文件设有偏移量,复制所有内容,结果正常
运行结果【五】要复制的文件和目标文件都没有偏移量,复制指定长度的内容,结果异常
运行结果【六】要复制的文件和目标文件都设有偏移量,复制指定长度的内容,结果正常
运行结果【七】要复制的文件设有偏移量,目标文件没有偏移量,复制指定长度的内容,结果正常
运行结果【八】要复制的文件没有偏移量,目标文件设有偏移量,复制指定长度的内容,结果异常
以上运行结果大家都能看到,只要是复制所有内容,无论要复制的文件和目标文件是否有偏移量,复制的结果都显示正常,中英文内容都是可读的;但是只复制一部分内容时,只要要复制的文件没有偏移量时,复制的结果就会出现异常,只有英文内容可读,中文内容变为乱码。
debug呀,一步一步执行很容易就能看出问题
你把前面的声明
long fCopy(FILE *, long, long, FILE *, long);
改成
long fCopy(FILE *fSource, long offsetSource, long len, FILE *fTarget, long offsetTarget);
不确定是不是这导致的