This question already has answers here:
How do I concatenate const/literal strings in C?
(17 answers)
Closed 6 years ago.
void xsdValidation(char *xsdName, char *xmlName){
char *terminalCommand;
system("xmllint --noout --schema ", xsdName , xmlName);
}
I have a little problem with that. I have a code to valide my xml. And my xml's name and xsd's name came from as a argument. How can i concat these 3 things?
Use snprintf():
/* determine buffer size */
int len = snprintf(NULL, 0, "xmllint, --noout --schema %s %s", xsdName, xmlName);
if (len < 0) {
/* error handling for EILSEQ here */
}
char *buf = malloc(len + 1);
if (buf == NULL) {
/* err handling for malloc() failure here */
}
snprintf(buf, (size_t)len, "xmllint, --noout --schema %s %s", xsdName, xmlName);
system(buf);
free(buf);
On a sufficiently recent system, you could also use asprintf() to greatly simplify this code:
char *buf = NULL;
asprintf(&buf, "xmllint, --noout --schema %s %s", xsdName, xmlName);
if (buf == NULL) {
/* error handling here */
}
system(buf);
free(buf);
Note that all these approaches fail if xsdName or xmlName contain spaces or other special characters. You might want to invoke an exec function directly to avoid that problem.
You can use strcat() to concat strings.
void xsdValidation(char *xsdName, char *xmlName){
static const char *xmlLint = "xmllint --noout --schema ";
/* do not forget to +1 for terminating null character */
char *terminalCommand = malloc(strlen(xmlLint) + strlen(xsdName) + strlen(xmlName) + 1);
if(terminalCommand != NULL){
strcpy(terminalCommand, xmlLint); /* use strcpy() here to initialize the result buffer */
strcat(terminalCommand, xsdName);
strcat(terminalCommand, xmlName);
system(terminalCommand);
free(terminalCommand);
}
}
This may improve performance a little because it reuse the calculated length.
void xsdValidation(char *xsdName, char *xmlName){
static const char *xmlLint = "xmllint --noout --schema ";
size_t xmlLintLen = strlen(xmlLint);
size_t xsdNameLen = strlen(xsdName);
size_t xmlNameLen = strlen(xmlName);
/* do not forget to +1 for terminating null character */
char *terminalCommand = malloc(xmlLintLen + xsdNameLen + xmlNameLen + 1);
if(terminalCommand != NULL){
/* use strcpy() to copy the strings */
strcpy(terminalCommand, xmlLint);
strcpy(terminalCommand + xmlLintLen, xsdName);
strcpy(terminalCommand + xmlLintLen + xsdNameLen, xmlName);
system(terminalCommand);
free(terminalCommand);
}
}
If the compiler supports variable length arrays then you may write
void xsdValidation( const char *xsdName, const char *xmlName )
{
const char *command = "xmllint --noout --schema ";
char terminalCommand[strlen( command ) + strlen( xsdName ) + strlen( xmlName ) + 2];
strcpy( terminalCommand, command );
strcat( terminalCommand, xsdName );
strcat( terminalCommand, " " );
strcat( terminalCommand, xmlName );
system( terminalCommand );
}
Otherwise you should to allocate the required array dynamically. For example
void xsdValidation( const char *xsdName, const char *xmlName )
{
const char *command = "xmllint --noout --schema ";
char *terminalCommand =
malloc( strlen( command ) + strlen( xsdName ) + strlen( xmlName ) + 2 );
if ( terminalCommand != NULL )
{
strcpy( terminalCommand, command );
strcat( terminalCommand, xsdName );
strcat( terminalCommand, " " );
strcat( terminalCommand, xmlName );
system( terminalCommand );
free( terminalCommand );
}
}
Related
Say I wanted to duplicate a string then concatenate a value to it.
Using stl std::string, it's:
string s = "hello" ;
string s2 = s + " there" ; // effectively dup/cat
in C:
char* s = "hello" ;
char* s2 = strdup( s ) ;
strcat( s2, " there" ) ; // s2 is too short for this operation
The only way I know to do this in C is:
char* s = "hello" ;
char* s2=(char*)malloc( strlen(s) + strlen( " there" ) + 1 ) ; // allocate enough space
strcpy( s2, s ) ;
strcat( s2, " there" ) ;
Is there a more elegant way to do this in C?
You could make one:
char* strcat_copy(const char *str1, const char *str2) {
int str1_len, str2_len;
char *new_str;
/* null check */
str1_len = strlen(str1);
str2_len = strlen(str2);
new_str = malloc(str1_len + str2_len + 1);
/* null check */
memcpy(new_str, str1, str1_len);
memcpy(new_str + str1_len, str2, str2_len + 1);
return new_str;
}
A GNU extension is asprintf() that allocates the required buffer:
char* s2;
if (-1 != asprintf(&s2, "%s%s", "hello", "there")
{
free(s2);
}
Not really. C simply doesn't have a good string management framework like C++ does. Using malloc(), strcpy() and strcat() like you have shown is about as close as you can get to what you are asking for.
You could use a library like GLib and then use its string type:
GString * g_string_append (GString *string, const gchar *val);
Adds a string onto the end of a GString, expanding it if necessary.
Inspired by nightcracker, I also thought of
// writes s1 and s2 into a new string and returns it
char* catcpy( char* s1, char* s2 )
{
char* res = (char*)malloc( strlen(s1)+strlen(s2)+1 ) ;
// A:
sprintf( res, "%s%s", s1, s2 ) ;
return res ;
// OR B:
*res=0 ; // write the null terminator first
strcat( res, s1 ) ;
strcat( res, s2 ) ;
return res ;
}
I am trying to extract the name of a store from strings that are stored as char arrays using the language C. Each one contains the price of an item and the store it is located at. I have many strings that follow this format, but I have provided I few examples below:
199 at Amazon
139 at L.L.Bean
379.99 at Best Buy
345 at Nordstrom
How could I extract the name of the store from these strings?
Thank you in advance.
const char *sought = "at ";
char *pos = strstr(str, sought);
if(pos != NULL)
{
pos += strlen(sought);
// pos now points to the part of the string after "at";
}
else
{
// sought was not find in str
}
If you want to extract a portion after pos, but not the entire remaining string, you can use memcpy:
const char *sought = "o ";
char *str = "You have the right to remain silent";
char *pos = strstr(str, sought);
if(pos != NULL)
{
char word[7];
pos += strlen(sought);
memcpy(word, pos, 6);
word[6] = '\0';
// word now contains "remain\0"
}
As it was already pointed in comments you can use standard function strstr.
Here is a demonstrative program
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
char * extract_name( const char *record, const char *prefix )
{
size_t n = 0;
const char *pos = strstr( record, prefix );
if ( pos )
{
pos += strlen( prefix );
while ( isblank( ( unsigned char )*pos ) ) ++pos;
n = strlen( pos );
}
char *name = malloc( n + 1 );
if ( name )
{
if ( pos )
{
strcpy( name, pos );
}
else
{
*name = '\0';
}
}
return name;
}
int main(void)
{
const char *prefix = "at ";
char *name = extract_name( "199 at Amazon", prefix );
puts( name );
free( name );
name = extract_name( "139 at L.L.Bean", prefix );
puts( name );
free( name );
name = extract_name( "379.99 at Best Buy", prefix );
puts( name );
free( name );
name = extract_name( "345 at Nordstrom", prefix );
puts( name );
free( name );
return 0;
}
The program output is
Amazon
L.L.Bean
Best Buy
Nordstrom
The function extract_name creates dynamically a character array where it stores the extracted name. If the memory allocation is failed the function returns a null pointer. If the prefix before the name ( in this case it is the string "at ") is not found the function returns an empty string.
I have experienced an issue while using strcat, using realloc however, strcat overwrites destination string
char *splitStr(char *line) {
char *str_;
str_ = (char *) malloc(1);
char *ptr = strtok(line,"\n");
int a;
while (ptr != NULL) {
if (ptr[0] != '$') {
printf("oncesi %s\n", str_);
a = strlen(ptr) + strlen(str_) + 1;
str_ = realloc(str_, a);
strcat(str_, ptr);
str_[a] = '\0';
printf("sontasi:%s\n", str_);
}
ptr = strtok(NULL, "\n");
}
printf("splitStr %d\n", strlen(str_));
printf("%s", str_);
return str_;
}
and my input value is ;
*4
$3
200
$4
4814
$7
SUCCESS
$4
3204
so I want to split this input value via strtok;
strtok(line,'\n');
and concat all line without start "$" char to new char. However, this code give following output;
line: *4
oncesi
sontasi:*4
oncesi *4
200tasi:*4
200esi *4
4814asi:*4
4814si *4
SUCCESS:*4
SUCCESS*4
3204ESS:*4
splitStr 25
seems to overwrite source string.
do you have any idea why this issue could be happening ?
the following proposed code:
cleanly compiles
performs the indicated functionality
is slightly reformated for readability of output
checks for errors from malloc() and realloc()
shows how to initialize the str[] array, which is the problem in the OPs posted code.
the function: strlen() returns a size_t, not an int. so the proper output format conversion specifier is: %zu
does not use trailing underscores on variable names
and now, the proposed code:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char *splitStr( char *line )
{
printf("original line: %s\n", line);
char *str = malloc(1);
if( !str )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
str[0] = '\0'; // critical statement
char *token = strtok(line,"\n");
while( token )
{
if( token[0] != '$')
{
char* temp = realloc( str, strlen( token ) + strlen( str ) + 1 );
if( ! temp )
{
perror( "realloc failed" );
free( str );
exit( EXIT_FAILURE );
}
str = temp; // update pointer
strcat(str, token);
printf( "concat result: %s\n", str );
}
token = strtok(NULL, "\n");
}
printf("splitStr %zu\n", strlen(str));
return str;
}
int main( void )
{
char firstStr[] = "$abcd\n$defg\nhijk\n";
char *firstNewStr = splitStr( firstStr );
printf( "returned: %s\n\n\n\n", firstNewStr );
free( firstNewStr );
char secondStr[] = "abcd\ndefg\nhijk\n";
char *secondNewStr = splitStr( secondStr );
printf( "returned: %s\n\n\n\n", secondNewStr );
free( secondNewStr );
}
a run of the proposed code results in:
original line: $abcd
$defg
hijk
concat result: hijk
splitStr 4
returned: hijk
original line: abcd
defg
hijk
concat result: abcd
concat result: abcddefg
concat result: abcddefghijk
splitStr 12
returned: abcddefghijk
Your input contains Windows/DOS end-of-line codings "\r\n".
Since strtok() just replaces '\n' with '\0', the '\r' stays in the string. On output it moves the cursor to the left and additional characters overwrite old characters, at least visually.
Your concatenated string should be OK, however. Count the characters, and don't forget to include a '\r' for each line: "*4\r200\r4814\rSUCCESS\r3204\r" are 25 characters as the output splitStr 25 shows.
Additional notes:
As others already said, str_ = (char *) malloc(1); does not initialize the space str_ points to. You need to do this yourself, in example by str_[0] = '\0';.
Don't use underscores that way.
You don't need to cast the result of malloc(), it is a void* that is compatible to char* (and any other).
Hello and sorry if this questions is already answered (in the way I want to :-) ) but I think I have a memory problem.
Assuming the following C function (yes, this one is very dirty and not optimized):
char *strreplace(char **str, char *dst, char *replace) {
int replacestart = stringcompare(*str, dst), replaceend = strlen(dst), replacelen = strlen(replace);
char *tmp1 = (char *)malloc(sizeof(char) * (replacestart + 1)), *tmp2 = (char *)malloc(sizeof(char) * ((strlen(*str) - replaceend) + 1));
memset(tmp1, 0, sizeof(char) * (replacestart + 1));
memset(tmp2, 0, sizeof(char) * ((strlen(*str) - replaceend) + 1));
strncpy(tmp1, *str, replacestart);
strncpy(tmp2, *str + replacestart + replaceend, (strlen(*str) - (replaceend + replacestart)));
*str = realloc(*str, strlen(tmp1) + replacelen + strlen(tmp2));
memset(*str, 0, strlen(tmp1) + replacelen + strlen(tmp2));
strncpy(*str, tmp1, strlen(tmp1));
strncpy(*str + strlen(tmp1), replace, replacelen);
strncpy(*str + strlen(tmp1) + replacelen, tmp2, strlen(tmp2));
return *str;
}
As seen, it should replace *dst with *replace whithin **str.
This works basically as expected. Here's the problem:
The output (*str) is not cleard out with zeros after memsetting it and has still the wrong length, even after reallocating.
Means, if the string is longer after replacing, the last chars are cutted of *str.
In turn, if the string is shorter, old chars are at the end of the char string are found.
What did I wrong. Mentioned that I don't want to use sprintf and no C++-STL, just want to do this with pointers in C.
Other words, what would be the right way to replace a string in an c char string with pointers.
Thanks alot.
EDIT after reqeuest for more information
I use this function as following:
...open a textfile via FILE type, determining file lengts (fseek)...
char *filecontent = (char *)malloc(sizeof(char) * filesize);
if(filesize != fread(filecontent, sizeof(char), filesize, fd)) {
free(filecontent);
return -1;
}
streplace(&filecontent, "[#TOBEREPLACED#]", "replaced");
...doing other stuff with filecontent...
EDIT 2, adding stringcompare()
int stringcompare(const char *string, const char *substr) {
int i, j, firstOcc;
i = 0, j = 0;
while(string[i] != '\0') {
while(string[i] != substr[0] && string[i] != '\0') {
i++;
}
if(string[i] == '\0') {
return -1;
}
firstOcc = i;
while(string[i] == substr[j] && string[i] != '\0' && substr[j] != '\0') {
i++;
j++;
}
if(substr[j] == '\0') {
return firstOcc;
}
if(string[i] == '\0') {
return -1;
}
i = firstOcc + 1;
j = 0;
}
}
The logic of the function is not simple as it seems at the first glance.
You should decide what action to execute 1) if the source string is empty and 2) if the destination string is empty.
For example if the destination string is empty then the standard function strstr will return the address of the first character of the source string. However logically if dst is an empty string then the source string should not be changed except the case when it is in turn an empty string. In this case the function should just create a new string equal to the string replace.
Take into account that as the strings dst and replace are not changed within the function they should be declared with the qualifier const.
char * strreplace( char **str, const char *dst, const char *replace );
Also the function should report whether a new memory allocation was successful within the function by returning a null pointer in case when the allocation failed.
Taking all this into account the function can look the following way as it is shown in the demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * strreplace( char **str, const char *dst, const char *replace )
{
char *result = *str;
if ( *str[0] == '\0' )
{
if ( dst[0] == '\0' && replace[0] != '\0' )
{
char *result = malloc( strlen( replace ) + 1 );
if ( result )
{
strcpy( result, replace );
free( *str );
*str = result;
}
}
}
else if ( dst[0] != '\0' )
{
char *pos = strstr( *str, dst );
if ( pos != NULL )
{
size_t dst_n = strlen( dst );
size_t replace_n = strlen( replace );
result = ( char * )malloc( strlen( *str ) - dst_n + replace_n + 1 );
if ( result )
{
size_t n = pos - *str;
strncpy( result, *str, n );
strcpy( result + n, replace );
strcpy( result + n + replace_n, *str + n + dst_n );
}
free( *str );
*str = result;
}
}
return result;
}
int main(void)
{
const char *hello = "Hello everybody here";
char *str = malloc( sizeof( char ) );
str[0] = '\0';
if ( strreplace( &str, "", hello ) )
{
puts( str );
}
if ( strreplace( &str, "everybody", "Eurobertics" ) )
{
puts( str );
}
if ( strreplace( &str, "Hello", "Bye" ) )
{
puts( str );
}
if ( strreplace( &str, " here", "" ) )
{
puts( str );
}
if ( strreplace( &str, "Bye ", "" ) )
{
puts( str );
}
free( str );
return 0;
}
The program output is
Hello everybody here
Hello Eurobertics here
Bye Eurobertics here
Bye Eurobertics
Eurobertics
Your function looks overly complex. Here is a simple, working version:
char *strreplace(char **str, char *dst, char *replace) char *strreplace(char **str, char *dst, char *replace)
{
char *start, *tmp;
int n;
if ((start=strstr(*str,dst))==0) return(0);
n= (start-*str) + strlen(start+strlen(dst)) + strlen(replace) + 1;
tmp=malloc(n);
strncpy(tmp,*str,start-*str);
strcpy(tmp+(start-*str),replace);
strcat(tmp,start+strlen(dst));
free(*str);
*str= tmp;
return(tmp);
}
With test function:
void reptest(void)
{
char *src;
char rep[]= "brave new world";
src=malloc(strlen("Hello world of wonders")+1);
strcpy(src,"Hello world of wonders");
strreplace(&src,"world",rep);
printf("%s\n", src);
free(src);
}
Edit: my previous version forgot to copy the remainder. Fixed.
This is my code:
#define LEN 40
#define STUDLIST "./students.txt"
int main()
{
FILE * studd;
char del[] = "" " '\n'";
char name[LEN], surname[LEN], str[LEN];
char *ret;
char *tokens[2] = {NULL};
char *pToken = str;
unsigned int i = 0;
/* open file */
if ( (studd = fopen(STUDLIST,"r") ) == NULL )
{
fprintf(stderr, "fopen\n");
exit(EXIT_FAILURE);
}
while((ret = fgets(str, LEN, studd)))
{
if(ret)
{
for( tokens[i] = strtok_r( str, del, &pToken ); ++i < 2;
tokens[i] = strtok_r( NULL, del, &pToken ) );
strcpy(name, tokens[0]);
strcpy(surname, tokens[1]);
printf( "name = %s\n", name );
printf( "surname = %s\n", surname );
}
fflush(studd);
}
fclose(studd);
return 0;
}
Here there is the file students.txt: http://pastebin.com/wNpmXYis
I don't understand why the output isn't correct as I expected.
I use a loop to read each line with fgets, then I have a sting composed by [Name Surname], and I want to divide it in two different strings ([name] and [surname]) using strtok_r. I tried with a static string and it works well, but If I read many strings from FILE the output is not correct as you can see here:
http://pastebin.com/70uPMzPh
Where is my fault?
Why are you using forloop?
...
while((ret = fgets(str, LEN, studd)))
{
if(ret)
{
tokens[0] = strtok_r( str, del, &pToken );
tokens[1] = strtok_r( NULL, del, &pToken );
strcpy(name, tokens[0]);
strcpy(surname, tokens[1]);
printf( "name = %s\n", name );
printf( "surname = %s\n", surname );
}
}
You start i at zero:
unsigned int i = 0;
And later you increment it:
++i < 2;
You never set i back to zero, and in fact, continue incrementing i again for every new line in your file. With 14 names in your input file, I expect i to get to about 14.(or maybe 13 or 15, depending on the exact logic).
So this line:
tokens[i] = strtok_r(...);
ends up putting strtok results into tokens[2..15]. But only tokens[0] and tokens[1] are valid. Everything else is undefined behavior.
Answer: Be sure you reset i to zero when you read a new line of your file.