Getting inputs from txt file and add in an array - c

I am newer in C language. I want to create an array for my code to make some operation. As I said above, I am trying to learn how to use C language efficiently. My problem is this: I have a input file, let's say input.txt. I know that every line have 4 different things, 2 of them are string and 2 of them number. Also, I want to create a 2D array. But I do not know how many lines will be in input file. It depends on the user. So, I have to use malloc to make my array dynamically. So, can you help me about this problem? Maybe this is so easy, but I think reading file and create some array in C more difficult than other languages. It was so easy in Python :( I am leaving my code below. If you tell me my mistakes, I will be happy :)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[]) {
char *arrChar;
int i;
char *str;
char *token;
arrChar = (char *) malloc( sizeof( char ) );
str = (char *) malloc( sizeof( char ) );
FILE *FileChars;
FileChars = fopen( argv[1], "r");
i = 0;
while ( fgets(str, sizeof str, FileChars) != NULL) {
int j;
for ( j = 0; j < 4; j++ ) {
token = strtok(str, ",");
arrChar[i][j] = token;
}
i++;
}
}

You need to understand precisely what the sizeof operator does, it doesn't return the size of a dynamically allocated memory block, it returns the size of a type, in case of arrays — roughly speaking — the size is part of the type specification and so it returns the number of bytes the array occupies.
In your case sizeof(char) is the size of the type char which is required to be exactl 1 by the (c-standard C Standard).
And sizeof(str) is the size of the type of str which is char *, that is, the size of a pointer. It's probably 4 or 8 depending on your current platform.
To solve this, you have to define a length to be used throughout your program as the length of the allocated chunk of memory, that after you make sure that the allocation was successful (see below).
A pointer to char can point to a sequence of elements that can be interpreted as a string if it is the correct sequence. A sequence of "printable" characters followed by a '\0' or null character is considered a string.
You have to pass NULL to strtok() after the first time, if you are going to be processing the same string.
You should CHECK that fopen() did return a valid stream, by comparing the return value to NULL.
The same as (5), for malloc() when allocation is not possible NULL is returned and using it as a valid pointer is undefined behavior.
All that said, here is what you probably wanted to write
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_WORDS 100
int main(int argc, char const *argv[])
{
char *token;
char **words;
char line[100];
char *ptr;
size_t count;
FILE *file;
file = fopen( argv[1], "r");
// Check that we DID open the file
if (file == NULL) {
perror(argv[1]);
return EXIT_FAILURE;
}
// Allocate space for `NUM_WORDS' pointers
words = malloc(NUM_WORDS * sizeof(*words));
// Check that we did allocate enough memory
if (words == NULL) {
fclose(file);
return EXIT_FAILURE;
}
// We use `sizeof' here because `line' is an array
count = 0;
while ((count < NUM_WORDS) && (fgets(line, sizeof(line), file) != NULL)) {
ptr = line;
do {
token = strtok(ptr, ",");
// We need to copy the `token' because
// it lies within `line' and the '\0' will
// be replaced by the original character
// in subsequent callse to `strtok()'
//
// The `strdup()' will allocate enough space with
// `malloc()' then copy the contents of `token' to the
// allocated buffer, and return it, so we will
// have to call `free()' on every `words' element.
words[count++] = strdup(token);
// Subsequent calls to `strtok()' with the same
// string require that the first parameter is
// NULL
ptr = NULL;
} while ((count < NUM_WORDS) && (token != NULL));
}
// Now we may print the words and free the memory
for (size_t index = 0; index < count; ++index) {
puts(words[index]);
free(words[index]);
}
free(words);
return 0;
}
Note that the code above, makes sure that we don't exceed the capacity of the array of pointers words1. If you need to resize it, you will need to learn how to use realloc() and do it in a specialized routine so that your code doesn't become too complex.
1Note that the allocated space has no predefined interpretation, we do interpret it as an array but it's not an array in the c sense of an array definition, which line IS, having elements of type char, line can also be interpreted as a string given it has contents compatible with the defintion given in the (2) second point above.

Related

C remove special characters from string

I am very new to C, and I have created a function that removes special characters from a string and returns a new string (without the special characters).
At first glance, this seemed to be working well, I now need to run this function on the lines of a (huge) text file (1 Million sentences). After a few thousand lines/sentences (About 4,000) I get a seg fault.
I don't have much experience with memory allocation and strings in C, I have tried to figure out what the problem with my code is, unfortunately without any luck.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char *preproccessString(char *str) {
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = malloc(sizeof(str));
// Array of allowed chars with a 0 on the end to know when the end of the array is reached, I don't know if there is a more elegant way to do this
// Changed from array to string for sake of simplicity
char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Initalize two integers
// i will be increased for every char in the string
int i = 0;
// j will be increased every time a new char is added to the result
int j = 0;
// Loop over the input string
while (str[i] != '\0') {
// l will be increased for every char in the allowed chars array
int l = 0;
// Loop over the chars in the allowed chars array
while (allowedCharsArray[l] != '\0') {
// If the char (From the input string) currently under consideration (index i) is present in the allowed chars array
if (allowedCharsArray[l] == toupper(str[i])) {
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = toupper(str[i]);
j++;
}
l++;
}
i++;
}
return result;
}
Here is the rest of the program, I think the problem is probably here.
int main(int argc, char *argv[]) {
char const * const fileName = argv[1];
FILE *file = fopen(fileName, "r");
char line[256];
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preproccessString(line));
}
fclose(file);
return 0;
}
You have several problems.
You're not allocating enough space. sizeof(str) is the size of a pointer, not the length of the string. You need to use
char *result = malloc(strlen(str) + 1);
+ 1 is for the terminating null byte.
You didn't add a terminating null byte to the result string. Add
result[j] = '\0';
before return result;
Once you find that the character matches an allowed character, there's no need to keep looping through the rest of the allowed characters. Add break after j++.
Your main() function is never freeing the results of preprocessString(), so you might be running out of memory.
while (fgets(line, sizeof(line), file)) {
char *processed = preproccessString(line);
printf("%s\n", processed);
free(processed);
}
You could address most of these problems if you have the caller pass in the result string, instead of allocating it in the function. Just use two char[256] arrays in the main() function.
int main(int argc, char *argv[])
{
char const* const fileName = argv[1];
FILE* file = fopen(fileName, "r");
char line[256], processed[256];
while (fgets(line, sizeof(line), file)) {
processString(line, processed);
printf("%s\n", processed);
}
fclose(file);
return 0;
}
Then just change the function so that the parameters are:
void preprocessString(const char *str, char *result)
A good rule of thumb is to make sure there is one free for every malloc/calloc call.
Also, a good tool to keep note of for the future is Valgrind. It's very good at catching these kinds of errors.
There are some major issues in your code:
the amount of memory allocated is incorrect, sizeof(str) is the number of bytes in a pointer, not the length of the string it points to, which would also be incorrect. You should write char *result = malloc(strlen(str) + 1);
the memory allocated in preproccessString is never freed, causing memory leaks and potentially for the program to run out of memory on very large files.
you do not set a null terminator at the end of the result string
Lesser issues:
you do not check if filename was passed nor if fopen() succeeded.
there is a typo in preproccessString, it should be preprocessString
you could avoid memory allocation by passing a properly sized destination array.
you could use isalpha instead of testing every letter
you should cast the char values as unsigned char when passing them to toupper because char may be a signed type and toupper is undefined for negative values except EOF.
there are too many comments in your source file, most of which are obvious but make the code less readable.
Here is a modified version:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
// transform the string in `str` into buffer dest, keeping only letters and uppercasing them.
char *preprocessString(char *dest, const char *str) {
int i, j;
for (i = j = 0; str[i] != '\0'; i++) {
if (isalpha((unsigned char)str[i])
dest[j++] = toupper((unsigned char)str[i]);
}
dest[j] = '\0';
return dest;
}
int main(int argc, char *argv[]) {
char line[256];
char dest[256];
char *filename;
FILE *file;
if (argc < 2) {
fprintf(stderr, "missing filename argument\n");
return 1;
}
filename = argv[1];
if ((file = fopen(filename, "r")) == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preprocessString(dest, line));
}
fclose(file);
return 0;
}
The following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
properly checks for length of input string parameter
makes use of characteristic of strchr() also checking the terminating NUL byte
limits scope of visibility of local variables
the calling function is expected to properly cleaning up by passing the returned value to free()
the calling function is expected to check the returned value for NULL
informs compiler the user knows and accepts when an implicit conversion is made.
moves allowedCharsArray to 'file static scope' so does not have to be re-initialized on each pass through the loop and marks as 'const' to help the compiler catch errors
and now the proposed code: (note: edited per comments)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
char *preproccessString(char *str)
{
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = calloc( sizeof( char ), strlen(str)+1);
if( !result )
{
perror( "calloc failed" );
return NULL;
}
// Array of allowed chars
static const char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Loop over the input string
for( int j=0, i=0; str[i]; i++)
{
if( strchr( allowedCharsArray, (char)toupper( str[i] ) ) )
{
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = (char)toupper(str[i]);
j++;
}
}
return result;
}
I think the problem is you are using malloc which allocates memory from the heap and since you are calling this function again and again you are running out of memory.
To solve this issue you have to call the free() function on the pointer returned by your preprocessString function
In your main block
char *result=preprocessString(inputstring);
//Do whatever you want to do with this result
free(result);

How should I fix this interesting getdelim / getline (dynamic memory allocation) bug?

I have this C assignment I am a bit struggling at this specific point. I have some background in C, but pointers and dynamic memory management still elude me very much.
The assignment asks us to write a program which would simulate the behaviour of the "uniq" command / filter in UNIX.
But the problem I am having is with the C library functions getline or getdelim (we need to use those functions according to the implementation specifications).
According to the specification, the user input might contain arbitrary amount of lines and each line might be of arbitrary length (unknown at compile-time).
The problem is, the following line for the while-loop
while (cap = getdelim(stream.linesArray, size, '\n', stdin))
compiles and "works" somehow when I leave it like that. What I mean by this is that, when I execute the program, I enter arbitrary amount of lines of arbitrary length per each line and the program does not crash - but it keeps looping unless I stop the program execution (whether the lines are correctly stored in " char **linesArray; " are a different story I am not sure about.
I would like to be able to do is something like
while ((cap = getdelim(stream.linesArray, size, '\n', stdin)) && (cap != -1))
so that when getdelim does not read any characters at some line (besides EOF or \n) - aka the very first time when user enters an empty line -, the program would stop taking more lines from stdin.
(and then print the lines that were stored in stream.linesArray by getdelim).
The problem is, when I execute the program if I make the change I mentioned above, the program gives me "Segmentation Fault" and frankly I don't know why and how should I fix this (I have tried to do something about it so many times to no avail).
For reference:
https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html
https://en.cppreference.com/w/c/experimental/dynamic/getline
http://man7.org/linux/man-pages/man3/getline.3.html
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_SIZE 20
typedef unsigned long long int ull_int;
typedef struct uniqStream
{
char **linesArray;
ull_int lineIndex;
} uniq;
int main()
{
uniq stream = { malloc(DEFAULT_SIZE * sizeof(char)), 0 };
ull_int cap, i = 0;
size_t *size = 0;
while ((cap = getdelim(stream.linesArray, size, '\n', stdin))) //&& (cap != -1))
{
stream.lineIndex = i;
//if (cap == -1) { break; }
//print("%s", stream.linesArray[i]);
++i;
if (i == sizeof(stream.linesArray))
{
stream.linesArray = realloc(stream.linesArray, (2 * sizeof(stream.linesArray)));
}
}
ull_int j;
for (j = 0; j < i; ++j)
{
printf("%s\n", stream.linesArray[j]);
}
free(stream.linesArray);
return 0;
}
Ok, so the intent is clear - use getdelim to store the lines inside an array. getline itself uses dynamic allocation. The manual is quite clear about it:
getline() reads an entire line from stream, storing the address of the
buffer containing the text into *lineptr. The buffer is
null-terminated and includes the newline character, if one was found.
The getline() "stores the address of the buffer into *lineptr". So lineptr has to be a valid pointer to a char * variable (read that twice).
*lineptr and *n will be updated
to reflect the buffer address and allocated size respectively.
Also n needs to be a valid(!) pointer to a size_t variable, so the function can update it.
Also note that the lineptr buffer:
This buffer should be freed by the user program even if getline() failed.
So what do we do? We need to have an array of pointers to an array of strings. Because I don't like becoming a three star programmer, I use structs. I somewhat modified your code a bit, added some checks. You have the excuse me, I don't like typedefs, so I don't use them. Renamed the uniq to struct lines_s:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct line_s {
char *line;
size_t len;
};
struct lines_s {
struct line_s *lines;
size_t cnt;
};
int main() {
struct lines_s lines = { NULL, 0 };
// loop breaks on error of feof(stdin)
while (1) {
char *line = NULL;
size_t size = 0;
// we pass a pointer to a `char*` variable
// and a pointer to `size_t` variable
// `getdelim` will update the variables inside it
// the initial values are NULL and 0
ssize_t ret = getdelim(&line, &size, '\n', stdin);
if (ret < 0) {
// check for EOF
if (feof(stdin)) {
// EOF found - break
break;
}
fprintf(stderr, "getdelim error %zd!\n", ret);
abort();
}
// new line was read - add it to out container "lines"
// always handle realloc separately
void *ptr = realloc(lines.lines, sizeof(*lines.lines) * (lines.cnt + 1));
if (ptr == NULL) {
// note that lines.lines is still a valid pointer here
fprintf(stderr, "Out of memory\n");
abort();
}
lines.lines = ptr;
lines.lines[lines.cnt].line = line;
lines.lines[lines.cnt].len = size;
lines.cnt += 1;
// break if the line is "stop"
if (strcmp("stop\n", lines.lines[lines.cnt - 1].line) == 0) {
break;
}
}
// iterate over lines
for (size_t i = 0; i < lines.cnt; ++i) {
// note that the line has a newline in it
// so no additional is needed in this printf
printf("line %zu is %s", i, lines.lines[i].line);
}
// getdelim returns dynamically allocated strings
// we need to free them
for (size_t i = 0; i < lines.cnt; ++i) {
free(lines.lines[i].line);
}
free(lines.lines);
}
For such input:
line1 line1
line2 line2
stop
will output:
line 0 is line1 line1
line 1 is line2 line2
line 2 is stop
Tested on onlinegdb.
Notes:
if (i == sizeof(stream.linesArray)) sizeof does not magically store the size of an array. sizeof(stream.linesArray) is just sizeof(char**) is just a sizeof of a pointer. It's usually 4 or 8 bytes, depending if on the 32bit or 64bit architecture.
uniq stream = { malloc(DEFAULT_SIZE * sizeof(char)), - stream.linesArray is a char** variable. So if you want to have an array of pointers to char, you should allocate the memory for pointers malloc(DEFAULT_SIZE * sizeof(char*)).
typedef unsigned long long int ull_int; The size_t type if the type to represent array size or sizeof(variable). The ssize_t is sometimes used in posix api to return the size and an error status. Use those variables, no need to type unsigned long long.
ull_int cap cap = getdelim - cap is unsigned, it will never be cap != 1.

Access value of a pointer in dynamically allocated memory

My assignment is to read words from a text file and store them in character arrays which are stored in an array of char*. All memory in these arrays needs to be dynamically allocated.
What I am doing is reading in each word with fscanf() and storing it into the variable str. I am then calculating the length of the word in str and dynamically allocating memory to store the value of str in the character array new_word. new_word is then inserted into the array of char* named words. When words runs out of space, I double its size and continue.
My problem lies in the commented code starting on line 62. I'm going to need to read these words later from words, so I'm testing my ability to access the pointers and their values. I can index new_word fine (in the lines above), but when I then store new_word in words and try to read from words, I get the following error:
hw1.c:63:25: error: subscripted value is not an array, pointer, or vector
while (*(words[count])[k] != '\0'){
on lines 63 and 64. I know it has something to do with dereferencing the pointer, but I have tried a bunch of variations with no success. How can I fix this?
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]){
if (argc != 3){
fprintf(stderr, "Incorrect number of arguments\n");
exit(1);
}
char* infile = argv[1];
FILE* finp = fopen(infile, "r");
if (finp == NULL){
fprintf(stderr, "Unable to open input file\n");
exit(1);
}
char* prefix = argv[2];
int count = 0;
int size = 20;
char* words = calloc(size, sizeof(char));
printf("Allocated initial array of 20 character pointers.\n");
char* str = malloc(30*sizeof(char));
while (fscanf(finp, "%s", str) == 1){
if (count == size){
words = realloc(words, 2 * size);
size *= 2;
printf("Reallocated array of %d character pointers.\n", size);
}
int i = 0;
while (str[i] != '\0'){
i++;
}
char* new_word = malloc((i+1)*sizeof(char));
int j = 0;
while (str[j] != '\0'){
new_word[j] = str[j];
j++;
}
new_word[j] = '\0';
int k = 0;
while (new_word[k] != '\0'){
printf("%c", new_word[k]);
k++;
}
printf("\n");
words[count] = *new_word;
/*k = 0;
while (*(words[count])[k] != '\0'){
printf("%c", *(words[count])[k]);
k++;
}
printf("\n");*/
count++;
}
}
Ok, dissecting that a bit:
char* words = calloc(size, sizeof(char));
this should probably read:
char **words = calloc(size, sizeof(char *));
Why? What you want here is a pointer to an array of pointers to char ... words points to the first char *, which points to your first "string".
char* str = malloc(30*sizeof(char));
while (fscanf(finp, "%s", str) == 1){
Buffer overflow here. Make sure to read at maximum 30 characters if you define your buffer not to hold more. Btw, just for convention, call your buffer buffer or buf (not str) and there's really no need to dynamically allocate it. Hint: Use a field size for fscanf() or, even better, some other function like fgets().
if (count == size){
words = realloc(words, 2 * size);
size *= 2;
printf("Reallocated array of %d character pointers.\n", size);
}
The realloc here will not work, should read
words = realloc(words, 2 * size * sizeof(char *));
You need to multiply the size of a single element, which, in this case, is a pointer to char.
No guarantee this will be all errors, but probably the most important ones. On a sidenote, strlen() and strncpy() will help you stop writing unnecessary code.
A pointer to "A [dynamically-allocated] array of char*" would need to be recorded in a variable of type char **. That is, a pointer to the first element of the array, which element is of type char *. Thus ...
char **words;
If you want to have sufficient space for size words, then you could allocate it as ...
words = calloc(size, sizeof(char *));
(note the difference from your code), though it's harder to make a mistake with this form:
words = calloc(size, sizeof(*words));
Note in that case that the sizeof operator does not evaluate its operand, so it does not matter that words is not yet allocated.
Most importantly, be aware that the elements of array words are themselves pointers, not the ultimately pointed-to strings. Thus you assign a new word to the array by
words[count] = new_word;
(Again, note the difference from your version.) Other adjustments are needed as well.
The problematic while loop, though, is not fixed even then. Remember that the expression pointer[index] is equivalent to *((pointer) + (index)), so the expression *(words[count])[k] attempts to triply derference words. Even with the type correction, you want only to doubly dereference it: words[count][k].
But why re-invent the wheel? As Olaf observed with respect to strlen() and some of your earlier code, C already has perfectly good functions in its standard library for dealing with strings. In this case ...
printf("%s", words[count]);
... would be so much simpler than that while loop.

append a character from an array to a char pointer

Ok, so I'm a person who usually writes Java/C++, and I've just started getting into writing C. I'm currently writing a lexical analyser, and I can't stand how strings work in C, since I can't perform string arithmetic. So here's my question:
char* buffer = "";
char* line = "hello, world";
int i;
for (i = 0; i < strlen(line); i++) {
buffer += line[i];
}
How can I do that in C? Since the code above isn't valid C, how can I do something like that?
Basically I'm looping though a string line, and I'm trying to append each character to the buffer string.
string literals are immutable in C. Modifying one causes Undefined Behavior.
If you use a char array (your buffer) big enough to hold your characters, you can still modify its content :
#include <stdio.h>
int main(void) {
char * line = "hello, world";
char buffer[32]; // ok, this array is big enough for our operation
int i;
for (i = 0; i < strlen(line) + 1; i++)
{
buffer[i] = line[i];
}
printf("buffer : %s", buffer);
return 0;
}
First off the buffer needs to have or exceed the length of the data being copied to it.
char a[length], b[] = "string";
Then the characters are copied to the buffer.
int i = 0;
while (i < length && b[i] != '\0') { a[i] = b[i]; i++; }
a[i] = '\0';
You can reverse the order if you need to, just start i at the smallest length value among the two strings, and decrement the value instead of increment. You can also use the heap, as others have suggested, ordinate towards an arbitrary or changing value of length. Furthermore, you can change up the snippet with pointers (and to give you a better idea of what is happening):
int i = 0;
char *j = a, *k = b;
while (j - a < length && *k) { *(j++) = *(k++); }
*j = '\0';
Make sure to look up memcpy; and don't forget null terminators (oops).
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
strcpy( buffer, line );
Though in C string literals have types of non-const arrays it is better to declare pointers initialized by string literals with qualifier const:
const char *line = "hello, world";
String literals in C/C++ are immutable.
If you want to append characters then the code can look the following way (each character of line is appended to buffer in a loop)
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
buffer[0] = '\0';
char *p = Buffer;
for ( size_t i = 0; i < strlen( line ); i++ )
{
*p++ = line[i];
*p = '\0';
}
The general approach is that you find the pointer to the terminating zero substitute it for the target character advance the pointer and appenf the new terminating zero. The source buffer shall be large enough to accomodate one more character.
If you want to append a single character to a string allocated on the heap, here's one way to do it:
size_t length = strlen(buffer);
char *newbuffer = realloc(buffer, length + 2);
if (newbuffer) { // realloc succeeded
buffer = newbuffer;
buffer[length] = newcharacter;
buffer[length + 1] = '\0';
}
else { // realloc failed
// TODO handle error...
free(buffer); // for example
}
However, this is inefficient to do repeatedly in a loop, because you'll be repeatedly calling strlen() on (essentially) the same string, and reallocating the buffer to fit one more character each time.
If you want to be smarter about your reallocations, keep track of the buffer's current allocated capacity separately from the length of the string within it — if you know C++, think of the difference between a std::string object's "size" and its "capacity" — and when it's necessary to reallocate, multiply the buffer's size by a scaling factor (e.g. double it) instead of adding 1, so that the number of reallocations is O(log n) instead of O(n).
This is the sort of thing that a good string class would do in C++. In C, you'll probably want to move this buffer-management stuff into its own module.
The simplest solution, lacking any context, is to do:
char buffer[ strlen(line) + 1 ];
strcpy(buffer, line);
You may be used to using pointers for everything in Java (since non-primitive types in Java are actually more like shared pointers than anything else). However you don't necessarily have to do this in C and it can be a pain if you do.
Maybe a good idea given your background would be to use a counted string object in C, where the string object owns its data. Write struct my_string { char *data; size_t length; } . Write functions for creating, destroying, duplicating, and any other operation you need such as appending a character, or checking the length. (Separate interface from implementation!) A useful addition to this would be to make it allocate 1 more byte than length, so that you can have a function which null-terminates and allows it to be passed to a function that expects a read-only C-style string.
The only real pitfall here is to remember to call a function when you are doing a copy operation, instead of allowing structure assignment to happen. (You can use structure assignment for a move operation of course!)
The asprintf function is very useful for building strings, and is available on GNU-based systems (Linux), or most *BSD based systems. You can do things like:
char *buffer;
if (asprintf(&buffer, "%s: adding some stuff %d - %s", str1, number, str2) < 0) {
fprintf(stderr, "Oops -- out of memory\n");
exit(1); }
printf("created the string \"%s\"\n", buffer);
free(buffer); /* done with it */
Appending is best done with snprintf
Include the stdio.h header
#include <stdio.h>
then
char* buffer;
char line[] = "hello, world";
// Initialise the pointer to an empty string
snprintf(buffer, 1, "%s", "");
for (i = 0; i < strlen(line); ++i) {
snprintf(buffer, sizeof line[i], "%s%s", buffer, line[i]);
}
As you have started the code you have there is different from the question you are asking.
You could have split the line with strtok though.
But I hope my answer clarifies it.

from static array assignment to array from file

I have this piece of code outside the main function
mystr * arrstr[] = {
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};
I modified it so that it can read the values from a text file. for this purpose i modified this working code to read line from file into array named string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int i=0,j;
char* string[100];
char line[100];
FILE *file;
file = fopen("patt", "r");
while(fgets(line, sizeof(line), file)!=NULL) {
printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return 0;
}
so the final code is now something like this.
..
..
char *getpatterns(const char *filename) {
int i=0;
char* string[100];
char line[100];
FILE *file;
file = fopen(filename, "r");
while(fgets(line, sizeof(line), file)!=NULL) {
//printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return(string);
}
mystr * arrstr[] = getpatterns("patt");/*{
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};*/
..
..
But i get errors like this.
example1.c: In function ‘getpatterns’:
example1.c:43:2: warning: return from incompatible pointer type [enabled by default]
example1.c:43:2: warning: function returns address of local variable [enabled by default]
example1.c: At top level:
example1.c:45:1: error: invalid initializer
make: *** [example1.o] Error 1
Here line 45 is this line
mystr * arrstr[] = getpatterns("patt");/*{
Please suggest corrective action.
The first warnings are that you are trying to return a char ** as a char * (which is not a good idea), and that you are returning a local variable which is deallocated when the function returns (also not a good idea). The last is telling you that you can't use function calls in initializers of global variables in C (you can do some of that in C++, though I'm not convinced you can do this one).
Fixing it will take some rethinking. You need the function to return allocated memory, or you need to pass the memory to the function. And you'll have to change the type of the global variable. And you'll need to know how many entries there are in the array, somehow.
mystr **arrstr = 0; // Either
mystr *arrstr[100]; // Or
On the whole, I'd probably go with memory allocation and the 'either' declaration:
mystr **arrstr = 0;
char **getpatterns(const char *file)
{
char **array = 0;
...code similar to yours that allocates entries in the array...
...include space for a null pointer to mark the end of the list of strings...
return(array);
}
int main(void)
{
arrstr = getpatterns("patt");
...
}
(Another 'cheat' mechanism would use static char *string[100]; in getpatterns(); you still have to fix the return type and the type of the global variable.)
I tried these but, errors were not resolved: ...
It's impossible to tell exactly what was wrong without your code. However, the code below works for me. The source code was in a file gp.c; the source code prints itself, and releases the memory. Checked under valgrind with a clean bill of health.
Note that your original code did not allocate enough space for the strings it was copying (because you retained the newline read by fgets() — but you were at least using fgets() and not gets(), which is very important). This code uses memmove() — it could use memcpy() instead since there's guaranteed to be no overlap, but memmove() always works and memcpy() doesn't necessarily work when the source data overlaps the target data. It knows how long the string is, so the copy function doesn't need to test for whether the character being copied is a NUL '\0'. The code carefully ensures that there's a null pointer at the end of the list of pointers; that's how you know when you've reached the end of the list of strings. The code also works when gp.c is an empty file.
The algorithm using three items num_xxx, max_xxx, and xxx is a typical way to handle incremental allocation. It typically over-allocates slightly; if you're concerned about the space, you could use strings = realloc(strings, (num_strings+1) * sizeof(*strings)); max_strings = num_strings + 1; at the end of the loop to release the extra space. The + 1 is to allow for the null pointer. By roughly doubling the size allocated each time you allocate, you avoid quadratic behaviour compared with incrementing by one each time.
Notice too that the code carefully avoids losing the allocated space if the realloc() fails. You should 'never' use space = realloc(space, new_size); to avoid losing your pointer. The code carefully avoids dereferencing null pointers, and simply stops reading when there is a memory shortage.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **getpatterns(const char *filename);
char **getpatterns(const char *filename)
{
size_t num_strings = 0;
size_t max_strings = 0;
char **strings = 0;
FILE *file = fopen(filename, "r");
if (file != 0)
{
char line[4096];
while (fgets(line, sizeof(line), file) != NULL)
{
if (max_strings == 0 || num_strings >= max_strings - 1)
{
size_t new_num = max_strings * 2 + 2;
char **new_space = realloc(strings, new_num * sizeof(*new_space));
if (new_space == 0)
break;
strings = new_space;
max_strings = new_num;
}
size_t len = strlen(line); /* Includes '\n' at end */
strings[num_strings] = (char*)malloc(len);
memmove(strings[num_strings], line, len - 1);
strings[num_strings][len] = '\0';
strings[++num_strings] = 0; /* Null terminate list of strings */
}
fclose(file);
}
return(strings);
}
int main(void)
{
char **data = getpatterns("gp.c");
char **argp = data;
if (argp != 0)
{
/* Print data */
while (*argp != 0)
puts(*argp++);
/* Free space */
argp = data;
while (*argp != 0)
free(*argp++);
free(data);
}
return(0);
}

Resources