c - 有没有办法在不将文件存储在c数组中的情况下移动文件的内容?
问题描述
我正在尝试替换文件中的单词,这适用于相同长度的单词。
我知道可以通过将内容存储在临时数组中然后移动来完成,但我想知道是否可以在不使用数组的情况下完成。
#include<stdio.h>
#include<string.h>
int main(int argc, char **argv)
{
char s1[20], s2[20];
FILE *fp = fopen(argv[1], "r+");
strcpy(s1, argv[2]);
strcpy(s2, argv[3]);
int l, i;
while(fscanf(fp, "%s", s1)!=EOF){
if(strcmp(s1, argv[2]) == 0){
l = strlen(s2);
fseek(fp, -l, SEEK_CUR);
i=0;
while(l>0){
fputc(argv[3][i], fp);
i++;
l--;
}
}
}
}
这是我用于替换相同长度单词的代码,我可以在此处针对不同长度修改什么?
解决方案
假设 OP 的目标是避免将文件的全部内容存储到字节数组中(可能内存不足),并且他还说它需要“转移”文件的内容,所以它不能使用临时文件来制作文本替换(可能存储设备中没有足够的空间)。
请注意,复制到临时文件将是最简单的方法。
所以我可以看到解决方案有两种算法:
- 向左移动:将一个文本替换为另一个长度相等或更小的文本。
- 向右移动:用更长的文本替换文本。
向左移动:
- 维护 2 个文件位置指针:一个用于读取位置 (rdPos),另一个用于写入位置 (wrPos)。
- 两者都从零开始。
- 从 rdPos 读取 char 直到找到 oldText 并将其写入 wrPos(但前提是 rdPos != wrPos 以避免不必要的写入操作)。
- 将 newText 写入 wrPos。
- 从步骤 3 重复直到 EOF。
- 如果 len(oldText) > len(newText) 然后截断文件
向右移动:
- 维护 2 个文件位置指针:(rdPos 和 wrPos)。
- 扫描整个文件以查找 oldText 出现的次数。
- 将它们的文件位置存储到一个小数组中(不是严格需要,但对于避免对 oldText 进行第二次反向扫描很有用)
- 设置 rdPos = EOF-1(文件中的最后一个字符)
- set wrPos = EOF+foundCount*(len(newText)-len(oldText)): 为移位保留足够的额外空间。
- 从 rdPos 读取 char 直到在“found”数组中找到位置并将 char 写入 wrPos。
- 将 newText 写入 wrPos。
- 从第 6 步重复直到 BOF。
我编写了以下实现作为上述算法的示例,但没有过多关注验证和边缘情况。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_ITEMS 100
#define DO_WRITE 0x01
#define DO_FIND 0x02
FILE *fp;
long rdPos = 0L, wrPos = 0L, rdCount=0L, wrCount=0L;
int newLen, oldLen;
char *newText, *oldText;
struct found_t { int len; long pos[MAX_ITEMS];} found;
/* helper functions */
void writeChars(char *buffer, int len){
if(wrPos < rdPos){
long p = ftell(fp);
fseek(fp, wrPos, SEEK_SET);
fwrite(buffer, len, 1, fp);
fseek(fp, p, SEEK_SET);
wrCount += len;
}
wrPos += len;
}
int nextReadChar = -1;
int readChar(){
int c;
if(nextReadChar == EOF) {
if((c = fgetc(fp)) != EOF)
rdCount++;
} else {
c = nextReadChar;
nextReadChar = EOF;
}
return c;
}
int findFirstChar(int action){
int c; char ch;
for(; (c = readChar()) != EOF && c != (int)oldText[0]; rdPos++)
if(action == DO_WRITE) {
ch = (char)c;
writeChars(&ch, 1);
}
return c;
}
int testOldText(int c, int action){
char *cmp;
for(cmp = oldText; *cmp != '\0' && c == (int)*cmp; cmp++)
c = readChar();
nextReadChar = c;
if(*cmp == '\0') { /* found oldText */
if(action == DO_FIND)
found.pos[found.len++] = rdPos;
rdPos += oldLen;
if(action == DO_WRITE){
writeChars(newText, newLen);
found.len++;
}
}
else { /* some chars were equal */
if(action == DO_WRITE)
writeChars(oldText, cmp-oldText);
rdPos += cmp-oldText;
}
return c;
}
void writeReverseBlock(long firstCharPos){
for(;rdPos >= firstCharPos+oldLen; rdPos--, wrPos--, rdCount++, wrCount++){
int c;
fseek(fp, rdPos, SEEK_SET); c = fgetc(fp);
fseek(fp, wrPos, SEEK_SET); fputc(c, fp);
}
rdPos = firstCharPos-1;
wrPos -= newLen-1;
fseek(fp, wrPos--, SEEK_SET);
fwrite(newText, newLen, 1, fp);
wrCount += newLen;
}
void scanFile(int action){
int c;
do {
if( (c = findFirstChar(DO_WRITE)) == EOF ) break;
}while(testOldText(c, DO_WRITE) != EOF);
}
/** Main Algorithms */
void shiftToLeft(){
scanFile(DO_WRITE);
fflush(fp);
ftruncate(fileno(fp), wrPos);
}
void shiftToRight(){
int i;
scanFile(DO_FIND);
wrPos = --rdPos + found.len * (newLen-oldLen); /* reserve space after EOF */
for(i=found.len-1; i>=0; i--)
writeReverseBlock(found.pos[i]);
}
/* MAIN program */
int main(int argc, char **argv){
if(argc != 4){
fprintf(stderr, "Usage: %s file.ext oldText newText\n", argv[0]);
return 1;
}
if(!(fp = fopen(argv[1], "r+b"))) {
fprintf(stderr, "Cannot open file '%s'\n", argv[1]);
return 2;
}
oldLen = strlen(oldText = strdup(argv[2]));
newLen = strlen(newText = strdup(argv[3]));
found.len = 0;
/* which algorithm? */
if(newLen <= oldLen) shiftToLeft();
else shiftToRight();
fclose(fp);
printf("%7d occurrences\n"
"%7ld bytes read\n"
"%7ld bytes written\n", found.len, rdCount, wrCount);
return 0;
}
推荐阅读
- html - 用文本值替换三个点
- windows - 如何获取使用 BTNS_AUTOSIZE 样式创建的工具栏按钮的实际大小?
- python - 在嵌套 python 列表中对外部维度使用切片分配时出现意外结果
- android - Kotlin/Android - setOnClickListener 仅适用于一个按钮
- java - Java 本身会改变字符的大小
- mysql - 用户上传文件后,如何在 Web 应用程序中向管理员发送通知
- python - 如何为序列化程序方法结果创建 json 属性
- firefox-addon - 当弹出窗口重新打开时,为什么弹出脚本会向内容脚本发送更多消息?
- javascript - 如何在命名函数 change() 中获取 prop('checked') this
- javascript - 如何在本机反应中从图像中获取颜色