C - Appending to string - Possible memory errors - c

I am trying to a create a function that keeps on appending a string to a char variable. However, some times it works and other times it doesn't. I am wondering where the bug is?
char *final_output = NULL;
void add_string(const char *);
int main(void) {
add_string("Hello world\n");
add_string("This is my new function!\n");
/* Let's print */
while (final_output && *final_output) {
printf("%c", *final_output);
*final_output++;
}
}
void add_string(const char *text) {
if (final_output == NULL) {
final_output = malloc(strlen(text) + 1);
}
else {
final_output = (char *) realloc(final_output, strlen(final_output) + strlen(text) + 2);
}
strncat(final_output, text, strlen(text));
}

The problem is in function add_string. You do not append the allocated or copied array with the terminating zero after statements
final_output = malloc(strlen(text) + 1);
and
strncat(final_output, text, strlen(text));
Rewrite the function the following way
void add_string( const char *s )
{
if ( final_output == NULL )
{
final_output = malloc( strlen( s ) + 1 );
final_output[0] = '\0';
}
else
{
final_output = realloc( final_output, strlen( final_output ) + strlen( s ) + 1 );
}
strcat( final_output, s );
}

Related

Is there any way to split a CONST char * with a delimiter into an array?

I'm trying to split a given string (input) into an array of elements. Here is my code:
char *buff = save_to_buff(); // save the input
int token_count = 1;
for(int i = 0; buff[i] != '\0';i++)
{
if(buff[i] == ' ')
{
token_count++;
}
}
char *token = strtok(buff, " ");
char *arr[token_count];
for(int i = 0;token != NULL;i++)
{
arr[i] = token;
token = strtok(NULL, " ");
}
for(int i = 0; i < token_count;i++)
{
printf("%s ", arr[i]);
}
It works, however I need to make a function char **parse_cmdline(const char *cmdline) which splits the buff(cmdline) in this case into an array, but how can I do that if it is even possible? I either get a warning that the 'const' qualifier is discared or an error. Is there any way?
You can split the function into two functions.
The first one will return the number of tokens in a given string. Using the return value of the function you can allocate an array of pointers the number of elements of which is equal to the number of tokens in the given string plus one. That is the array of tokens will end with a null pointer.
The second function will fill the provided array with tokens of the given string.
Here is a demonstration program.
#include <stdio.h>
#include <string.h>
size_t count_tokens( const char *s1, const char *s2 )
{
size_t n = 0;
while (*s1)
{
s1 += strspn( s1, s2 );
if (*s1)
{
++n;
s1 += strcspn( s1, s2 );
}
}
return n;
}
size_t get_tokens( char **s1, const char *s2, const char *s3 )
{
size_t n = 0;
while (*s2)
{
s2 += strspn( s2, s3 );
if (*s2)
{
++n;
const char *p = s2;
s2 += strcspn( s2, s3 );
size_t len = s2 - p;
*s1 = malloc( len + 1 );
if (*s1)
{
memcpy( *s1, p, len );
( *s1 )[len] = '\0';
}
++s1;
}
}
*s1 = NULL;
return n;
}
int main( void )
{
const char *s1 = "Hello World!";
size_t n = count_tokens( s1, " " );
printf( "%zu\n", n );
char **p = malloc( ( n + 1 ) * sizeof( char * ) );
get_tokens( p, s1, " " );
for ( size_t i = 0; i < n; i++ )
{
if ( p[i] ) puts( p[i] );
}
for (size_t i = 0; i < n; i++)
{
free( p[i] );
}
free( p );
}
The program output is
2
Hello
World!
As a delimiter of tokens you can pass any string to the functions as for example " \t\n'.
const objects cannot be modified. It is undefined behaviour. You need to make a modifiable copy of the string before you use strtok.
char **split(const char *restrict str, const char *restrict delim)
{
char **result = NULL;
char *copy;
size_t ntokensLen;
if(str && delim && *str && *delim)
{
copy = malloc(ntokensLen = strlen(str + 1));
if(copy)
{
char *token;
memcpy(copy, str, ntokensLen + 1);
ntokensLen = 0;
token = strtok(copy, delim);
if(!token) free(copy);
while(token)
{
char **tmp;
tmp = realloc(result, (ntokensLen + 2) * sizeof(*tmp));
if(!tmp) { /* error hanling */}
result = tmp;
result[ntokensLen] = token;
result[ntokensLen + 1] = NULL;
token = strtok(NULL, delim);
ntokensLen++;
}
}
}
return result;
}
int main(void)
{
const char *str = "This!is string ^to test...";
char **result = split(str, "! ^.");
size_t cnt = 0;
while(result[cnt])
{
printf("result[%zu] = `%s`\n", cnt, result[cnt]);
cnt++;
}
// how to free?
free(result[0]);
free(result);
}
EDIT:
Added How to free. result holds the reference to realloced memory, result[0] to malloced.
result is NULL pointer terminated.
Other split versions:
char **mystrtok(const char *str, const char *del, int alowempty)
{
char **result = NULL;
const char *end = str;
size_t size = 0;
int extrachar;
while(*end)
{
if((extrachar = !!strchr(del, *end)) || !*(end + 1))
{
/* add temp variable and malloc / realloc checks */
/* free allocated memory on error */
if(!(!alowempty && !(end - str)))
{
extrachar = !extrachar * !*(end + 1);
result = realloc(result, (++size + 1) * sizeof(*result));
result[size] = NULL;
result[size -1] = malloc(end - str + 1 + extrachar);
strncpy(result[size -1], str, end - str + extrachar);
result[size -1][end - str + extrachar] = 0;
}
str = end + 1;
}
end++;
}
return result;
}
double pointer supplied by the caller
char **split(char **argv, int *argc, const char *str, const char *delimiter, int allowempty)
{
char *string = malloc(strlen(str + 1));
strcpy(string, str);
*argc = 0;
do
{
if(*string && (!strchr(delimiter, *string) || allowempty))
{
argv[(*argc)++] = string;
}
while(*string && !strchr(delimiter, *string)) string++;
if(*string) *string++ = 0;
if(!allowempty)
while(*string && strchr(delimiter, *string)) string++;
}while(*string);
return argv;
}

Parse comma serpataed file of different types

This is what I came up with for reading a csv file with multiple types. It seems to get the job done in all cases but 1,2,,"a". Where there is a blank space. Can I please have some ideas on how to fix this?
const char* getfield(char* line, int num)
{
const char* tok;
for (tok = strtok(line, ",");
tok && *tok;
tok = strtok(NULL, ",\n"))
{
if (!--num)
return tok;
}
return NULL;
}
while(fgets(line, 80, inputfp1) != NULL)
{
printf(" line is %s \n", line);
char* tmp1 = strdup(line);
char* tmp2 = strdup(line);
char* tmp3 = strdup(line);
char* tmp4 = strdup(line);
printf("Field 1 would be %s\n", getfield(tmp1, 1));
printf("Field 2 would be %s\n", getfield(tmp2, 2));
printf("Field 3 would be %s\n", getfield(tmp3, 3));
printf("Field 4 would be %s\n", getfield(tmp4, 4));
// NOTE strtok clobbers tmp
free(tmp1);
free(tmp2);
free(tmp3);
free(tmp4);
//sscanf(line, "%d, %d, %d, %d", &column1[i], &column2[i], &column3[i], &column4[i]);
//printf(" column1[i] is %d column2[i] is %d column3[i] is %d column4[i] is %d \n", column1[i], column2[i], column3[i], column4[i]);
i++;
memset(line, 0, 80);
}
Ok I used a library like was suggested and don't understand why this is not working.
https://ideone.com/RylYp1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Given a string which might contain unescaped newlines, split it up into
* lines which do not contain unescaped newlines, returned as a
* NULL-terminated array of malloc'd strings.
*/
char **split_on_unescaped_newlines(const char *txt) {
const char *ptr, *lineStart;
char **buf, **bptr;
int fQuote, nLines;
/* First pass: count how many lines we will need */
for ( nLines = 1, ptr = txt, fQuote = 0; *ptr; ptr++ ) {
if ( fQuote ) {
if ( *ptr == '\"' ) {
if ( ptr[1] == '\"' ) {
ptr++;
continue;
}
fQuote = 0;
}
} else if ( *ptr == '\"' ) {
fQuote = 1;
} else if ( *ptr == '\n' ) {
nLines++;
}
}
buf = malloc( sizeof(char*) * (nLines+1) );
if ( !buf ) {
return NULL;
}
/* Second pass: populate results */
lineStart = txt;
for ( bptr = buf, ptr = txt, fQuote = 0; ; ptr++ ) {
if ( fQuote ) {
if ( *ptr == '\"' ) {
if ( ptr[1] == '\"' ) {
ptr++;
continue;
}
fQuote = 0;
continue;
} else if ( *ptr ) {
continue;
}
}
if ( *ptr == '\"' ) {
fQuote = 1;
} else if ( *ptr == '\n' || !*ptr ) {
size_t len = ptr - lineStart;
if ( len == 0 ) {
*bptr = NULL;
return buf;
}
*bptr = malloc( len + 1 );
if ( !*bptr ) {
for ( bptr--; bptr >= buf; bptr-- ) {
free( *bptr );
}
free( buf );
return NULL;
}
memcpy( *bptr, lineStart, len );
(*bptr)[len] = '\0';
if ( *ptr ) {
lineStart = ptr + 1;
bptr++;
} else {
bptr[1] = NULL;
return buf;
}
}
}
}
int main(void) {
// your code goes here
char line[80] = "52,243,,542";
split_on_unescaped_newlines(line);
printf("line %s\n", line[0]);
printf("line %s\n", line[1]);
return 0;
}

Assigning data to 2D array and displaying it C

I am writing a program, that will open csv file and save data to 3D array.
Most of code works pretty good, but I have a problem with assiging records to 2D array.
Here is a code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_MODE "r"
/*
Explode string with given token and assign result to list variable
*/
int explode(const char *src, const char *tokens, char ***list, size_t *len)
{
if(src == NULL || list == NULL || len == NULL) {
return 0;
}
char *str, *copy, **_list = NULL, **tmp;
*list = NULL;
*len = 0;
copy = strdup(src);
if(copy == NULL)
return 1;
str = strtok(copy, tokens);
if(str == NULL) {
goto free_and_exit;
}
_list = realloc(NULL, sizeof *_list);
if(_list == NULL) {
goto free_and_exit;
}
_list[*len] = strdup(str);
if(_list[*len] == NULL)
goto free_and_exit;
(*len)++;
while((str = strtok(NULL, tokens)))
{
tmp = realloc(_list, (sizeof *_list) * (*len + 1));
if(tmp == NULL)
goto free_and_exit;
_list = tmp;
_list[*len] = strdup(str);
if(_list[*len] == NULL)
goto free_and_exit;
(*len)++;
}
free_and_exit:
*list = _list;
free(copy);
return 2;
}
/*
Exploding lines in CSV file
*/
const char* getfield(char* line, int num)
{
const char* tok;
for (tok = strtok(line, ";");
tok && *tok;
tok = strtok(NULL, ";\n"))
{
if (!--num)
return tok;
}
return NULL;
}
int main()
{
FILE *stream;
char fileName[256], table[256], line[256],
**list, **columns, **data;
size_t length;
printf("Witaj uzytkowniku! Podaj nazwe pliku z rozszerzeniem .csv. \n");
scanf("%s", fileName);
explode(fileName, ".", &list, &length);
strcpy(table, list[0]);
stream = fopen("file.csv", FILE_MODE); // not to write path every single time
if (stream == NULL) {
printf("Nie moge otworzyc pliku %s do odczytu!\n", fileName);
exit(1);
}
fgets(line, sizeof line, stream);
explode(line, ";", &columns, &length);
int recordNumber = 0
,columnNumber = 0;
while (fgets(line, sizeof line, stream))
{
char* tmp = strdup(line);
if (getfield(tmp, recordNumber) != NULL) {
columnNumber++;
}
recordNumber++;
free(tmp);
}
fseek(stream, 0, SEEK_SET); // Go to beginning of file
fgets(line, 1024, stream);
int i = 0 // Number of records
,h = 0; // number of columns
char **records[recordNumber][columnNumber];
length = 0;
char *tmp[recordNumber];
// Here I get number of lines and columns in csv file to make 3D array??
while (fgets(line, sizeof line, stream) && i < recordNumber)
{
tmp[i] = strdup(line);
explode(tmp[i], ";", &data, &length);
for (h = 0; h < columnNumber; h++)
{
memcpy(records[i][h], data[h], sizeof(data[h]));
}
i++;
}
for (i = 0; i < recordNumber; i++)
{
for (h = 0; h < columnNumber; h++)
{
printf("%s ", records[i][h][0]);
}
printf("\n");
}
fclose(stream);
return EXIT_SUCCESS;
}
Problem starts, when I try do a loop, that assign data to array:
while (fgets(line, sizeof line, stream) && i < recordNumber)
{
tmp[i] = strdup(line);
explode(tmp[i], ";", &data, &length);
for (h = 0; h < columnNumber; h++)
{
memcpy(records[i][h], data[h], sizeof(data[h]));
}
i++;
}
I tried to use memcpy and strcpy, but none works correctly - I am pretty sure.
When code goes to these lines, there is an error: segmentation fault (core dumping).
All i want to achieve is to fill this array with data from csv file and print it.
Thanks for your help! :)
EDIT:
explode function is not mine. Probably, I found it somewhere on stackoverflow.
When, it comes to the code, after little change, it works
char records[recordNumber][columnNumber][1024];
length = 0;
char *tmp[recordNumber];
while (fgets(line, sizeof line, stream) && i < recordNumber)
{
tmp[i] = strdup(line);
explode(tmp[i], ";", &data, &length);
for (h = 0; h < columnNumber; h++)
{
strcpy(records[i][h], data[h]);
}
i++;
}
Read each line of the file with fgets. strpbrk can be used to find the delimiters. Two pointers can be used to get the number of characters between the delimiters. Then allocate memory and use memcpy to copy the field to the allocated memory.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char ***strpdlm ( char *pdelim, int skip);
char ***freedlm ( char ***ppp);
int main()
{
char ***expld = NULL;
int line = 0;
int field = 0;
//last argument of 1 is skip consecutive delimiters. 0 do not skip
expld = strpdlm ( ";\n", 1);// delimiters semicolon and newline
//print each extracted string
line = 0;
if ( expld) {//not null
while ( expld[line]) {//not null
field = 0;
printf ( "\nfields for line %d\n", line);
while ( expld[line][field]) {//not null
printf ( "expld[%d][%d] %s\n", line, field, expld[line][field]);
field++;
}
line++;
}
}
//free memory and set NULL
expld = freedlm ( expld);
return 0;
}
char ***freedlm ( char ***ppp) {
size_t each = 0;
size_t item = 0;
if ( ppp) {
while ( ppp[each]) {
item = 0;
while ( ppp[each][item]) {
free ( ppp[each][item]);
item++;
}
free ( ppp[each]);
each++;
}
free ( ppp);
}
return NULL;
}
char ***strpdlm ( char *pdelim, int skip) {
char ***xpld = NULL;
char ***temprecord = NULL;
char **tempfield = NULL;
char *pnt = NULL;
char *cur = NULL;
char line[1024] = "";
int span = 0;
int len = 0;
int record = 0;
int field = 0;
FILE *pf = NULL;
if ( ( pf = fopen ( "file.csv", "r")) == NULL) {
perror ( "could not open \"file.csv\"");
return NULL;
}
if ( pdelim) {
while ( fgets ( line, sizeof line, pf)) {
//make sure each line ends with \n
len = strcspn ( line, "\n");
if ( len + 1 < sizeof line) {
line[len] = '\n';
line[len + 1] = '\0';
}
//allocate record + 2 pointers
if ( ( temprecord = realloc ( xpld, ( record + 2) * sizeof ( *xpld))) == NULL) {
fprintf ( stderr, "problem realloc records\n");
fclose ( pf);
return xpld;
}
xpld = temprecord;
xpld[record] = NULL;
field = 0;
cur = line;//cur points to line
while ( ( pnt = strpbrk ( cur, pdelim))) {
if ( pnt != cur || !skip) {
if ( ( tempfield = realloc ( xpld[record], ( field + 2) * sizeof ( **xpld))) == NULL) {
fprintf ( stderr, "problem realloc fields\n");
fclose ( pf);
return xpld;
}
xpld[record] = tempfield;
xpld[record][field] = NULL;
if ( pnt) {
span = pnt - cur;
}
else {
span = strlen ( cur);
}
if ( ( xpld[record][field] = malloc ( span + 1)) == NULL) {
fprintf ( stderr, "problem malloc\n");
fclose ( pf);
return xpld;
}
memcpy ( xpld[record][field], cur, span);
xpld[record][field][span] = '\0';
field++;
xpld[record][field] = NULL;//sentinel NULL
}
cur = pnt + 1;//set cur to point to next field
}
record++;
xpld[record] = NULL;//sentinel NULL
}
}
fclose ( pf);
return xpld;
}

Read a string and get it in (int) in C

Can you help me? I have a string 23;56;36.6;run in a txt file.Then I am reading this string in order to use it for some work: I would like to take this values from a string, then compare them with some values in a code and output my result in console. I think,I should use atoi() function that make my string in numbers, for picking out , I am using strtok(). But how correctly should I record my tokens in loop while and the last token is a type of char. How can I do this work?
CODE:
void printInfo(int note)
{
int i;
FILE *out;
char str[250];
char sp[10]=";";
char *istr;
if ((out =fopen("test.txt","r"))==NULL)
printf("Error open, file\n");
else
{
for (i=0;i<note;i++)
{
fgets(str,250,out);
istr=strtok(str,sp);
while (istr != NULL)
{
printf("%d\n",atoi(istr));
istr=strtok(NULL,sp);
// I think, I need to create a variable for recording my values.
}
}
}
fclose(out);
}
I would use sscanf to convert the string to the three floats:
#include <stdio.h> // sscanf
#include <stdlib.h> // EXIT_SUCCESS
#include <string.h> // memset
int main(void) {
const char *input = "23;56;36.6;run";
int i;
float numbers[3] = {0, 0, 0};
char buf[10];
int nElementsRead;
// init buf
memset(buf, 0, sizeof(buf));
// sscanf returns the number of read elements
// or EOF on error
nElementsRead = sscanf(input, "%f;%f;%f;%9s", &numbers[0], &numbers[1], &numbers[2], buf);
if (nElementsRead == 4) {
printf("Successfully read %d elements\n", nElementsRead);
for (i = 0; i < 3; ++i) {
printf("number[%d]: %f\n", i, numbers[i]);
}
printf("Buffer is: %s\n", buf);
} else {
printf("Something went wrong!");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Another solution, using comparable records:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct MyRecord_s {
int f1;
int f2;
float f3;
char f4[10];
} MyRecord;
static int MyRecordInit( MyRecord * out, char * line ) {
if( out == NULL ) {
return 0;
}
char * f1 = strtok( line, ";" );
char * f2 = strtok( NULL, ";" );
char * f3 = strtok( NULL, ";" );
char * f4 = strtok( NULL, ";" );
if( f1 && f2 && f3 && f4 ) {
char * err = NULL;
out->f1 = strtol( f1, &err, 10 );
if( err && *err ) {
return 0;
}
out->f2 = strtol( f1, &err, 10 );
if( err && *err ) {
return 0;
}
out->f3 = strtof( f1, &err );
if( err && *err ) {
return 0;
}
strncpy( out->f4, f4, 10 );
out->f4[9] = '\0';
return 1;
}
return 0;
}
int MyRecordCmp( const MyRecord * r1, const MyRecord * r2 ) {
int diff = r1->f1 - r1->f2;
if( diff ) {
return diff;
}
diff = r1->f2 - r2->f2;
if( diff ) {
return diff;
}
float d = r1->f3 - r2->f3;
if( d > 0.000001 ) {
return +1;
}
if( d < -0.000001 ) {
return -1;
}
return strcmp( r1->f4, r2->f4 );
}
int main() {
char line1[] = "23;56;36.6;run";
char line2[] = "24;57;37.6;stop";
MyRecord r1, r2;
if( MyRecordInit( &r1, line1 ) && MyRecordInit( &r2, line2 )) {
printf( "cmp: %d\n", MyRecordCmp( &r1, &r2 ));
}
return 0;
}

how to implement next() PHP function in C

I am tried to implement next() PHP function in C. if I have
The different to my implementation is I want to do this work with more one points. For example:
if I have two char * like:
char * a = "ac\0";
char * b = "bd\0";
and I call:
printf("%c", cgetnext(a));
printf("%c", cgetnext(b));
printf("%c", cgetnext(a));
printf("%c", cgetnext(b));
gets an output like: abcd
but I get abab
here is my code:
`#include <string.h>
#include <stdlib.h>
typedef struct
{
int pLocation;
int myindex;
int lastIndex;
} POINTERINFORMATION;
int __myIndex = 0;
int pointersLocationsLength = 0;
char * temparr = NULL;
POINTERINFORMATION pointersLocations[256];
int
plAdd (int p)
{
if (pointersLocationsLength >= sizeof(pointersLocations)) {
return -1;
} else {
pointersLocations[pointersLocationsLength].pLocation = p;
pointersLocations[pointersLocationsLength].lastIndex = 0;
pointersLocationsLength ++;
return pointersLocationsLength;
}
}
int
getPointer (int p, POINTERINFORMATION * out)
{
int i;
for (i = 0; i < pointersLocationsLength; i++)
{
if(pointersLocations[pointersLocationsLength].pLocation == p)
{
pointersLocations[i].myindex = i;
*out = pointersLocations[i];
return 1;
}
}
return 0;
}
void
getPointerIndex(char ** variable, int * val)
{
char * buf = malloc(256);
if(sprintf(buf,"%p", &variable) > 0){
*val = strtol(buf, NULL, 16 );
} else {
*val = -1;
}
}
int
inArrayOfPointers (int pointer)
{
POINTERINFORMATION pi;
return getPointer(pointer, &pi);
}
char
cgetnext(char * arr)
{
char * myarr;
const size_t size = sizeof(char *) + 1;
int pof;
myarr = malloc(size);
getPointerIndex (&arr, &pof);
if (inArrayOfPointers(pof)){
POINTERINFORMATION pi;
if (getPointer(pof, &pi))
{
myarr = (char *)*(int *)pi.pLocation;
__myIndex = pi.lastIndex;
++pointersLocations[pi.myindex].myindex;
} else {
return 0;
}
} else {
if (plAdd(pof) == -1) {
printf(" CANNOT ADD ELEMENT TO ARRAY\n");
exit(0);
} else {
myarr = arr;
__myIndex = 0;
}
}
if (strlen(myarr) == __myIndex) {
return 0;
} else {
temparr = malloc(size);
temparr = strdup(myarr);
return myarr[__myIndex];
}
}`
how to fix this? differents solutions for solve it are very apreciated! Thanks in advance.
If you are specifically interested in char* "arrays", then a simple solution might be to just increment the pointer. Note that if using dynamically allocated memory, you need to save the original pointer to free it. The same idea could be used for other data types as well.
This is an example of what I mean. cgetnext here just returns the character at the current position in the array and increments pointer (that is passed by address).
char cgetnext( char **p )
{
assert( p != NULL );
// check for end of string
if ( **p == '\0' )
return '\0';
return *(*p)++;
}
int main( int argc, char* argv[] )
{
char *a = "ac";
char *b = "bd";
printf( "%c", cgetnext(&a));
printf( "%c", cgetnext(&b));
printf( "%c", cgetnext(&a));
printf( "%c", cgetnext(&b));
}

Resources