How read lines of file and get them to array in C? - c

I would like to create a function to read file line by line. One every line is one name.
int readConfig(char ** path, FILES ** files )
{
FILE* f;
f = fopen("file", "r");
int ch;
while ((ch=fgetc(f)) != EOF )
{
}
return 0;
}
How to use the fgetc function to parse the file? And how to get the results to the files[count].name?

Right off the bat:
char configFile [11] = "kernels.cfg";
[11] is too small. Try:
char configFile [12] = "kernels.cfg";
or
char configFile [] = "kernels.cfg"; /* let the compiler do the counting */
Also char is too small for ch -- use:
int ch;
You may also find fgets() -- which reads a whole line at at time -- simpler to use than fgetc().

You are getting SIGSEGV because of modifying string literal and that causes an undefined behavior (e.g. your SIGSEGV). I am not sure what should be stored in filename and name variables. If by line:
strcpy(files[count].filename,'.bin');
you've meant to add a '.bin' to filename variable, then this approach is wrong. You should use strcat. strcpy would write to filename from beginning of this variable, so some chars previously saved there would be overwritten. strcpy also adds a null termination char, so if you wanted to print it out, printf would stop on that \0 char and won't go further. However, the real problem is that you should allocate with malloc some space for your variables in struct. Then you will be able to modify them.
Consider simple example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct file {
char* name;
char* filename;
};
int main(void)
{
struct file x;
x.name = malloc(30);
x.filename = malloc(40);
strncpy(x.name, "copied_string", 13);
printf("%s\n", x.name);
strcat(x.name, "_suffix");
printf("%s\n", x.name);
strcpy(x.name, "erased");
printf("%s\n", x.name);
free(x.name);
free(x.filename);
return 0;
}
output:
copied_string
copied_string_suffix
erased
This should make it a little more clear what's the origin of your crash. You should also consider using fgets or getline. Remember to free what you've malloc'd.
EDIT:
Calling readConfig(&path, &files); results in passing to readConfig a pointer of type FILES (*)[256]. Consider changing FILES files[256]; to
FILES* files = malloc(sizeof(FILES)*256);
and later call your function like readConfig(&path, files);
Then you would pass to readConfig function a compatible type of files.

Related

Function that modifies string and returns the modified string

I have a function that takes a string from a macro. It modifies the string and then returns a new modified string. My intention is to use this returned string. However, it does not work, as it does not return the modified string.
#include <stdio.h>
#include "string.h"
#define ENCRYPTED_FILE "hello.txt"
char *decrypt(){
char str[]=ENCRYPTED_FILE;
strtok(str,".txt");
strcat(str,"_decrypted.txt");
//printf("%s\n",str);
return str;
};
int main()
{
printf("%s\n",decrypt()); //output: *** stack smashing detected ***: ./a.out terminated
return 0;
}
For starters the function returns a pointer to the first element of the local array str with automatic storage duration that will not be alive after exiting the function.
So as a result the function returns an invalid pointer.
You need to allocate memory for the array dynamically.
Also this call of strtok
strtok(str,".txt");
does not make a sense. The function does not search the sub-string ".txt". It searches the first character of the set of characters specified by the string ".txt". Instead you could use the function strstr.
And this code strcat
strcat(str,"_decrypted.txt");
invokes undefined behaviour because the target array does not have enough space to store the appended string literal.
The function can look for example like it is shown in the demonstrative program below.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ENCRYPTED_FILE "hello.txt"
char *decrypt( void )
{
const char *encrypted_file = ENCRYPTED_FILE;
const char *p = strstr( encrypted_file, ".txt" );
if ( p == NULL ) p = encrypted_file + strlen( encrypted_file );
size_t n = p - encrypted_file;
const char *decrypted_file = "_decrypted.txt";
char *s = malloc( n + strlen( decrypted_file ) + 1 );
memmove( s, encrypted_file, n );
s[n] = '\0';
strcat( s + n, decrypted_file );
return s;
}
int main(void)
{
char *s = decrypt();
puts( s );
free( s );
return 0;
}
The program output is
hello_decrypted.txt
You're returning a pointer to a temporary array which becomes dangling after control leaves the function. First, you'll have to allocate it on heap, via malloc, and next, make sure its allocated size is enough to contain the original size plus the extra "_decrypted" suffix.
I just noticed your library notation as #include "string.h" instead of #include <string.h> that might solve the problem.
IMHO you are making bad use of the strtok() function. It will split a string into substrings each time it finds a dot ., a t, or an x. As you have written the code I'm afraid this is not what you want (to eliminate the .txt suffix?)
Read the manual page of strtok() as it will explain exactly what this function actually does.
On the other side, you cannot truncate the string at the start of .txt and then append a longer string to it. When you declared the str[] array (by explicitly not using a length) the compiler reserved as many characters to hold the text comming from the macro, plus one more to hold the \0 delimiter. So your array has only space to hold 10 characters (the 9 of "hello.txt", plus one for the '\0' end of string). Of course, there's no place there to hold hello_decripted.txt which would need 19 characters plus one more for the \0. A workaround to this problem could be to indicate in the array declaration how many characters you want the compiler to use, as in:
char str[100] = ENCRYPTED_FILE;
and then you can extend upto 100 characters (99 plus a holder for the string end char \0).
If you find the string you search for (.txt) and put a \0 in the first position of it, you'll truncate the original string and you'll be able to do what you actually want, that is:
#include <stdio.h>
#include <stdlib.h>
#include "string.h" /* is this what you actually mean and not <string.h>? */
#define ENCRYPTED_FILE "hello.txt"
char *decrypt(){
char str[100]=ENCRYPTED_FILE;
char *p = strstr(str,".txt");
if (p != NULL) { /* the string actually has a .txt suffix */
*p = '\0'; /* string truncated */
}
strcat(str,"_decrypted.txt"); /* add new suffix */
//printf("%s\n",str);
/* you cannot return str as str is a local variable,
* and it will cease to exist as soon as we leave this
* function body, better return a new dynamically
* allocated string (that need to be freed with free(3)
*/
return strdup(str);
};
int main()
{
/* the stack smashing probably is due to returning the
* address of a local variable, that ceased to exist.
*/
char *name = decrypt();
printf("%s\n", name);
free(name); /* return the memory allocated in decrypt() */
return 0;
}
This will solve the problem respecting your intentions. But you are wrong in another point:
What if the string .txt appears just before the end of the original name? In my opinion, what you are searching for is for a .txt suffix (what was earlier known as an extension) What impedes your file to be named something like blahblah.txt01.txt? --which has two occurences of the substring .txt--) This is not the correct algorithm to search for a .txt suffix. The correct way is to search if .txt is at the end of the string, and for that, the algorithm to use is different (and far more efficient):
char *decrypt(){
char str[100]=ENCRYPTED_FILE;
char *suff = ".txt";
/* go to the point that is strlen(str) further than
* the beginning of the string minus the string
* of the suffix */
char *p = str + strlen(str) - strlen(suff);
if (strcmp(p, suff) == 0) { /* the string actually has a .txt suffix */
*p = '\0'; /* string truncated */
}
/* from this point on, everything goes the same */
strcat(str,"_decrypted.txt"); /* add new suffix */
//printf("%s\n",str);
return strdup(str);
};
in this case you only need to do one string comparison (which is done multiple times in the body of strstr() to search for a full match) and you'll know if it fails or not quickly and efficiently.
Note
A final note about the #include "string.h" line in your code: Including a file with double quotes instead of a pair of <> characters is ok if you have a local file (in your local directory) that is called the same as some library file, because that will make it to be found before the system library one. But it is a bad habit if you include the standard library include file, because if you later decide to create an include file (in other module or program) and create a local string.h file, this program will suddenly start to compile with errors and you'll not guess why. Be careful about #include names and the two ways of calling them. Files named as <file.h> are normally standard library include files, and are searched for in fixed places in the system. Files named as "file.h" are searched first in the working directory, and if not found, then they are searched in the library fixed paths. Try to use " only for your files or files you have in your build directory, and search system files only with < & >.

The variable 'buff' is being used without being defined

I need to read some data from text file and store it in 2D-array.
This code works good:
#include <string.h>
#include <stdio.h>
int main() {
FILE *f = fopen("Read.txt", "r");
char buff[100][100];
char str[100];
int i = 0;
while(fgets(str, 100, f)) {
strcpy(buff[i], str);
i++;
}
return 0;
}
But why doesn't it work when I try to change buff definition in line 5 to:
char (*buff)[100];
I expected this definition to work too.
The error I get:
Run-Time Check Failure #3 - The variable 'buff' is being used without being defined
char (*buff)[100];
Here buff is a pointer to an array of 100 characters. So first you should make the pointer point to valid memory location before storing some value in it.
Presuming you want to go for dynamic memory allocation then you can have
char *buff[100];
Now in the fgets() loop allocate memory to each pointer individually like
buff[i] = malloc(100);
Note here buff is an array of 100 char pointers.

How to make backslash character not to escape

I don't know the title correctly addresses my problem or not. So, I will just go with it.
Here is the problem, I have to input a char array of a file path (in Windows) containing lots of backslashes in it, eg. "C:\myfile.txt" and return an unsigned char array of C-style file paths, eg. "C:\myfile.txt".
I tried to write a function.
unsigned char* parse_file_path(char *path);
{
unsigned char p[60];
int i,j;
int len = strlen(path);
for(i=0,j=0; i<len; i++, j++)
{
char ch = path[i];
if(ch==27)
{
p[j++]='\\';
p[j]='\\';
}
else
p[j] = path[i];
}
p[j]='\0';
return p;
}
The weird thing (for me) I am encountering is, here path contains only one backslash '\'. In order to get one backslash, I have to put '\' in path. This is not possible, cause path cannot contain '\'. When I call it like this parse_file_path("t\es\t \it), it returns
t←s it. But parse_file_path("t\\es\\t \\it") returns t\es\t \it.
How can I accomplish my task? Thanks in advance.
If I can just mention another problem with your code.
You are returning a local variable (your unsigned char p). This is undefined behavior. Consider declaring a char* p that you assign memory to dynamically using malloc and then returning p as you do. E.g. something like:
char* p = malloc(60);
A common practice is to use sizeof when allocating memory with malloc but here I've passed 60 directly as the C standard guarantees that a char will be 1 byte on all platforms.
But you have to free the memory assigned with malloc.
Or alternatively, you can change the function to take a buffer as an input argument that it then writes to. That way you can pass a normal array where you would call this function.
Regarding your slashes issue, here:
p[j++]='\\';
p[j]='\\';
Position j in p will be changed to \\, then j will be incremented and at the very next line you do the same for the succeeding char position. Are you sure you want the two assignments?
By the way if you are inputting the path from the command line, the escaping will be taken care of for you. E.g. consider the following code:
#include <stdio.h>
#include <string.h> /* for strlen */
#include <stdlib.h> /* for exit */
int main()
{
char path[60];
fgets(path, 60, stdin); /* get a maximum of 60 characters from the standard input and store them in path */
path[strlen(path) - 1] = '\0'; /* replace newline character with null terminator */
FILE* handle = fopen(path, "r");
if (!handle)
{
printf("There was a problem opening the file\n");
exit(1); /* file doesn't exist, let's quite with a status code of 1 */
}
printf("Should be good!\n");
/* work with the file */
fclose(handle);
return 0; /* all cool */
}
And then you run it and input something like:
C:\cygwin\home\myaccount\main.c
It should print 'Should be good!' (provided the file does exist, you can also test with 'C:\').
At least on Windows 7 with cygwin this is what I get. No need for any escapes as this is handled for you.

Error reading char* type from .DAT file with C

So, for some reason, I need to make a external file (.DAT) to store data by appending the new one to the end of old data.
#include <stdio.h>
#include <stdlib.h>
int main () {
typedef struct {
char *Name;
int Index;
} DataFile;
static FILE *file;
size_t result;
DataFile *DataTable;
file = fopen("database.DAT","ab");
DataTable = (DataFile *) malloc (sizeof(DataFile));
DataTable[0].Name = "somefile.txt";
DataTable[0].Index = 7;
printf("%s %d \n",DataTable[0].Name,DataTable[0].Index);
result = fwrite(DataTable,sizeof(DataFile),1,file);
fclose(file);
free(DataTable);
return 0;
}
After running code above, I then check if the data stored correctly. So, I make this code below.
#include <stdio.h>
#include <stdlib.h>
int main () {
typedef struct {
char *Name;
int Index;
} DataFile;
static FILE *file;
size_t result;
long size;
int i;
DataFile *DataTable;
file = fopen("database.DAT","rb");
if (file == NULL) printf("Error1");
// Determine the size of file
fseek(file,0,SEEK_END);
size = ftell(file);
rewind(file);
DataTable = (DataFile *) malloc ((size/sizeof(DataFile)) * sizeof(DataFile));
if (DataTable == NULL) printf("Error2");
result = fread(DataTable,sizeof(DataFile),size/sizeof(DataFile),file);
fclose(file);
for (i=0; i<result; i++) {
printf("%s %d \n",DataTable[i].Name,DataTable[i].Index);
}
free(DataTable);
return 0;
}
However, it gives output
somefile.txt 7
from the first code block and
Error1 7
from the second code block.
I notice that the problem is not because the failure either when opening .DAT file or when allocating memory for DataTable. Also, it works for int type (Index) but not for char* type (Name) when reading from .DAT file. I have no idea what to do to solve this char*-type-reading problem (and where 'error1' comes from). (not even google gives me answer.)
Your structure DataFile stores one pointer and one integer. When you write it to the file, you write some program specific pointer to a string, and an integer.
When reading from it, you just refill your structure with the pointer and the integer, wich means that DataFile.Name will be a pointer to a probably-not-initialized memory segment. But since you created your file pointing to the first hard-coded string ("filename.txt"), some undefined but understandable behaviour happens, and your pointer in this case points to the first hard-coded string you wrote in you second program (which in your case is Error1)
What you really want to do is write the real string in your file.
A simple solution, if you want to the keep the hole writing structure thing is to create an array instead of a pointer
typedef struct {
char Name[512];
int Index;
} DataFile;
then initialize your data with
strncpy(DataTable[0].Name, "somefile.txt", sizeof(DataTable[0].Name) - 1); // just to make sure you dont overflow your array size
DataTable[0].Name[sizeof(DataTable[0].Name) - 1] = '\0';
and retreview your data the way you did.
A char* is only a pointer, i.e. the address of the character array containing your strings. You don't write the strings themselves to the file. After reading the file, as the same strings aren't in your memory at the same addresses any more, the application will fail.
You'll have to come up with a way to save the strings themselves to file as well. Probably by first writing their length, and then writing their content. Upon reading, you can use the length information to allocate memory dynamically, then read into that memory.
In your writing code you haven't allocated storage for char *Name. When you perform the DataTable[0].Name = "somefile.txt" instruction you're not actually copying the "somefile.txt" into memory pointed by Name, it's actually assigning a Name a value pointing to a constant characters string (moreover, it will become dangling pointer since the string is an rvalue, i.e. doesn't have a memory to be addressed via). Same goes for your file reading code.
You need to:
Allocate storage for your Name.
Copy the string using memcpy or similar into the allocated storage.

This variable declaration can creates memory issue

here i have one function which is call many times in one application this application is running continuously.
Here i am take one character array size of 1024.
here i am declaring char input[1024];
so which is best way
1) char input[1024];
2) char input[1024] = NULL;
this thing will not create any memory issue after so many times if we used this function.char input[1024];
i think may be after using input we have to make NULL ?
or in declaration we have to declare this thing as char input[1024] = NULL; so when it will be called next time so that time first input make null than its taking any memory.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
void do() {
char input[1024];
strcat(input,"ussp-push 04:18:0F:B1:48:B5#9");
strcat(input,"> dev/null&");
if(system(input) != 0)
{
printf("\nFailed command\n");
}
else
{
printf("\nSuccesss command\n");
}
}
Your program does not initialise input at all. This can lead to buffer overrun and even if that does not happen, your program is likely to have incorrect results. Thus your code invokes undefined behaviour.
Presumably you actually want to initialize input to be an empty string. Like this:
char input[1024] = "";
Once you have done this your calls to strcat will work.
If you don't want to initialise input then you can use strcpy instead of the first strcat:
char input[1024];
strcpy(input, "ussp-push 04:18:0F:B1:48:B5#9");
If ever you need to assign the empty string to input later in the code do it like this:
input[0] = '\0';
The code you suggest, char input[1024] = NULL is not valid. It does not compile.
As you're not changing it inside the function, make it a static const array
void dostuff() {
static const char input[] = "ussp-push 04:18:0F:B1:48:B5#9> dev/null&";
if (system(input) != 0)
{
printf("\nFailed command\n");
}
else
{
printf("\nSuccesss command\n");
}
}
strcat appends a string to another, and to do so it searches in the destination string the terminator char (\0). Since you didn't initialize the destination string, it may or may not find the \0 in that.
just initialize it, for example with:
input[0]='\0';
This will not compile:
char input[1024] = NULL;
$ make nullarray
cc nullarray.c -o nullarray
nullarray.c: In function ‘main’:
nullarray.c:4:2: error: invalid initializer
make: *** [nullarray] Error 1
$
When you declare char input[size], the compiler will cause the function prototype to allocate that much memory on the stack (if in a function) or in the .bss or .data section (if at top-level), and you cannot assign NULL to the array to try to stop it.
Perhaps a take step back and describe what problem you're really trying to solve? Should you be using malloc(3) to allocate memory instead?
This will not cause memory issues. Each time do() is called, an array of 1024 bytes will be allocated on the stack; that memory will be used for input throughout that call of do(). When do() returns, the 1024 bytes will be freed, and a (conceptually) new set of 1024 will be allocated on the next invocation.
In any case char input[1024] = NULL; is not valid code. If you want to initialise input, try something like char input[1024] = ""; instead.

Resources