繁体   English   中英

将行从文件复制到c中的向后

[英]Copying the lines from a file to another backwards in c

我正在尝试将具有相同权限,相同大小,全部相同的文件本身复制到另一个文件(新),我做到了,但是正常复制了内容,但是现在试图向后复制(最后一行是新文件),我对此的计划是,首先获取文件的行数,如下所示:

while (!feof(fp)) {
    ch = fgetc(fp);
    if (ch == '\n') {
        lines++;
    }
}

然后,在我将数组充满了for ,创建一个数组以放置文件中的每一行

for (unsigned i = n; i-- > 0;) {
    //n is the number of lines
}

然后在此上for我写一个文档,然后将其反向写入,是否有更好的选择呢?

如果可以,请给我一个例子吗?

我这里不会写完整的代码,因为您似乎在设计方面经常遇到问题。

但是您应该能够通过以下方式做您想做的事情:

  1. 整个文件读入一个字符串有一个大readfread
  2. 使用strchrstrtok (更简单,但要注意)找到所有换行符,并将它们的位置存储到一个指针数组中(如果您不知道前面有多少行,则realloc是您的朋友)。
  3. 最后,将每个字符串写到文件中,并通过指针数组向后移动,中间使用换行符(使用writefwrite )(您将需要记住某个地方某个变量的行数)。

请注意,我在步骤2中提到了两个可能使用的库函数strtok可能是最容易使用的函数,但是有一些警告,例如忽略空行,不是线程安全的和修改输入参数,所以我只会使用它可以快速破解。

因此,如果您希望程序正常运行,则strchr会更合适。 但是,这将很难使用。 最重要的是,您每次每次调用它时都需要记住,在最后一次调用它的结果之后在char上调用它,并确保对字符串的末尾进行null检查。 另外,您还需要确保只写回下一行时才写到下一个换行符,因为strchr不会将它们替换为空字符。

对于行较长的中等大小的文件,这是一种快速而肮脏的递归实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    static char buf[10000];
    if (fgets(buf, sizeof(buf), stdin)) {
        char *str = strdup(buf);
        main();
        fputs(str, stdout);
        free(str);
    }
    return 0;
}

这是一个非常简单的过滤器:它从标准输入读取文件,然后以相反的顺序将行写入标准输出。

现在,由于您没有进行任何编码,因此您的任务将是确定这种方法的潜在问题。

  • 将文件读入内存(如果适合...)
  • 反转整个文件(按字符)
  • 反转结果行中的字符
  • 写文件
  • 请不要重新格式化。 我有权采用不同的缩进样式,而且我也不喜欢您的缩进样式。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
size_t size, beg, end, this, used;
char*all = NULL;
int ch;
FILE *fp;

fp = fopen (argv[1], "rb+" );
if (!fp) {
        perror("open failed");
        exit(EXIT_FAILURE);
        }

fprintf(stderr,"Reading file...\n");
for (size = used=0;1; used++) {
        ch = getc(fp);
        if (used+1 >= size) {
                size_t newsize;
                newsize = (size==0) ? 1024: size *2;
                fprintf(stderr,"Realloc %zu <<--%zu ...\n", size, newsize);
                all = realloc(all, newsize);
                if (!all) exit(EXIT_FAILURE);
                size = newsize;
                }
        if (ch == EOF) break;
        all[used] = ch;
        }
all[used] = 0;

fprintf(stderr,"Read %zu / %zu ...\n", used, size );
rewind(fp);

fprintf(stderr, "Inverting file...\n");
for (beg = 0, end = used; beg < --end ; beg++) {
        ch = all[beg];
        all[beg] = all[end];
        all[end] = ch;
        }

fprintf(stderr,"beg/End= %zu / %zu ...\n", beg, end );
rewind(fp);
fprintf(stderr,"Inverting lines...\n");
for (beg = 0; beg < used; beg = this) {
        char *nl = strchr(all+beg+1, '\n' );
        if (nl) end = nl - all;
        else end = used;
        for(this = end; beg < --end; beg++) {
                ch = all[beg];
                all[beg] = all[end];
                all[end] = ch;
                }
        }

 /* at this point, you could fclose(fp); the input file,
 ** and open a new file for the result, using
 ** fp = fopen("somename", "w");
 ** But here, the result is written back into the original file.
 */

rewind(fp);

fprintf(stderr,"Writing file...\n");
for (beg =0;beg < used; beg++) {
        putc(all[beg] , fp);
        }

fprintf(stderr,"Closing file...\n");
fclose(fp);
return 0;
}

这是一种使用mmap来访问文件的方法。 对于较大的文件,它应该更快。

我已经对此进行了测试,它似乎可以处理大多数边缘情况。

为了进一步提高速度,您可以添加madvise呼叫。

旁注:对不起_GNU_SOURCE 这是为了获得memrchr的定义。 如果您的系统没有该功能,那么创建一个就足够简单了。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>

#define sysfault(_fmt...) \
    do { \
        fprintf(stderr,_fmt); \
        exit(1); \
    } while (0)

#ifdef DEBUG
#define dbgprt(_fmt...)     fprintf(stderr,_fmt)
#else
#define dbgprt(_fmt...)     /**/
#endif

typedef unsigned char byte;

typedef struct {
    const char *map_file;               // filename
    int map_fd;                         // file descriptor
    size_t map_siz;                     // file size
    byte *map_base;                     // base of map area
    byte *map_ptr;                      // current map pointer
    size_t map_off;                     // current file offset
    struct stat map_st;                 // stat syscall
} filemap;

// mapopen -- open the file
void
mapopen(filemap *map,const char *file,int mode,filemap *mapfm)
{
    int prot;
    int err;

    memset(map,0,sizeof(filemap));

    map->map_file = file;
    map->map_fd = open(map->map_file,mode,0644);
    if (map->map_fd < 0)
        sysfault("mapopen: unable to open '%s' -- %s\n",
            map->map_file,strerror(errno));

    // create new file
    if (mode & O_CREAT) {
        map->map_siz = mapfm->map_siz;
        err = ftruncate(map->map_fd,map->map_siz);
        if (err < 0)
            sysfault("mapopen: unable to ftruncate -- %s\n",strerror(errno));
        map->map_off = 0;
    }

    // process existing file
    else {
        fstat(map->map_fd,&map->map_st);
        map->map_siz = map->map_st.st_size;
        map->map_off = map->map_siz - 1;
        lseek(map->map_fd,map->map_off,0);
    }

    prot = 0;
    if (mode & O_CREAT)
        prot |= PROT_WRITE;
    else
        prot |= PROT_READ;

    do {
        if (map->map_siz == 0)
            break;

        map->map_base = mmap(NULL,map->map_siz,prot,MAP_SHARED,map->map_fd,0);
        if (map->map_base == MAP_FAILED)
            sysfault("mapopen: unable to map map_file='%s' map_siz=%ld -- %s\n",
                map->map_file,map->map_siz,strerror(errno));

        // get starting offset for file
        map->map_ptr = map->map_base + map->map_off;
    } while (0);
}

// mapclose -- close the file
void
mapclose(filemap *map)
{

    if (map->map_base != NULL)
        munmap(map->map_base,map->map_siz);
    map->map_base = NULL;

    if (map->map_fd >= 0)
        close(map->map_fd);
    map->map_fd = -1;
}

// mapreverse -- reverse copy lines in file
void
mapreverse(const char *srcfile,const char *dstfile)
{
    filemap src;
    filemap dst;
    byte *base;
    byte *prev;
    byte *cur;
    ssize_t lhslen;
    ssize_t rawlen;
    ssize_t curlen;

    mapopen(&src,srcfile,O_RDONLY,NULL);
    mapopen(&dst,dstfile,O_RDWR | O_CREAT,&src);

    base = src.map_base;

    // point past last char in file
    lhslen = src.map_siz;
    prev = base + lhslen;

    do {
        // empty file
        if (lhslen <= 0)
            break;

        // assume file with last line that has _no_ newline

        // bug out if short file
        cur = prev - 1;
        if (cur < base) {
            dbgprt("mapreverse: SHORT\n");
            break;
        }

        // well behaved file with newline as last char
        if (*cur == '\n') {
            dbgprt("mapreverse: NICE\n");
            lhslen -= 1;
        }
    } while (0);

    // copy over the bulk of the file
    while (lhslen > 0) {
        dbgprt("mapreverse: LOOP lhslen=%ld prev=%ld\n",lhslen,prev - base);

        // locate next (i.e. "previous") line
        cur = memrchr(base,'\n',lhslen);

        // copy over final part
        if (cur == NULL) {
            dbgprt("mapreverse: FINAL\n");
            break;
        }

        // get length of current line (including newline on left)
        rawlen = prev - cur;
        dbgprt("mapreverse: CURLEN cur=%ld prev=%ld rawlen=%ld\n",
            cur - base,prev - base,rawlen);

        // remove newline on left from copy buffer and length
        curlen = rawlen - 1;

        // copy current line
        dbgprt("mapreverse: COPY\n");
        memcpy(dst.map_ptr,cur + 1,curlen);
        dst.map_ptr += curlen;
        dst.map_siz -= curlen;

        // cut back on the length we scan
        lhslen = cur - base;

        // point one past the newline we just found
        prev = cur + 1;
    }

    // copy over final part
    if (dst.map_siz > 0) {
        dbgprt("mapreverse: FINAL map_siz=%ld\n",dst.map_siz);
        memcpy(dst.map_ptr,base,dst.map_siz);
    }

    mapclose(&src);
    mapclose(&dst);
}

// main -- main program
int
main(int argc,char **argv)
{
    char *cp;

    --argc;
    ++argv;

    for (;  argc > 0;  --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        default:
            break;
        }
    }

    setlinebuf(stderr);

    if (argc != 2)
        sysfault("main: must have exactly two arguments\n");

    mapreverse(argv[0],argv[1]);

    return 0;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM