[英]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
我写一个文档,然后将其反向写入,是否有更好的选择呢?
如果可以,请给我一个例子吗?
我这里不会写完整的代码,因为您似乎在设计方面经常遇到问题。
但是您应该能够通过以下方式做您想做的事情:
read
或fread
strchr
或strtok
(更简单,但要注意)找到所有换行符,并将它们的位置存储到一个指针数组中(如果您不知道前面有多少行,则realloc
是您的朋友)。 write
或fwrite
)(您将需要记住某个地方某个变量的行数)。 请注意,我在步骤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.