C code about the pointer array - c

My goal is to read a file, and save each elements in this file into a new array..
rewind(fp); ii = 0; while (!feof(fp)) {
ii ++;
fscanf(fp, "%s\n", filename_i);
fp_i = fopen(filename_i, "r");
if (fp_i == NULL) {
fprintf(stderr, "can't open input file %s \n", filename_i);
exit(1);
}
filename_ii[ii] = filename_i;
printf("%s, %d\n", filename_ii[ii],ii);
fclose(fp_i);
}
printf("a %s %d\n",filename_ii[9],DataSize[2]);
printf("a %s %d\n",filename_ii[1],DataSize[2]);
In while() function, my output is each elements, but I don't know why the last two printf() returns the same results, i.e, it seems like both filename_ii[1] and filename_ii[9] point the last element in the file. Does anyone have ideas about what's wrong in my code? Thank you~

You need to use strcpy to copy a string. Change:
filename_ii[ii] = filename_i; // this just assigns a pointer -
// it doesn't actually copy a string
to:
strcpy(filename_ii[ii], filename_i); // copy the *contents* of `filename_i`
// to `filename_ii[ii]`
This assumes of course that the filename_ii array has been correctly initialised and is not just an array of dangling char * pointers (not possible to tell from the code as currently posted in the question).
Note that if filename_ii is just an array of uninitialised char * pointers then you can use strdup to handle the memory allocation and copying all in one convenient function call. In which case you would change the line above to:
filename_ii[ii] = strdup(filename_i); // allocate memory to `filename_ii[ii]` and
// copy the *contents* of `filename_i`
// to `filename_ii[ii]`

Stop using feof()/fscanf() like that, it's super-brittle and needlessly hard to get right.
Instead:
char line[1024]; /* or whatever makes you feel comfortable */
while(fgets(line, sizeof line, fp) != NULL)
{
size_t len = strlen(line);
if(len == 1) /* Ignore blank lines. */
continue;
if(line[len - 1] == '\n')
line[--len] = '\0'; /* Remove linefeed. */
if(access(line, R_OK) == 0)
strcpy(filename_ii[ii++], line);
}
This:
Uses fgets() to read in a whole line.
Uses access() to check if the file can open. Note that this kind of checking is always prone to race-conditions.
Uses strcpy() to copy the filename, assuming filename_ii[] is a properly set up array.

Related

Why does saving strings into this a *char array from STDIN only save the last line in C? [duplicate]

I'm trying to understand why an issue is happening. I have a file, from which I read several lines:
char *array_slave[128];
int i = 0;
while ((read = getline(&line, &len, fp)) != -1)
{
if (strstr(line, "X") != NULL)
{
array_slave[i] = line;
printf("%s\n",array_slave[i]);
i++;
}
}
After this cycle, I know that array_slave contains 32 lines:
size_t array_length(char *ptr[])
{
size_t i=0;
while(ptr[i]!=NULL){
//printf("%d\n", i);
//printf("%s\n",ptr[i]);
i++;
}
return i;
}
Now, I simply want to print the last 4 elements of array_slave. Anyway, I noticed that it prints always the same line:
for(int i=0; i<10;i++){
printf("%s\n", array_slave[i]);
}
I think that this happens because, in the first cycle, the i++ operation shifts the pointer, so now it is in a memory address that is not of my interest. How can I return the original position of array_slave? I want to point to array_slave[0], array_slave[1] and so on...
Because they are both pointing to same line.
array_slave[i] = line; stores the same address at the ith location for each i. The memory pointed by line is overwritten with new content each time. You should copy the contents of line to a new place and store the address of that place in the array_slave array.
Alternatively to the solution proposed by the previous answers the problem could also be solved by setting line to NULL after copying the pointer to slave_array:
char *array_slave[128];
int i = 0;
while ((read = getline(&line, &len, fp)) != -1)
{
if (strstr(line, "X") != NULL)
{
array_slave[i] = line;
line = NULL; // HERE
printf("%s\n",array_slave[i]);
i++;
}
}
That will cause getline() to malloc() a new buffer when it's called the next time.
Be aware you have to free() each array_slave[i] when you don't need it anymore.
After this cicle, I know that array_slave contains 32 lines
That is not entirely accurate, it contains 32 pointers to char that will all point to1 the address of line, which is in itself a pointer to char that will be pointing to a unique memory location, it will not change where it's pointing to along the loop, what will change is what is stored in that memory location, which is the read line in that particular iteration.
So with the above information you can see that the pointers stored in array_slave will all point to line and print whatever is in there, repeatedly in your cycle, and what line contains2 is the last line you read from the file.
What you need to do is to copy the content of line to array_slave[i] in each iteration:
//...
if (strstr(line, "X") != NULL)
{
strcpy(array_slave[i], line); //<---
printf("%s\n",array_slave[i]);
i++;
}
//...
1 - Contain the address of
2 - Points to
Adding (lately, for late readers) yet another approach: Just skip this intermediate variable line entirely and read into the array directly; for this purpose, it must be initialised appropriately, though:
char *array_slave[128] = { NULL };
which then allows reading the lines as
getline(array_slave + i, &len, fp)

Cannot get Call to Function Working

I found this piece of code at Reading a file character by character in C and it compiles and is what I wish to use. My problem that I cannot get the call to it working properly. The code is as follows:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL)
return NULL; //could not open file
code = malloc(1500);
while ((c = fgetc(file)) != EOF)
{
code[n++] = (char) c;
}
code[n] = '\0';
return code;
}
I am not sure of how to call it. Currently I am using the following code to call it:
.....
char * rly1f[1500];
char * RLY1F; // This is the Input File Name
rly1f[0] = readFile(RLY1F);
if (rly1f[0] == NULL) {
printf ("NULL array); exit;
}
int n = 0;
while (n++ < 1000) {
printf ("%c", rly1f[n]);
}
.....
How do I call the readFile function such that I have an array (rly1f) which is not NULL? The file RLY1F exists and has data in it. I have successfully opened it previously using 'in line code' not a function.
Thanks
The error you're experiencing is that you forgot to pass a valid filename. So either the program crashes, or fopen tries to open a trashed name and returns NULL
char * RLY1F; // This is not initialized!
RLY1F = "my_file.txt"; // initialize it!
The next problem you'll have will be in your loop to print the characters.
You have defined an array of pointers char * rly1f[1500];
You read 1 file and store it in the first pointer of the array rly1f[0]
But when you display it you display the pointer values as characters which is not what you want. You should just do:
while (n < 1000) {
printf ("%c", rly1f[0][n]);
n++;
}
note: that would not crash but would print trash if the file read is shorter than 1000.
(BLUEPIXY suggested the post-incrementation fix for n BTW or first character is skipped)
So do it more simply since your string is nul-terminated, pass the array to puts:
puts(rly1f[0]);
EDIT: you have a problem when reading your file too. You malloc 1500 bytes, but you read the file fully. If the file is bigger than 1500 bytes, you get buffer overflow.
You have to compute the length of the file before allocating the memory. For instance like this (using stat would be a better alternative maybe):
char *readFile(char *fileName, unsigned int *size) {
...
fseek(file,0,SEEK_END); // set pos to end of file
*size = ftell(file); // get pos, i.e. size
rewind(file); // set pos to 0
code = malloc(*size+1); // allocate the proper size plus one
notice the extra parameter which allows you to return the size as well as the file data.
Note: on windows systems, text files use \r\n (CRLF) to delimit lines, so the allocated size will be higher than the number of characters read if you use text mode (\r\n are converted to \n so there are less chars in your buffer: you could consider a realloc once you know the exact size to shave off the unused allocated space).

read file line by line including multiple newline characters

I am trying to read a file of unknown size line by line including single or multiple newline characters.
for example if my sample.txt file looks like this
abc cd er dj
text
more text
zxc cnvx
I want my strings to look something like this
string1 = "abc cd er dj\n";
string2 = "text\n\n";
string3 = "more text\n\n\n";
string4 = "zxc convex";
I can't seem to come up with solution that works properly. I have tried following code to get the length of each line including newline characters but it gives me incorrect length
while((temp = fgetc(input)) != EOF) {
if (temp != '\n') {
length++;
}
else {
if (temp == '\n') {
while ((temp = fgetc(input)) == '\n') {
length++;
}
}
length = 0;
}
}
I was thinking, if I can get length of each line including newline character(s) and then I can malloc string of that length and then read that size of string using fread but I am not sure if that would work because I will have to move the file pointer to get the next string.
I also don't want to use buffer because I don't know the length of each line. Any sort of help will be appreciated.
If the lines are just short and there aren't many of them, you could use realloc to reallocate memory as needed. Or you can use smaller (or larger) chunks and reallocate. It's a little more wasteful but hopefully it should average out in the end.
If you want to use just one allocation, then find the start of the next non-empty line and save the file position (use ftell). Then get the difference between the current position and the previous start position and you know how much memory to allocate. For the reading yes you have to seek back and forth but if it's not to big all data will be in the buffer to it's just modifying some pointers. After reading then seek to the saved position and make it the next start position.
Then you could of course the possibility to memory-map the file. This will put the file contents into your memory map like it was all allocated. For a 64-bit system the address space is big enough so you should be able to map multi-gigabyte files. Then you don't need to seek or allocate memory, all you do is manipulate pointers instead of seeking. Reading is just a simply memory copying (but then since the file is "in" memory already you don't really need it, just save the pointers instead).
For a very simple example on fseek and ftell, that is somewhat related to your problem, I put together this little program for you. It doesn't really do anything special but it shows how to use the functions in a way that could be used for a prototype of the second method I discussed above.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *file = fopen("some_text_file.txt", "r");
// The position after a successful open call is always zero
long start_of_line = 0;
int ch;
// Read characters until we reach the end of the file or there is an error
while ((ch = fgetc(file)) != EOF)
{
// Hit the *first* newline (which differs from your problem)
if (ch == '\n')
{
// Found the first newline, get the current position
// Note that the current position is the position *after* the newly read newline
long current_position = ftell(file);
// Allocate enough memory for the whole line, including newline
size_t bytes_in_line = current_position - start_of_line;
char *current_line = malloc(bytes_in_line + 1); // +1 for the string terminator
// Now seek back to the start of the line
fseek(file, start_of_line, SEEK_SET); // SEEK_SET means the offset is from the beginning of the file
// And read the line into the buffer we just allocated
fread(current_line, 1, bytes_in_line, file);
// Terminate the string
current_line[bytes_in_line] = '\0';
// At this point, if everything went well, the file position is
// back at current_position, because the fread call advanced the position
// This position is the start of the next line, so we use it
start_of_line = current_position;
// Then do something with the line...
printf("Read a line: %s", current_line);
// Finally free the memory we allocated
free(current_line);
}
// Continue loop reading character, to read the next line
}
// Did we hit end of the file, or an error?
if (feof(file))
{
// End of the file it is
// Now here's the tricky bit. Because files doesn't have to terminated
// with a newline, at this point we could actually have some data we
// haven't read. That means we have to do the whole thing above with
// the allocation, seeking and reading *again*
// This is a good reason to extract that code into its own function so
// you don't have to repeat it
// I will not repeat the code my self. Creating a function containing it
// and calling it is left as an exercise
}
fclose(file);
return 0;
}
Please note that for brevity's sake the program doesn't contain any error handling. It should also be noted that I haven't actually tried the program, not even tried to compile it. It's all written ad hoc for this answer.
Unless you are trying to write your own implementation, you can use the standard POSIX getline() function:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("/etc/motd", "r");
if (fp == NULL)
exit(1);
while ((read = getline(&line, &len, fp)) != -1) {
printf("Retrieved line of length %zu :\n", read);
printf("%s", line);
}
if (ferror(fp)) {
/* handle error */
}
free(line);
fclose(fp);
return 0;
}
You get the wrong length. The reason is that before you enter the loop:
while ((temp = fgetc(input)) == '\n')
you forgot to increment length as it has just read a \n character. So those lines must become:
else {
length++; // add the \n just read
if (temp == '\n') { // this is a redundant check
while ((temp = fgetc(input)) == '\n') {
length++;
}
ungetc(temp, input);
}
EDIT
After having read the first non \n, you now have read the first character of the next line, so you must unget it:
ungetc(temp, input);

Duplicate string detection in C

I'm trying to create a function that removes duplicate strings from a stream in C. The strings are already sorted so the only necessary step is to check the string that just appeared to make sure the current string is not a duplicate. However, my attempted implementation is not giving me the correct output.In fact, I get no output at all. The strings are separated by newline characters. Can anyone tell me what I'm missing here?
void dupEliminate(int file, char string[100])
{
FILE *stream;
stream = fdopen(file, "r");
char* savedString;
char* prevString;
while(!feof(stream)){
(fgets(savedString, 100, stream));
if(strcmp(savedString,prevString) != 0 ){
strcat(string, savedString);
strcpy(prevString,savedString);
}
char* prevString;
prevString is uninitialized in this function , and yet you compare it here -
if(strcmp(savedString,prevString) != 0 )
Also , before taking input in savedString using fgets , you need to allocate memory to it using malloc or calloc ,as it is an unintialized pointer.
What it will compare to ? Initialize you prevString and then compare it .
Note- Just a suggestion instead of using while(!feof(stream)) , use fgets to control loop -
while(fgets(savedString, 100, stream)!=NULL){
...
}
You need to give savedString and prevString some memory - malloc or make them char[101]
You will also need to initialise prevString
prevString needs to be updated with the last line unconditionally, not in the if block
I don't like the names either :)
It is unclear what you ultimately want to do with the non-duplicate strings given the code you have posted. At present, even if you collect only non-duplicate strings, you are currently overwriting the value in string without doing any thing with it.
If you are reading from a continual stream, then you either need to just print (or save or copy) the non-duplicate string somewhere to make use of it, or you need to buffer the non-duplicate strings somewhere and make use of them when your buffer is full.
Since you say you are not getting the correct output, but have no output functions listed, it is somewhat of a guess what you are trying to do. If you simply want to output the non-duplicate strings, then you can simply print them (note using fgets you will have a newline at the end of each string read). An example would be:
#define MAXC 100
...
char string[MAXC] = {0};
...
void dupEliminate (int file, char *string)
{
FILE *stream;
stream = fdopen (file, "r");
if (!stream) {
fprintf (stderr, "dupEliminate() error: stream open failed.\n");
return;
}
char pre[MAXC] = {0}; /* previous string */
size_t word = 0;
while (fgets (string, MAXC, stream)) {
if (word == 1) {
if (strcmp (pre, string) != 0) {
printf ("%s", pre);
printf ("%s", string);
}
else
printf ("%s", string);
}
if (word > 1 && strcmp (pre, string) != 0)
printf ("%s", string);
strncpy (pre, string, MAXC);
word++;
}
fclose (stream);
}
If your intent was to buffer all non-duplicate strings and make them available in main(), then there are several things you need to do. Let me know if you have any further questions and I'm happy to work with your further once I have more details about your intent.

Gibberish after file contents

I'm working on the following function to pull scripts into my C application:
char *anne_script_load(char fileName[]){
char path[6400] = "data/scripts/";
strncat(path, fileName, 6000);
strncat(path, ".lua", 5);
struct stat fileInfo;
char *contents = NULL;
FILE * pFile;
stat(path, &fileInfo);
contents = (char*)malloc(fileInfo.st_size);
pFile = fopen (path,"r");
fread (contents,1,fileInfo.st_size,pFile);
fclose(pFile);
printf("Script path: %s\n", path);
printf("Script loaded: %s\n", contents);
return contents;
}
At runtime, the second printf generates the following output:
test script - if you see this load is working :) helicopters����
The garbled text looks different on my console, but I'm not sure it matters: my theory is that the file stream doesn't end in a null byte (it's not stored on disk as a c string, after all - so I terminated it myself as follows:
contents[fileInfo.st_size] = 0;
This appears to work, but I'm concerned about the robustness of this solution. Is there a better, generally accepted way of doing this?
You need to add +1 to your malloc for the termination:
if(stat(path, &fileInfo) != 0) {
perror("stat");
...
}
contents = (char*)malloc(fileInfo.st_size + 1); // don't forget this
pFile = fopen (path,"r");
if(pFile == 0) {
perror("fopen");
...
}
int n = fread (contents,1,fileInfo.st_size,pFile);
if(n != fileInfo.st_size) {
perror("read");
...
}
contents[n] = 0; // terminate after what you read. Not what you think you read.
** Check the return values from fopen, stat, and read.**
There are number of things you could probably improve here... but to answer your question, fread() is reading bytes into an array that you didn't initialize. In C, you can't expect a \0 character after the last byte you read via fread -- you've got to put it there first, either with a memset() or calloc().
Also, if the file content is treated as a text string, be sure to allocate one additional byte over the size to hold that terminating \0 character!
You must nul-terminated the string otherwise printf() will not know for sure when to stop reading from the pointer you just passed. The same applies to other functions like strcmp(), you could have two strings with the same content while having strcmp() yelling non-zero because of a non nul-terminated string.
So contents[fileInfo.st_size] = 0; is just fine.
You could read a little bit more about nul-terminated strings on Wikipedia

Resources