如何在C中编辑txt文件的特定行 [英] How to edit a specific line of a txt file in C

查看:97
本文介绍了如何在C中编辑txt文件的特定行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am currently trying to edit specific lines of .txt file in C. The file that im using looks like this :

像素位置和RGB颜色

现在让我说我想更改在图像上突出显示的特定行上写着什么:

Now lets say I want to change whats written on the specific line that its highlighted on the image:

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

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

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

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

基本上,我试图在其中创建黑点特定像素,在这种情况下为400,300这就是我的代码:

Basically, im trying to create a black dot in specific pixels, in this case on 400,300. This is what i have of code:

#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文件对我来说是一种新事物,所以我不知道该怎么做,我读得越多,就会越困惑。我该如何解决这个问题以及适合的代码?

Creating, opening and editing .txt files is kind of new for me so I dont know what to do, the more i read about it, the more confused I get. How do I approach this problem and what code would fit here?

更新1:

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

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

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

Ok so after reading what you have placed below i came up with an idea but still needs work. I would print everything from the file to a char array. After that i would search in each slot for the specific line of code that I was looking for and keep the number slot. After that, i would go to array, run it, and when it comes to that specific slot, i would replace the needed data. Now all i needed to do is to swap the information thats in the file for the one thats in the array, save the file and problem solved. But im getting erros in the code and im missing the bits of code that would clear the txt file and save the new data.

更新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);

}

推荐答案

缺少的内容在概念上与 fopen 有关。当考虑使用 fopen 打开文件时,需要特别注意文件模式的效果。如果您仔细查看手册页,其中涉及 w w + 。在这两种情况下,现有文件都被截断。在 w 情况下为 0长度

What you are missing is somewhat conceptual, and somewhat related to fopen. When you think about opening a file with fopen, you need to pay particular attention to the effect of the file modes. If you look carefully at the man page regarding either "w" or "w+". In both cases the existing file is truncated. To 0-length in the case of "w".

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

To avoid this issue, one approach is to read the entire file into a buffer and then make changes to the buffer, writing the modified buffer back to the original filename. This avoids the possibility to attempting to insert/delete bytes without rewriting the remainder of the file.

要处理将文件读取到缓冲区中的操作,链接必须 覆盖文本文件上的特定行吗? ,提供了更改文件中单行的路线图。您的情况不同。您想查找/替换特定模式的 All 事件。 (这就是 truncation 问题所带来的挑战)。但是,那里的许多解决方案都可以应用于将文件本身读取到缓冲区中。具体来说,使用 fseek ftell

To handle reading the file into a buffer, the link posted overwriting a specific line on a text file?, provides a roadmap to changing a single line in a file. Your case is different. You want to find/replace All occurrences of a particular pattern. (that is where the truncation issue posses challenges) However much of the solution there can be applied to reading the file itself into a buffer. Specifically the use of fseek and ftell.

使用 fseek ftell 提供了一种简单的方法来确定文件的大小(或长度),然后将其用于分配空间以将整个文件保存在内存中。下面是一种简单函数的方法,该函数使用字符指针和文件指针的地址,然后使用 fseek ftell 分配所需的内存来保存文件,然后通过 fread filebuf ) $ c>。缓冲区被填充到位并返回。指向文件长度 fplen 的指针被传递给该函数,因此长度可以在调用函数( main())。如果成功返回一个指向缓冲区的指针(否则为 NULL )将允许返回的赋值,以及一种确定读取成功/失败的方法:

Using fseek and ftell provides a simply way to determine the size (or length) of the file that can then be used to allocate space to hold the entire file in memory. Below is one approach to a simple function that takes the address of a character pointer and a file pointer, then using fseek and ftell allocates the required memory to hold the file and then reads the file into the buffer (filebuf) in a single operation with fread. The buffer is filled, in place, and also returned. A pointer to the file length fplen is passed to the function so the length is made available back in the calling function (main() in this case). Returning a pointer to the buffer on success (NULL otherwise) will allow assignment of the return, if desired, and a way to determine success/failure of the read:

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;
}

一旦您将文件存储在内存中,第二个难题就是扫描缓冲区并进行所需的替换。您可以应用许多不同的调整来优化搜索/替换,但以下只是简单的基本搜索/替换,其中唯一的优化尝试是在使用常规<$ c $之前比较起始字符c> string.h 字符串比较功能可检查您指定的搜索字符串。该函数返回进行替换的次数,因此您可以确定是否需要写出原始文件名:

Once you have the file in memory, the second piece of the puzzle is simply to scan through the buffer and make the replacements you need. Here there are a number of different tweaks you can apply to optimize the search/replace, but the following is just a straight forward basic search/replace where the only optimization attempt is a comparison of the starting character before using the normal string.h string comparison functions to check for your specified search string. The function returns the number of replacements made so you can determine whether a write out to the original filename is required:

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 )。给出文件名)。总是还可以包括其他验证检查:

Putting all the pieces together in a short example program using your sample data could be written as follows. The program expects the filename as the first argument (or it will read from stdin and write to stdout by default if no filename is given). There are always additional validation checks you can include as well:

#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;
}

只需在文件底部添加函数即可。然后,您可以:

Just include the functions at the bottom of the file. You can then:

编译

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

$ ./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

内存/错误检查

在您可以动态分配内存的任何代码中,您对任何块的内存都有2种责任分配的内存:(1)始终保留指向该内存块起始地址的指针,因此,(2)不再需要它时可以将其释放。

In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

必须使用一个内存错误检查程序来确保您没有在所分配的内存块之外/之外进行写操作,试图读取或基于其跳转一个未初始化的值,最后确认您已释放所有已分配的内存。

It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.

对于Linux valgrind 是正常选择。有许多微妙的方法来滥用新的内存块。使用内存错误检查器可以识别任何问题并验证分配的内存的正确使用,而不是通过 segfault 找出存在的问题。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。例如:

For Linux valgrind is the normal choice. There are many subtle ways to misuse a new block of memory. Using a memory error checker allows you to identify any problems and validate proper use of of the memory you allocate rather than finding out a problem exist through a segfault. There are similar memory checkers for every platform. They are all simple to use, just run your program through it. E.g.:

$ 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)

您要确认所有堆块都已释放-不可能泄漏错误摘要:来自0个上下文的0个错误(忽略被抑制的注释,该注释仅与缺少的未安装在系统上的 debug 符号文件有关)

You want to confirm All heap blocks were freed -- no leaks are possible and ERROR SUMMARY: 0 errors from 0 contexts (ignore the suppressed note which simply relates to missing debug symbol files not installed on my system)

查看代码,了解它在做什么。这不是作为您尝试做的唯一方法,而是作为解决问题的方法的一个示例,同时避免了尝试一次更改一条线路时固有的陷阱在现有文件中利用偏移量和对该文件的多次读/写操作。让我知道您是否有疑问。

Look over the code and understand what it is doing. This isn't presented as the only way of doing what you are attempting to do, but it is presented as an example of how to approach the problem while avoiding a number of pitfalls inherent in trying to change a line-at-a-time in an existing file utilizing offsets and a number of reads/writes to the file. Let me know if you have questions.

这篇关于如何在C中编辑txt文件的特定行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆