Getting words from text file to an array - c

My text file is formated like that:
my.txt
Red
Green
Blue
Yellow
I'm tring to get words like that:
typedef char * string;
main(){
int i;
string array[4];
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++)
fscanf(data, "%s", &array[i]);
fclose(my);
}
When I try to print the array there is an error. What's wrong with my code and how can I fix it?

You'll need to allocate memory for your null-terminated strings.
At the moment you are only allocating memory for 4 char *, but these pointers are uninitialized and therefor will result in UB (undefined behavior) when you try to write data to the memory pointed to by them.
Working example snippet
The use of "%127s" in the below snippet is to prevent us writing outside the bounds of the allocated memory. With the format-string in question we will at the most read/write 127 bytes + the null-terminator.
Please remember that further error checks should be implemented if this is to be used in "real life".
Check to see that file_handle is indeed valid after attempt to open the file
Check to see that malloc did indeed allocate requested memory
Check to see that fscanf read the desired input
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int
main (int argc, char *argv[])
{
int i;
char * lines[4];
FILE *file_handle = fopen ("my.txt", "r");
for (i =0; i < 4; ++i) {
lines[i] = malloc (128); /* allocate a memory slot of 128 chars */
fscanf (file_handle, "%127s", lines[i]);
}
for (i =0; i < 4; ++i)
printf ("%d: %s\n", i, lines[i]);
for (i =0; i < 4; ++i)
free (lines[i]); /* remember to deallocated the memory allocated */
return 0;
}
output
0: Red
1: Green
2: Blue
3: Yellow

You try to read some data, but you don't have anywhere to put it. All you have is 4 pointers, pointing to god knows where and you are trying to write into it!
There are many ways to do this:
You know the bound to the size of the data:
#include <stdio.h>
#define MAX_CHARS 20
typedef char string[MAX_CHARS+1]; // leave one space for '\0'
main(){
int i;
string array[4];
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++)
fscanf(data, "%s", array[i]); // no need for & with %s
fclose(my);
}
Assume a bound to the size of the data, and ignore the rest of the strings (if it was too big):
#include <stdio.h>
#define MAX_CHARS 20
#define MAX_CHARS_STR "20" // there are better ways to get this
typedef char string[MAX_CHARS+1];
main(){
int i;
string array[4];
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++){
fscanf(data, "%"MAX_CHARS_STR"s", &array[i]); // read at most 20 chars for the string
ungetc('x', data); // append one character to make sure we don't hit space
fscanf(data, "%*s"); // ignore whatever is left of string
}
fclose(my);
}
Read the file twice, first time find out the sizes of each string (or the maximum size, for simplicity), then allocate memory for the strings (using malloc). Then read the file again and this time actually store the strings:
#include <stdio.h>
typedef char *string;
main(){
int i;
string array[4];
int cur_size = 0;
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++){
fscanf(data, "%*s%n", &cur_size);
array[i] = malloc((cur_size+1)*sizeof(*array[i]));
}
fclose(my);
my = fopen("my.txt","r");
for(i = 0; i < 4; i++){
fscanf(data, "%s", array[i]);
}
fclose(my);
// and when done:
for(i = 0; i < 4; i++){
free(array[i]);
}
}
Read from the input chunk by chunk. For each string, if input string was not finished yet resize the memory allocated for the string (increase its size), read another chunk and check again. Method 3 is faster though and I recommend it, but just so you know, this is basically what happens in C++'s string.

Since all the other answers told you what you did wrong but not how to fix it. Here
typedef char * string;
#define LEN 100 //long enough for your line
main(){
int i;
string array[4];
for(i = 0; i < 4; i++) {
if((array[i] = (char *)(malloc(sizeof(char) * LEN))) == NULL) {
printf("malloc failed");
return 1;
}
}
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++)
fscanf(data, "%s", &array[i]);
fclose(my);
}
And like they said you made space for the pointers but not for what the pointers point to.

Related

Cant get first character in array of strings

I want to get first char character of each string. Here a example:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
int size = 2;
char** text = (char**) malloc(sizeof(char*) * size);
for(int i = 0; i < size; ++i) {
char buf[80];
fgets(buf, 80, stdin);
text[i] = (char*)malloc(strlen(buf));
strcpy(text[i], buf);
}
for(int i = 0; i < strlen(text[i]); ++i) {
printf("%c ", text[i][0]);
}
}
In last for loop, program falls in Segmentation fault. I dont know why.
The strlen function returns the number of characters in the given string not including the terminal nul character; however, the strcpy function copies all characters including that terminating nul!
So, your allocation for text[i] is not quite big enough and, by writing beyond the buffer's bounds, you are getting undefined behaviour.
Add an extra character to the malloc call:
for(int i = 0; i < size; ++i) {
char buf[80];
fgets(buf, 80, stdin);
text[i] = malloc(strlen(buf) + 1); // Need space for the terminal nul!
strcpy(text[i], buf);
}
Or, more simply, use the strdup function, which achieves the same result as your malloc and strcpy in one fell swoop:
for(int i = 0; i < size; ++i) {
char buf[80];
fgets(buf, 80, stdin);
text[i] = strdup(buf);
}
Either way, don't forget to call free on all the buffers you allocate.
EDIT: You are also using the wrong 'limit' in your final output loop; this:
for(int i = 0; i < strlen(text[i]); ++i) { // strlen() is not the # strings
printf("%c ", text[i][0]);
}
Should be:
for(int i = 0; i < size; ++i) { // "size" is your number of strings!
printf("%c ", text[i][0]);
}

Unable to print string from 2D array in struct?

I'm from the Python/JS world, so I'm having issues wrapping my head around strings and arrays in C.
I'm attempting to create a struct -- FileInfo -- which contains a 2D array so that I can store a number of email addresses. I have a function -- createFileInfo() -- which creates the struct, manages memory for the struct and array indexes, assigns an email address to the array, and returns a pointer to the struct:
typedef struct FileInfo {
char **emailArr;
} FileInfo;
FileInfo *createFileInfo(int count) {
FileInfo *fi = malloc(sizeof(FileInfo));
char *buffer;
char *emailPrefix = "test";
char *emailDomain = "#test.com";
for (int i=0; i<count; ++i) {
fi->emailArr[i] = malloc(count * sizeof(char));
}
snprintf(buffer, sizeof(char), "%s%s", emailPrefix, emailDomain);
for (int i=0; i<count; ++i) {
for(int j=0; j<count; ++j) {
fi->emailArr[i][j] = *buffer;
}
}
return fi;
}
Then, in my main() function, I'm trying to create a pointer to the struct and print the index that contains the email address. However, I cannot get the string to print.
int main() {
int count = 1;
FileInfo *fi = createFileInfo(count);
for (int i=0; i<count; ++i) {
for(int j=0; j<count; ++j) {
fprintf(stdout, "test %s\n", fi->emailArr[i][j]);
}
}
for (int i=0; i<count; ++i) {
free(fi->emailArr[i]);
}
free(fi);
return 0;
}
What am I doing incorrectly? I'm also having quite the time with memory management; is memory allocation the issue?
In order to allocate a 2D dynamic array in C, you would need to do this instead, assuming that your 2D array is of dimensions N x M, where N is the number of emails, and M a number large enough to be able to store the email (that is something bigger than the length of the longest email to be stored, +1 for the C-string Null Terminator):
fi->emailArr = malloc( N*sizeof(char *) );
for(int i = 0 ; i < N ; i++)
fi->emailArr[i] = malloc( M*sizeof(char) );
In your attempt you missed the dynamic allocation which is just before the for loop.
You should also allocate memory for your buffer pointer. Now you do:
char *buffer;
snprintf(buffer, sizeof(char), "%s%s", emailPrefix, emailDomain);
but buffer has no memory allocated for it, so no place to store the string. You could do this instead:
size_t email_len = strlen(emailPrefix) + strlen(emailDomain);
char *buffer = malloc(email_len + 1); // +1 for the Null Terminator
snprintf(buffer, email_len, "%s%s", emailPrefix, emailDomain);
I also changed the call to populate the buffer, since the 2nd parameter of snprintf(), n, is the "Maximum number of bytes to be used in the buffer".
Putting everything together, a complete, minimal and basic example would look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct FileInfo {
char **emailArr;
} FileInfo;
FileInfo *createFileInfo(int count) {
FileInfo *fi = malloc(sizeof(FileInfo));
char *emailPrefix = "test";
char *emailDomain = "#test.com";
size_t email_len = strlen(emailPrefix) + strlen(emailDomain);
char *buffer = malloc(email_len + 1); // +1 for the Null Terminator
snprintf(buffer, email_len + 1, "%s%s", emailPrefix, emailDomain);
fi->emailArr = malloc( count * sizeof(char *) );
for(int i = 0 ; i < count; i++)
fi->emailArr[i] = malloc( (email_len + 1) * sizeof(char) );
for(int i = 0 ; i < count; i++)
strcpy(fi->emailArr[i], buffer);
return fi;
}
int main(void) {
int count = 1;
FileInfo *fi = createFileInfo(count);
for (int i = 0; i < count; ++i) {
printf("%s\n", fi->emailArr[i]);
}
for (int i = 0; i < count; ++i) {
free(fi->emailArr[i]);
}
free(fi);
return 0;
}
Output:
test#test.com
Now of course, once you understand this piece of code, you could, if you like, think of how you would generalize this code to handle more emails than one.
emailArr is not initialized. You should initialize it first:
f->emailArr = malloc(number_of_char_arrays * sizeof(char*))
buffer is not initialized, too:
buffer = malloc(char_array_length * sizeof(char))
About snprintf's 2nd argument:
Maximum number of bytes to be used in the buffer.
The generated string has a length of at most n-1, leaving space for the additional >terminating null character.
size_t is an unsigned integral type.
It should be at least 14 if you want it to copy emailPrefix and emailDomain to buffer.
fprintf(stdout, "test %s\n", fi->emailArr[i][j]);
fi->emailArr[i][j] is a single character. You should only use the first index if you want to print it by %s.
In the createFileInfo function you're effectively creating a count*count char array, which I doubt is what you want and with count = 1 it can't contain the string "test#test". You should be allocating the size of the string you want to store, not "count" chars.
int size = 9; //this should probably be a parameter of the function
for (int i=0; i<count; ++i) {
fi->emailArr[i] = malloc(size * sizeof(char));
}
And you should allocate emailArr itself first (more on this here)
fi->emailArr = malloc(count * sizeof(char *));
You're also trying to copy the string in the wrong way; right now you're copying the pointer to buffer in each character of the string, which means each "string" is actually an array of pointers and cannot be printed as a string (I'm surprised it even compiles). It also means that if the content buffer points to changes, every element in your array will change. You need to use strncpy to do what you want.
for (int i=0; i<count; ++i) {
strncpy(fi->emailArr[i], buffer, size);
}
You also forgot to allocate memory for buffer.
I suggest you try some simpler programs involving strings to familiarize yourself with pointer logic.

How to convert array contents into a single string C

So the following is a sandbox program. The issue I'm having is combining an array into a single string. I would like to do something similar to the code below:
for (i = 0; i < size_of_array; i++)
{
string += A[i]; // print array
}
The goal is to run a command using popen() and capture the output into a single string. the reason for this is so that I can return the output to a separate function for example:
run_command()
{
return output;
}
main()
{
run_command()
}
Now the exact code that the "sandbox" program is using is down below:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *in;
extern FILE *popen();
char buff[512];
int i, size_of_array;
char A[512][512];
in = popen("ls -lt", "r"); // run command
i = 0;
while(fgets(buff, sizeof(buff), in)!=NULL) // get output into buff
{
strcpy(A[i], buff); // copy buff into array
i ++;
}
pclose(in);
size_of_array = i; // get length or size of array
for (i = 0; i < size_of_array; i++)
{
printf("A[%d]= %s", i, A[i]); // print array
}
return 0;
}
I apologize if this is a noob question, I appreciate the help, thank you!
strcat, strcat_s, or strncat append a string at the end of a destination string (cf. cppreference for strcat). The only thing is to make sure that the destination buffer is large enough. strcat_s can be used to avoid buffer overflows, but is not available on all systems. strncat can be used to avoid buffer overflows, too, yet one needs to track the length of the string within the buffer:
#define maxSize 512*512
char result[maxSize] = { 0x0 };
for (int i = 0; i < size_of_array; i++) {
strcat(result, A[i]);
}
or:
char result[maxSize] = { 0x0 };
for (int i = 0; i < size_of_array; i++) {
strcat_s(result, maxSize, A[i]);
}
or:
char result[maxSize] = { 0x0 };
for (int i = 0; i < size_of_array; i++) {
strncat(result, A[i], maxSize-strlen(result)-1);
}

Assigning char array of pointers with scanf

I'm trying to use scanf to fill an array of char pointers to store the input as a string. The variable T is used to build an array of size T dynamically. Then T amount of strings are entered and displayed however when I fill in the array for example if T = 2 the first line could dog and the second line cat, it prints out "cdog" and "cat". So the first letter of the first string then the all of the 2nd string. I'm not sure where my mistake is in using char*. Any help would be appreciated.
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main()
{
int T;
int i;
scanf("%d",&T);
char *new_array = (char*)malloc(T * sizeof(char));
for (i = 0; i < T; ++i)
{
scanf("%s", new_array+i);
}
for (i = 0; i < T; i++)
{
printf("%s\n", new_array+i);
}
}
Always check the return value of scanf().
You are not allocating space for pointers, but for bytes, which is the main problem, you need
char **new_array = malloc(T * sizeof(char *));
/* ^ ^ */
/* allocate pointer to poitners sizeof(pointer) */
if (new_array == NULL)
handleThisErrorAndDoNotTryToWriteTo_new_array();
you will also need space for each string so
new_array[i] = malloc(1 + lengthOfTheString);
if (new_array[i] == NULL)
handleThisErrorAndDoNotTryToWriteTo_new_array_i();
right before scanf(), and instead of scanf("%s", new_array + i) do this
scanf("%s", new_array[i]);
If you enable compiler warnings, the compiler should warn you that you are passing incompatible types to printf().
It would also be good, to use a length modifier for scanf() to prevent buffer overflow, and don't forget to call free() when you no longer need the pointers.
In your code, new_array is of type char *, which is not what you want. You have to change your definition to
char *new_array[T] = malloc(T * sizeof(char*));
Then, you can use the scanf() as per your previous code.
Do this instead, together with the rest of the body:
int string_size;
//this begins just after reading T
scanf("%d", &string_size);
char **new_arrays = malloc(T * sizeof(char*));
for(i = 0; i < T; i++)
{
new_arrays[i] = malloc(string_size * sizeof(char));
}
The first malloc is to specify how many strings you want, and the second malloc is to specify how big a string can be.
Further tips:
When you're writing in C, do not cast void* produced by malloc and realloc.
You should de-allocate the memory used in the reverse way:
for (i = 0; i < T; ++i)
{
free(new_array[i]);
}
free(new_array);
Always check if the memory allocation process is (un)succesful:
char **new_arrays = malloc(T * sizeof(char*));
if(new_arrays == NULL)
exit(0) //e.g.
for(i = 0; i < T; i++)
{
new_arrays[i] = malloc(string_size * sizeof(char));
if(new_arrays[i] == NULL)
exit(0) //e.g.
}
Check if the user provides valid values through scanf.
Thanks everyone. The length of the strings in the char* array can not be greater than 1000 chars so thats hard coded. Here is the final working code...
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main()
{
int T;
int i;
scanf("%d",&T);
char **new_array = malloc(T * sizeof(char*));
for (i = 0; i < T ; i++)
{
new_array[i] = malloc(1000 * sizeof(char));
scanf("%s", new_array[i]);
}
for (i = 0; i < T; i++)
printf("%s\n", new_array[i]);
}

fgets "Access violation writing location 0xCCCCCCCC." error

Error: Unhandled exception at 0x60092A8D (msvcr110d.dll) in C_Son60.exe: 0xC0000005: Access violation writing location 0xCCCCCCCC.
When below code executed this error code is given.(Compiles successfully)
Where is my mistake?
#include <stdio.h>
int i;
int main(void){
char *names[3];
//get the names of the cities
puts("Enter names of cities");
for (i = 0; i < 3; i++)
{
fgets( names[i], 99, stdin);
}
//print entered names
for (i = 0; i < 3; i++)
{
printf("%s", *names[i]);
}
getch();
}
You need to allocate memory to which the char pointers point to before you read them in
for instance:
for (i = 0; i < 3; i++)
{
names[i] = malloc(200);
fgets( names[i], 99, stdin);
}
2 things:
you need to allocate the strings you created- you can do it with malloc(100 * sizeof(char))
when printing you do *names[i] which means - **(names + i).
all you need is names[i]
use the code:
#include <stdio.h>
int i;
int main(void){
char *names[3];
//get the names of the cities
puts("Enter names of cities");
for (i = 0; i < 3; i++)
{
names[i] = (char *)malloc(100 * sizeof(char));
fgets( names[i], 99, stdin);
}
//print entered names
for (i = 0; i < 3; i++)
{
printf("%s", names[i]);
}
getch();
}
You must allocate your memory before storing anything into it. When you need to allocate an array of elements, and you do not know the number of elements at compile time, you must use malloc() to allocate them.
Don't forget to free your dynamically allocated memory later in order to avoid memory leaks!

Resources