如何在C

时间:2015-12-30 20:07:43

标签: c file io edit

我目前正在尝试在C中编辑.txt文件的特定行。我使用的文件如下所示:

Pixel location and RGB Color

现在假设我想改变写在图像上突出显示的特定行上的内容:

400,300:(255,255,255)#FFFFFF

进入这个:

400,300:(000,000,000)#000000

基本上,我试图在特定像素中创建黑点,在本例中为400,300。这就是我的代码:

#include <stdio.h>
int main(void)
{
    const char *filename = "sample.txt";
    int x = 400;
    int y = 300;

    FILE *fp;
    fp = fopen(filename, "w+");

    // Algorithm that reads all the file

    // If("Operation that reads" == x+","+y)
    // {
    //     Replace the line information after where it starts with "400,300"
    //     Like this : 400,300: (000,000,000) #000000
    // }


    // Algorithm that saves the file with the changes.


    fclose(fp)
    printf("Ok - File %s saved\n", filename);
    return 0;

创建,打开和编辑.txt文件对我来说是新的,所以我不知道该怎么做,我读的越多,我就越困惑。我如何解决这个问题以及适合的代码?

更新1:

FILE *fp;
fp = fopen(filename, "w+");

if ( fp == NULL )
{
    printf("Error while opening file"); 
}   

好的,所以在阅读了下面的内容后,我提出了一个想法,但仍需要工作。我会打印从文件到char数组的所有内容。之后我会在每个插槽中搜索我正在寻找的特定代码行并保留数字插槽。在那之后,我会去阵列,运行它,当涉及到那个特定的插槽时,我会替换所需的数据。现在,我需要做的就是将文件中的信息交换为数组中的信息,保存文件并解决问题。但我在代码中得到了错误,并且错过了清除txt文件并保存新数据的代码。

更新2:

#include <stdio.h>

int main(void)
{

int x,y;
int k = 0;
int noline; // Used to locate which line is the string im looking for
char search; // Used to compare with each string
char blackcode = (char)000; // In RGB, Black uses (000,000,000)
char blackhexcode = (char)000000; // The hexcode for black is #000000
const char *filename = "sample.txt";
char* strings[480000]; // Since its a 800x600 resolution picture, it needs that many lines.
char line[30]; // Space created to store whats inside each line of the file before transfering
char temp;

FILE * fp;
fp= fopen(filename, "r+");

if ( fp == NULL )
{
    printf("Error while opening file"); 
}   
else
{
    while(fgets(line, sizeof line, fp))
    {       
        strings[k]=strdup(line); // ERROR HERE! What Am I missing?
        k++;
    }

    for(k = 0; k< sizeof strings; k++)
    {
        temp = scanf("%[^:]s", strings[k]);
        search = ("%s,%s",x,y);

        if(temp = search)
        {
            noline = k;
        }
        else
        {
            printf("Error : Wrong Coordinates");
        }
    }

    for(k = 0; k < sizeof strings; k++)
    {
        if(k == noline)
        {
            strings[k] = ("%d,%d: (%s,%s,%s) #%s", x, y, blackcode, blackcode, blackcode, blackhexcode); // ERROR HERE! What did i did wrong?
        }
    }

    // Code that cleans the txt file and saves the array back to txt file
}
fclose(fp);

}

3 个答案:

答案 0 :(得分:2)

您缺少的内容有点概念性,与fopen有些相关。当您考虑使用fopen打开文件时,需要特别注意文件模式的效果。如果您仔细查看有关"w""w+"手册页。在这两种情况下,现有文件都是截断。在0-length的情况下,"w"

要避免此问题,一种方法是将整个文件读入缓冲区,然后更改缓冲区,将修改后的缓冲区写回原始文件名。这样就可以避免尝试插入/删除字节而不重写文件的其余部分。

要处理将文件读入缓冲区,发布的链接overwriting a specific line on a text file?提供了更改文件中单行的路线图。你的情况不同。您想要查找/替换特定模式的所有出现次数。 (这就是截断问题带来挑战的地方)然而,可以将大部分解决方案应用于将文件本身读入缓冲区。特别是使用fseekftell

使用fseekftell提供了一种简单的方法来确定文件的大小(或长度),然后可以使用该文件来分配空间以将整个文件保存在内存中。下面是一个简单函数的方法,它采用字符指针和文件指针的地址,然后使用fseekftell分配所需的内存来保存文件,然后将文件读入缓冲区(filebuf)在fread的单个操作中。缓冲区已填充到位,并且还返回。将指向文件长度fplen的指针传递给函数,以便在调用函数中返回长度(在这种情况下为main())。成功时返回指向缓冲区的指针(否则为NULL)将允许分配返回(如果需要),以及确定读取成功/失败的方法:

char *read_file_into_buf (char **filebuf, long *fplen, FILE *fp)
{
    fseek (fp, 0, SEEK_END);
    if ((*fplen = ftell (fp)) == -1) {  /* get file length */
        fprintf (stderr, "error: unable to determine file length.\n");
        return NULL;
    }
    fseek (fp, 0, SEEK_SET);  /* allocate memory for file */
    if (!(*filebuf = calloc (*fplen, sizeof *filebuf))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return NULL;
    }

    /* read entire file into filebuf */
    if (!fread (*filebuf, sizeof *filebuf, *fplen, fp)) {
        fprintf (stderr, "error: file read failed.\n");
        return NULL;
    }

    return *filebuf;
}

将文件放入内存后,第二部分就是扫描缓冲区并进行所需的替换。这里有许多不同的调整可以用来优化搜索/替换,但以下只是一个直接的基本搜索/替换,其中唯一的优化尝试是在使用普通{{1}之前比较起始字符用于检查指定搜索字符串的字符串比较函数。该函数返回所做的替换次数,以便您确定是否需要写出原始文件名:

string.h

使用您的样本数据将所有部分放在一个简短的示例程序中可以写成如下。程序期望文件名作为第一个参数(或者它将从unsigned find_replace_text (char *find, char *rep, char *buf, long sz) { long i; unsigned rpc = 0; size_t j, flen, rlen; flen = strlen (find); rlen = strlen (rep); for (i = 0; i < sz; i++) { /* if char doesn't match first in find, continue */ if (buf[i] != *find) continue; /* if find found, replace with rep */ if (strncmp (&buf[i], find, flen) == 0) { for (j = 0; buf[i + j] && j < rlen; j++) buf[i + j] = rep[j]; if (buf[i + j]) rpc++; } } return rpc; } 读取,并且如果没有给出文件名,则默认写入stdin。您还可以包含其他验证检查:

stdout

只需在文件底部包含这些功能即可。然后你可以:

<强>编译

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

char *read_file_into_buf (char **filebuf, long *fplen, FILE *fp);
unsigned find_replace_text (char *find, char *rep, char *buf, long sz);

int main (int argc, char **argv) {

    char *srchstr = "400,300";
    char *repstr = "400,300: (000,000,000) #000000";
    char *filebuf = NULL;
    long int fplen = 0;
    FILE *fp = NULL;

    /* open file for reading (default stdin) */
    fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open */
        fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
        return 1;
    }

    if (!read_file_into_buf (&filebuf, &fplen, fp)) return 1;
    if (fplen < 1 || fplen >= INT_MAX) { /* validate file length */
        fprintf (stderr, "error: length of file invalid for fwrite use.\n");
        return 1;
    }
    if (fp != stdin) fclose (fp);

    /* find/replace text in filebuf */
    if (!find_replace_text (srchstr, repstr, filebuf, fplen)) {
        printf ("no replacements made.\n");
        return 0;
    }

    /* open file for writing (default stdout) */
    fp = argc > 1 ? fopen (argv[1], "w") : stdout;

    if (!fp) {  /* validate file open */
        fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
        return 1;
    }

    /* write modified filebuf back to filename */
    if (fwrite (filebuf, sizeof *filebuf, (size_t)fplen, fp) != (size_t)fplen) {
        fprintf (stderr, "error: file write failed.\n");
        return 1;
    }
    if (fp != stdout) 
        if (fclose (fp) == EOF) {
            fprintf (stderr, "error: fclose() returned EOF\n");
            return 1;
        }

    free (filebuf);

    return 0;
}

(或使用与编译器等效的编译字符串)

输入文件

gcc -Wall -Wextra -O3 -o bin/fread_file fread_file.c

替换后使用/文件

$ cat dat/rbgtst.txt
400,280: (234,163,097) #EAA361
400,300: (255,255,255) #FFFFFF
400,320: (064,101,160) #4065A0
400,340: (220,194,110) #DCC26E

或阅读$ ./bin/fread_file dat/rbgtst.txt $ cat dat/rbgtst.txt 400,280: (234,163,097) #EAA361 400,300: (000,000,000) #000000 400,320: (064,101,160) #4065A0 400,340: (220,194,110) #DCC26E 写给stdin

stdout

内存/错误检查

在你的动态分配内存的任何代码中,你有2个责任关于任何分配的内存块:(1)总是保留一个指向内存块起始地址的指针,所以,(2)它可以在释放时释放它不再需要了。

必须使用内存错误检查程序,以确保您没有在已分配的内存块之外/之外写入,尝试读取或基于未初始化的值跳转,最后确认您已释放所有内存你分配的记忆。

对于Linux $ ./bin/fread_file <dat/rbgtst.txt 400,280: (234,163,097) #EAA361 400,300: (000,000,000) #000000 400,320: (064,101,160) #4065A0 400,340: (220,194,110) #DCC26E 是正常的选择。有许多微妙的方法可以滥用新的内存块。使用内存错误检查器可以识别任何问题并验证您分配的内存的正确使用,而不是通过valgrind找出问题。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。 E.g:

segfault

您要确认$ valgrind ./bin/fread_file dat/rbgtst.txt ==13768== Memcheck, a memory error detector ==13768== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==13768== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==13768== Command: ./bin/fread_file dat/rbgtst.txt ==13768== ==13768== ==13768== HEAP SUMMARY: ==13768== in use at exit: 0 bytes in 0 blocks ==13768== total heap usage: 3 allocs, 3 frees, 2,128 bytes allocated ==13768== ==13768== All heap blocks were freed -- no leaks are possible ==13768== ==13768== For counts of detected and suppressed errors, rerun with: -v ==13768== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) All heap blocks were freed -- no leaks are possible(忽略已删除注释,该注释仅与我系统上未安装的 debug 符号文件有关)

查看代码并了解它在做什么。这不是作为您尝试做的事情的唯一方式,而是作为如何解决问题的示例,同时避免在尝试一次更改一行时固有的一些陷阱在现有文件中利用偏移量和对文件的多次读/写。如果您有疑问,请告诉我。

答案 1 :(得分:0)

一般情况下,您无法编写特定的txt文件行。

实际上,txt文件只是一个字节序列。每一行只用特殊符号'\ n'(或符号'\ r','\ n':彼此分开)有两种方法。

因此,如果你重写一些行,你必须在新行之后移动文件中保留的数据(行)。

但是如果你的新行与之前的行长度相同,你可以在旧行上写下它而不用担心。

答案 2 :(得分:0)

我能想到的最好的方法是以只读模式打开文件,然后通过在'w +'模式下打开一个新文件将所有内容复制到新文件夹。然后在读取文件中逐行进行,直到找到要更改的行,然后在新的副本文件中自行重写该行。然后在读取文件中跳过该行并继续。 复制文件是您想要的后,您可以将其名称替换为您希望它具有的原始文件名。然后它会像你想要的那样编​​辑文件。