如何在C中编辑txt文件的特定行
How to edit a specific line of a txt file in C
我目前正在尝试在 C 中编辑 .txt 文件的特定行。我使用的文件如下所示:
现在假设我想更改在图像上突出显示的特定行上写的内容:
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");
}
好的,所以在阅读了您在下面放置的内容后,我想出了一个主意,但仍然需要工作。我会打印从文件到字符数组的所有内容。之后我会在每个插槽中搜索我正在寻找的特定代码行并保留数字插槽。在那之后,我会去数组,运行 它,当涉及到那个特定的插槽时,我会替换所需的数据。现在我需要做的就是将文件中的信息交换为数组中的信息,保存文件并解决问题。但是我在代码中遇到错误,并且我遗漏了一些可以清除 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);
}
txt文件一般不能写特定行。
实际上,txt文件只是一个字节序列。每行仅由特殊符号'\n'(或符号'\r'、'\n':有两种方法)相互分隔。
因此,如果您重写某行,则必须在新行之后移动文件中保留的数据(行)。
但是如果你的新行和以前的长度一样,你可以放心地把它写在旧行上。
我能想到的最好的方法是以只读模式打开文件,然后通过以 'w+' 模式打开一个新文件将所有内容复制到一个新文件夹。然后在读取文件中逐行查找,直到找到要更改的行,然后在新的副本文件中自己重写该行。然后跳过读取文件中的那一行并继续。
复制文件是你想要的后,你可以把它的名字替换成你想要的原始文件名。然后它会像您编辑文件一样起作用。
您缺少的是一些概念性的,并且与 fopen
有点相关。当您考虑使用 fopen
打开文件时,您需要特别注意 文件模式 的影响。如果您仔细查看有关 "w"
或 "w+"
的 手册页 。在这两种情况下,现有文件都是 t运行cated。以0-length
为例 "w"
.
为避免此问题,一种方法是将整个文件读入缓冲区,然后对缓冲区进行更改,将修改后的缓冲区写回原始文件名。这避免了在不重写文件其余部分的情况下尝试 insert/delete 字节的可能性。
为了处理将文件读入缓冲区,link 发布的 overwriting a specific line on a text file? 提供了更改文件中单行的路线图。你的情况不同。您想要 find/replace All 个特定模式的出现。 (这就是 t运行cation 问题面临挑战的地方)然而,那里的大部分解决方案都可以应用于将文件本身读入缓冲区。具体使用fseek
和ftell
.
使用 fseek
和 ftell
提供了一种简单的方法来确定文件的大小(或长度),然后可用于分配 space 以将整个文件保存在记忆。下面是一个简单函数的一种方法,它获取字符指针和文件指针的地址,然后使用 fseek
和 ftell
分配所需的内存来保存文件,然后将文件读入缓冲区(filebuf
) 在与 fread
的单个操作中。缓冲区已就位填充,并且 returned。指向文件长度 fplen
的指针被传递给函数,因此该长度在调用函数中可用(在本例中为 main()
)。成功返回指向缓冲区的指针(NULL
否则)将允许分配 return,如果需要,以及一种确定读取的 success/failure 的方法:
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;
}
将文件存入内存后,第二个难题就是简单地扫描缓冲区并进行所需的替换。这里有许多不同的调整可以用来优化 search/replace,但以下只是一个简单的基本 search/replace,其中唯一的优化尝试是在使用普通字符之前比较起始字符string.h
字符串比较函数,用于检查您指定的搜索字符串。函数 return 表示替换次数,因此您可以确定是否需要写出原始文件名:
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
Use/File替换后
$ ./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
Memory/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。
您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块,试图读取或基于未初始化的值进行跳转,并最终确认你已经释放了你分配的所有内存。
对于Linux valgrind
是正常的选择。有许多微妙的方法可以滥用新的内存块。使用内存错误检查器可以让您识别任何问题并验证您分配的内存是否正确使用,而不是通过 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
和 ERROR SUMMARY: 0 errors from 0 contexts
(忽略 suppressed 注释,它仅与缺少 debug 我的系统上没有安装符号文件)
查看代码并了解它在做什么。这并不是做你想做的事情的唯一方法,而是作为一个例子来说明如何解决这个问题,同时避免尝试更改 line-at-a-time 中的一些固有陷阱。现有文件利用文件的偏移量和 reads/writes 的数量。如果您有任何问题,请告诉我。
我目前正在尝试在 C 中编辑 .txt 文件的特定行。我使用的文件如下所示:
现在假设我想更改在图像上突出显示的特定行上写的内容:
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");
}
好的,所以在阅读了您在下面放置的内容后,我想出了一个主意,但仍然需要工作。我会打印从文件到字符数组的所有内容。之后我会在每个插槽中搜索我正在寻找的特定代码行并保留数字插槽。在那之后,我会去数组,运行 它,当涉及到那个特定的插槽时,我会替换所需的数据。现在我需要做的就是将文件中的信息交换为数组中的信息,保存文件并解决问题。但是我在代码中遇到错误,并且我遗漏了一些可以清除 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);
}
txt文件一般不能写特定行。
实际上,txt文件只是一个字节序列。每行仅由特殊符号'\n'(或符号'\r'、'\n':有两种方法)相互分隔。
因此,如果您重写某行,则必须在新行之后移动文件中保留的数据(行)。
但是如果你的新行和以前的长度一样,你可以放心地把它写在旧行上。
我能想到的最好的方法是以只读模式打开文件,然后通过以 'w+' 模式打开一个新文件将所有内容复制到一个新文件夹。然后在读取文件中逐行查找,直到找到要更改的行,然后在新的副本文件中自己重写该行。然后跳过读取文件中的那一行并继续。 复制文件是你想要的后,你可以把它的名字替换成你想要的原始文件名。然后它会像您编辑文件一样起作用。
您缺少的是一些概念性的,并且与 fopen
有点相关。当您考虑使用 fopen
打开文件时,您需要特别注意 文件模式 的影响。如果您仔细查看有关 "w"
或 "w+"
的 手册页 。在这两种情况下,现有文件都是 t运行cated。以0-length
为例 "w"
.
为避免此问题,一种方法是将整个文件读入缓冲区,然后对缓冲区进行更改,将修改后的缓冲区写回原始文件名。这避免了在不重写文件其余部分的情况下尝试 insert/delete 字节的可能性。
为了处理将文件读入缓冲区,link 发布的 overwriting a specific line on a text file? 提供了更改文件中单行的路线图。你的情况不同。您想要 find/replace All 个特定模式的出现。 (这就是 t运行cation 问题面临挑战的地方)然而,那里的大部分解决方案都可以应用于将文件本身读入缓冲区。具体使用fseek
和ftell
.
使用 fseek
和 ftell
提供了一种简单的方法来确定文件的大小(或长度),然后可用于分配 space 以将整个文件保存在记忆。下面是一个简单函数的一种方法,它获取字符指针和文件指针的地址,然后使用 fseek
和 ftell
分配所需的内存来保存文件,然后将文件读入缓冲区(filebuf
) 在与 fread
的单个操作中。缓冲区已就位填充,并且 returned。指向文件长度 fplen
的指针被传递给函数,因此该长度在调用函数中可用(在本例中为 main()
)。成功返回指向缓冲区的指针(NULL
否则)将允许分配 return,如果需要,以及一种确定读取的 success/failure 的方法:
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;
}
将文件存入内存后,第二个难题就是简单地扫描缓冲区并进行所需的替换。这里有许多不同的调整可以用来优化 search/replace,但以下只是一个简单的基本 search/replace,其中唯一的优化尝试是在使用普通字符之前比较起始字符string.h
字符串比较函数,用于检查您指定的搜索字符串。函数 return 表示替换次数,因此您可以确定是否需要写出原始文件名:
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
Use/File替换后
$ ./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
Memory/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。
您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块,试图读取或基于未初始化的值进行跳转,并最终确认你已经释放了你分配的所有内存。
对于Linux valgrind
是正常的选择。有许多微妙的方法可以滥用新的内存块。使用内存错误检查器可以让您识别任何问题并验证您分配的内存是否正确使用,而不是通过 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
和 ERROR SUMMARY: 0 errors from 0 contexts
(忽略 suppressed 注释,它仅与缺少 debug 我的系统上没有安装符号文件)
查看代码并了解它在做什么。这并不是做你想做的事情的唯一方法,而是作为一个例子来说明如何解决这个问题,同时避免尝试更改 line-at-a-time 中的一些固有陷阱。现有文件利用文件的偏移量和 reads/writes 的数量。如果您有任何问题,请告诉我。