Find specific file in folder and edit it - c

I am really new to C, so maybe the question is too easy or not relevant. But I didn't find answer to this question.
I have folder with files which named as: -90.txt, -89.txt, ..., 0.txt, 1.txt, 2.txt, ..., 90.txt.
I need to have some function which will receive number (folder name) as an argument and open that file.
function editFile(number){
fp=fopen("/data/" + number + ".txt", "wr");
...
// do some other things
}

In C you do not have flexibility to add a number to a string. If you want to append a number to a string then first you have to convert it into a string using itoa function. You can find online documentation about that function.
And here is general outline how would you add a number to string in C
First convert the number into string (store it in a new buffer of type char[]) using itoa or sprintf
Then make a new buffer of type char[]. Make sure you create it of enough size so that it can hold complete file path.
Then use strcat to append the two strings.
Instead of all the above process, you can also use sprintf.
Then you can pass the result to fopen

C doesn't have strings with the idea of concatenation. So if you want to concatenate strings you have to use functions like this:
function editFile(char* number)
{
char pathName[MAX_PATH];
strcpy(pathName, "/data/");
strcat(pathName, number);
strcat(pathName, ".txt");
fp=fopen(pathName, "wr");
...
// do some other things
}
You can utilize sprintf to make it shorter. But you should add security checks because you have the risk of buffer overrun. Hint: strcpy_s, strccat_s, sprintf_s. (But this' beyond the answer to your question.)

In C you cannot build a string in that way. Use sprintf to create the file name in a local variable.
char filename[1024];
sprintf(filename, "/data/%d.txt", number); // assume that a signed value comes from `int`
fp = fopen(filename, "w+"); // replaced "wr"

You are probably used to high level languages where you can "build" a string by concatenating integers, floats, other strings, etc. to it. This doesn't work in C.
As commented #WeatherVane pointed out, the sprintf function is the right tool for the job:
char buffer[PATH_MAX]; // assuming POSIX
sprintf(buffer, "/data/%d.txt", number);
fp = fopen(buffer, "wr");

Related

Changing char* string in a C loop

I am trying to change a string within a loop to be able to save my images with a changing variable. Code snippet is as follows:
for (frames=1; frames<=10; frames++)
{
char* Filename = "NEWIMAGE";
int Save_Img = is_SaveImageMemEx (hCam, Filename, pMem, memID,
IS_IMG_PNG, 100);
printf("Status Save %d\n",Save_Img);
}
What I want to do is put a variable that changes with the loop counter inside Filename so my saved file changes name with every iteration.
Any help would be great.
Create a file name string with sprintf and use the %d format conversion specifier for an int:
char filename[32];
sprintf(filename, "NEWIMAGE-%d", frames);
sprintf works just like printf, but "prints" to a string instead of stdout.
If you declared frames as an unsigned int, use %u. If it is a size_t use %zu. For details see your friendly printf manual page, which will tell you how you can for example zero pad the number.
Be sure that the character array you write to is large enough to hold the longest output plus an extra '\0' character. In your particular case NEWIMAGE-10 + 1 means 11 + 1 = 12 characters is enough, but 32 is future-proof for some time.
If you want to program like a pro, look at the snprintf and asnprintf functions, which can limit or allocate the memory written to, respectively.
You can use sprintf to create a formatting string:
char Filename[50];
sprintf(Filename, "NEWIMAGE%d", frames);

sscanf to get segment of string surrounded by two fixed strings

I'm trying to remove the extension of a file (I know it is .txt) using sscanf(). I've tried with many format strings I think may work, but with no success. The main problem is that I just can't understand sscanf()'s documentation, so I don't get how to use this [=%[*][width][modifiers]type=] I've tried to tell it that end must be ".txt" or to save initial string in a variable and a %4ccorresponding to the extension in another one, but again… can't make it work.
I know this has been asked before here: sscanf: get first and last token in a string but as I said... I don´t understand its solution.
The part of my code that does that:
sscanf(fileName,"the_sender_is_%s%*[.txt]", sender);
The input file name is, for example: "the_sender_is_Monika.txt"
In sender I should have
Monika
but whatever I try gives me
Monika.txt
When you use
sscanf(fileName,"the_sender_is_%s%*[.txt]", sender);
The function reads as much as it can with %s before it processes %*[.txt].
Use
sscanf(fileName,"the_sender_is_%[^.]", sender);
While sscanf() is powerful, it is not the universal tool. There are limits on what you can do with it, and you're hitting them. A moderate approximation to the task would be:
char body[32];
char tail[5];
if (sscanf("longish-name-without-dots.txt", "%31[^.]%4s", body, tail) != 2)
…oops — can't happen with the constant string, but maybe with a variable one…
This gets you longish-name-without-dots into body and .txt into tail. But it won't work all that well if there are dots in the name part before the extension.
You're probably looking for:
const char *file = "longish-name.with.dots-before.txt";
char *dot = strrchr(file, '.');
if (dot == NULL)
…oops — can't happen with the literal, but maybe with a variable…
strcpy(tail, dot); // Beware buffer overflow
memcpy(body, file, dot - file);
body[dot - file] = '\0';

Removing a file in C states that the file wasn't found

I am working on a c project and I need to remove a file from within a directory. For some reason though it keeps on saying that it can't delete because the file or directory doesn't exist. Below is the code that I am using to remove the file.
void deleteOldestLog()
{
FILE *fp;
char path[FILE_PATH_BUF_LEN], *fileName;
fp = popen("ls -tr /home/myfolder/logs/ |head -1", "r");
if (fp == NULL)
{
printf("Failed to run command");
}
else
{
char removalPath[FILE_PATH_BUF_LEN];
while ((fileName = fgets(path, sizeof(path)-1, fp)) != NULL)
{
sprintf(removalPath, "/home/myfolder/logs/%s", fileName, sizeof(fileName)-1);
printf("Removing file: %s", removalPath);
if (remove(removalPath) != 0)
{
perror("ERROR DELETING LOG");
}
else
{
printf("Successfully deleted %s", removalPath);
}
break;
}
pclose(fp);
}
}
Even though it says that it can't find the file because it doesn't exist I know that this isn't true because if I run ll followed by the path that the c program printed it returns the file that I am trying to delete.
I think it might be because fgets is putting '\0' on the end of the string which is stopping the remove from working.
How can I fix this?
There's a newline at the end of the file name read by fgets(). Your file name doesn't actually end with a newline.
You attempt to remove the newline with:
sprintf(removalPath, "/home/myfolder/%s", fileName, sizeof(fileName)-1);
However, to be effective, you'd need to use strlen() instead of sizeof(), and you'd need to modify the format string:
sprintf(removalPath, "/home/myfolder/%.*s", (int)strlen(fileName)-1, fileName);
The argument for the * must be an int and strlen() returns a size_t; hence the cast. (GCC will warn about that sort of thing if you turn on the warnings; use at least -Wall.)
A tip for you: when in doubt, print the string. I'll typically use a format like this. Note the angle brackets around the string:
printf("Removing: <<%s>>\n", removalPath);
When you see:
Removing: <</home/myfolder/something
>>
you know there's a problem with a newline in the string. Without the markers, you might not notice that there's a newline in the string causing the extra newline in the output.
Why does the format string need to be modified?
Let's look at the original sprintf() again:
sprintf(removalPath, "/home/myfolder/%s", fileName, sizeof(fileName)-1);
The format string expects 1 argument, a string. The call provides two values, a string and a length. So, the first problem is that there is a left-over argument. This usually does no damage, but be aware of it. Presumably, the reason for passing the length minus one was to lose the last character. The formats in the printf() family can be adorned with one or two numbers, and either or both can have a * instead of an integer value. These numbers constrain the lengths of the formatted value. When you write:
%.*s
you state the length of the output shall be exactly the length specified by an int value passed as an argument before the string itself. Hence the revision:
sprintf(removalPath, "/home/myfolder/%.*s", (int)strlen(fileName)-1, fileName);
(which I just fixed while adding this information.)
I've also not added error checking to the output of sprintf() etc. That's not unusual; however, best coding practices do ensure that functions like sprintf() return the value you expect (which is the number of characters written to the string, excluding the trailing null '\0'.
(Aside: in general, it is better to use snprintf() than sprintf(); that can avoid buffer overflows.
snprintf(removalPath, sizeof(removalPath), "/home/myfolder/%.*s",
(int)strlen(fileName)-1, fileName);
However, the behaviour of the *snprintf() functions under MSVC is different from the behaviour mandated by the C Standards (C99, C11). Worse, in the case of vsnprintf_s() and the other _s functions, the argument lists are different between the MSVC and the C Standard.)

fgets combined with sscanf

Today I've looked over some C code that was parsing data from a text file
and I've stumbled upon these lines
fgets(line,MAX,fp);
if(line[strlen(line)-1]=='\n'){
line[strlen(line)-1]='\0');
}else{
printf("Error on line length\n");
exit(1);
}
sscanf((line,"%s",records->bday));
with record being a structure
typedef struct {
char bday[11];
}record;
So my question here regards the fgets-sscanf combination to create a type/length safe stream reader:
Is there any other way to work this out beside having to combine these two readers?
What about the \n checking-removing sequence?
The combination of fgets() with sscanf() is usually good. However, you should probably be using:
if (fgets(line, sizeof(line), fp) != 0)
{
...
}
This checks for I/O errors and EOF. It also assumes that the definition of the array is visible (otherwise sizeof gives you the size of a pointer, not of the array). If the array is not in scope, you should probably pass the size of the array to the function containing this code. All that said, there are worse sins than using MAX in place of sizeof(line).
You have not checked for a zero-length birthday string; you will probably end up doing quite a lot of validation on the string that is entered, though (dates are fickle and hard to process).
Given that MAX is 60, but sizeof(records->bday) == 11, you need to protect yourself from buffer overflows in the sscanf(). One way to do that is:
if (sscanf(line, "%10s", records->bday) != 1)
...handle error...
Note that the 10 is sizeof(records->bday) - 1, but you can't provide the length as an argument to sscanf(); it has to appear in the format string literally. Here, you can probably live with the odd sizing, but if you were dealing with more generic code, you'd probably think about:
sprintf(format, "%%%zus", sizeof(records->bday) - 1);
The first %% maps to %; the %zu formats the size (z is C99 for size_t); the s is for the string conversion when the format is used.
Or you could consider using strcpy() or memcpy() or memmove() to copy the right subsection of the input string to the structure - but note that %10s skips leading blanks which strcpy() et al will not. You have to know how long the string is before you do the copying, of course, and make sure the string is null terminated.

Reading and comparing numbers from txt file C

I am new to C programming, so I am having difficulties with the problem below.
I have a text file inp.txt which contains information like the following:
400;499;FIRST;
500;599;SECOND;
670;679;THIRD;
I need to type a number and my program needs to compare it with numbers from the inp.txt file.
For example, if I type 450, it's between 400 and 499, so I need write to the word FIRST to the file out.txt
I have no idea how to convert a character array to an int.
I think you'll want these general steps in your program (but I'll leave it to you to figure out how you want to do it exactly)
Load each of the ranges and the text "FIRST", "SECOND", etc. from the file inp.txt, into an array, or several arrays, or similar. As I said in the comment above, fscanf might be handy. This page describes how to use it - the page is about C++, but using it in C should be the same http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/. Roughly speaking, the idea is that you give fscanf a format specifier for what you want to extract from a line in a file, and it puts the bits it finds into the variables you specify)
Prompt the user to enter a number.
Look through the array(s) to work out which range the number fits into, and therefore which text to output
Edit: I'll put some more detail in, as asker requested. This is still a kind of skeleton to give you some ideas.
Use the fopen function, something like this (declare a pointer FILE* input_file):
input_file = fopen("c:\\test\\inp.txt", "r") /* "r" opens inp.txt for reading */
Then, it's good to check that the file was successfully opened, by checking if input_file == NULL.
Then use fscanf to read details from one line of the file. Loop through the lines of the file until you've read the whole thing. You give fscanf pointers to the variables you want it to put the information from each line of the file into. (It's a bit like a printf formatting specifier in reverse).
So, you could declare int range_start, range_end, and char range_name[20]. (To make things simple, let's assume that all the words are at most 20 characters long. This might not be a good plan in the long-run though).
while (!feof(input_file)) { /* check for end-of-file */
if(fscanf(input_file, "%d;%d;%s", &range_start, &range_end, range_name) != 3) {
break; /* Something weird happened on this line, so let's give up */
else {
printf("I got the following numbers: %d, %d, %s\n", range_start, range_end, range_name);
}
}
Hopefully that gives you a few ideas. I've tried running this code and it did seem to work. However, worth saying that fscanf has some drawbacks (see e.g. http://mrx.net/c/readfunctions.html), so another approach is to use fgets to get each line (the advantage of fgets is that you get to specify a maximum number of characters to read, so there's no danger of overrunning a string buffer length) and then sscanf to read from the string into your integer variables. I haven't tried this way though.

Resources