首页 > 解决方案 > 有没有办法在不将文件存储在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--;
                            }
                    }
            }
    }

这是我用于替换相同长度单词的代码,我可以在此处针对不同长度修改什么?

标签: c

解决方案


假设 OP 的目标是避免将文件的全部内容存储到字节数组中(可能内存不足),并且他还说它需要“转移”文件的内容,所以它不能使用临时文件来制作文本替换(可能存储设备中没有足够的空间)。

请注意,复制到临时文件将是最简单的方法。

所以我可以看到解决方案有两种算法:

  • 向左移动:将一个文本替换为另一个长度相等或更小的文本。
  • 向右移动:用更长的文本替换文本。

向左移动:

  1. 维护 2 个文件位置指针:一个用于读取位置 (rdPos),另一个用于写入位置 (wrPos)。
  2. 两者都从零开始。
  3. 从 rdPos 读取 char 直到找到 oldText 并将其写入 wrPos(但前提是 rdPos != wrPos 以避免不必要的写入操作)。
  4. 将 newText 写入 wrPos。
  5. 从步骤 3 重复直到 EOF。
  6. 如果 len(oldText) > len(newText) 然后截断文件

向右移动:

  1. 维护 2 个文件位置指针:(rdPos 和 wrPos)。
  2. 扫描整个文件以查找 oldText 出现的次数。
  3. 将它们的文件位置存储到一个小数组中(不是严格需要,但对于避免对 oldText 进行第二次反向扫描很有用)
  4. 设置 rdPos = EOF-1(文件中的最后一个字符)
  5. set wrPos = EOF+foundCount*(len(newText)-len(oldText)): 为移位保留足够的额外空间。
  6. 从 rdPos 读取 char 直到在“found”数组中找到位置并将 char 写入 wrPos。
  7. 将 newText 写入 wrPos。
  8. 从第 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;
}

推荐阅读