Extract string from JSON in C - c

I have a function which choose and extract a JSON line. This JSON line will be computed in another function which is supposed to extract the value of it.
"key" : "value",
I wish to extract value without using any library made to manipulate JSON.
My code looks like this so far :
char* extractValueInLine(char* line) {
if(!verifyLine(line)) // Checks wether line contains 4 quotes or not
return "";
int len = strlen( line );
char* res = NULL;
unsigned quoteCpt = 0;
for (unsigned i = 0; i < len; i++){
if (line[i] == '\"')
quoteCpt++;
if (quoteCpt == 3) // If 3 quotes has been skipped
{
res = malloc((char) sizeof(res) + sizeof(char)); // Adding memory for one char
if (NULL == res)
fprintf(stderr, "Not enough memory.\n");
else
{
char toAppend[2];
toAppend[1] = '\0';
toAppend[0] = line[i];
strcat(res, toAppend); // Append current char
}
}
return res;
}
This is very ugly and not efficient at all since the output is blank instead of being value.
Nevertheless I have tried more and more ways to do this, using string library functions and with sscanf, strtok and stuff but it was either not working or easily breakable.
A code which could work even if a blank space were added or missing would be perfect.
If anyone could advise me or tell me what I am missing here.

Use strchr() to find the position of the quotes. Then, with that information extract the value.
Something like
char *q1 = strchr(line, '\"'); /* needs error checking */
char *q2 = strchr(q1 + 1, '\"');
char *q3 = strchr(q2 + 1, '\"');
char *q4 = strchr(q3 + 1, '\"');
/* allocate (q4 - q3) bytes for res */
sprintf(res, "%.*s", (int)(q4 - q3 - 1), q3 + 1);

The issues with your code
Missing one } which makes your code return in the first iteration itself.
You are allocating new space every time. Which makes it lose the previous contents.
The fix would be
char* extractValueInLine(char* line) {
int len = strlen( line );
char* res = malloc(1);
res[0] = '\0';
unsigned quoteCpt = 0;
for (unsigned i = 0; i < len; i++){
if (line[i] == '\"')
quoteCpt++;
else if (quoteCpt == 3) {
res = realloc(res, strlen(res) + sizeof(char) + 1); // Free memory for additional char
if (NULL == res)
fprintf(stderr, "Not enough memory.\n");
else{
char toAppend[2];
toAppend[1] = '\0';
toAppend[0] = line[i];
strcat(res, toAppend);
}
}
}
return res;
}
The main change is that I have used realloc instead of malloc which copies the previous contents and frees the old buffer.
You can see the demo at Ideone

Related

reduce the size of a string

(disclaimer: this is not a complete exercise because I have to finish it, but error occurred in this part of code)
I did this exercise to practice memory allocation.
create a function that takes an url (a C string) and returns the name of the website (with "www." and with the extension).
for example, given wikipedia's link, "http://www.wikipedia.org/", it has to return only "www.wikipedia.org" in another string (dynamically allocated in the heap).
this is what I did so far:
do a for-loop, and when "i" is greater than 6, then start copying each character in another string until "/" is reached.
I need to allocate the other string, and then reallocate that.
here's my attempt so far:
char *read_website(const char *url) {
char *str = malloc(sizeof(char));
if (str == NULL) {
exit(1);
}
for (unsigned int i = 0; url[i] != "/" && i > 6; ++i) {
if (i <= 6) {
continue;
}
char* s = realloc(str, sizeof(char) + 1);
if (s == NULL) {
exit(1);
}
*str = *s;
}
return str;
}
int main(void) {
char s[] = "http://www.wikipedia.org/";
char *str = read_website(s);
return 0;
}
(1) by debugging line-by-line, I've noticed that the program ends once for-loop is reached.
(2) another thing: I've chosen to create another pointer when I've used realloc, because I have to check if there's memory leak. Is it a good practice? Or should I've done something else?
There are multiple problems in your code:
url[i] != "/" is incorrect, it is a type mismatch. You should compare the character url[i] with a character constant '/', not a string literal "/".
char *s = realloc(str, sizeof(char) + 1); reallocates only to size 2, not the current length plus 1.
you do not increase the pointers, neither do you use the index variable.
instead of using malloc and realloc, you should first compute the length of the server name and allocate the array with the correct size directly.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *read_website(const char *url) {
// skip the protocol part
if (!strncmp(url, "http://", 7))
url += 7;
else if (!strncmp(url, "https://", 8))
url += 8;
// compute the length of the domain name, stop at ':' or '/'
size_t n = strcspn(url, "/:");
// return an allocated copy of the substring
return strndup(url, n);
}
int main(void) {
char s[] = "http://www.wikipedia.org/";
char *str = read_website(s);
printf("%s -> %s\n", s, str);
free(str);
return 0;
}
strndup() is a POSIX function available on many systems and that will be part of the next version of the C Standard. If it is not available on your target, here is a simple implementation:
char *strndup(const char *s, size_t n) {
char *p;
size_t i;
for (i = 0; i < n && s[i]; i++)
continue;
p = malloc(i + 1);
if (p) {
memcpy(p, s, i);
p[i] = '\0';
}
return p;
}
The assignment doesn't say the returned string must be of minimal size, and the amount of memory used for URLs is minimal.
Building on chqrlie's solution, I'd start by finding the beginning of the domain name (skipping the protocol portion), duplicate the rest of the string, and then truncate the result. Roughly:
char *prot[] = { "http://", "https://" };
for( int i=0; i < 2; i++ ) {
if( 0 == strncmp(url, http, strlen(prot)) )
s += strlen(prot);
break;
}
}
char *output = strdup(s);
if( output ) {
size_t n = strcspn(output, "/:");
output[n] = '\0';
}
return output;
The returned pointer can still be freed by the caller, so the total "wasted" space is limited to the trailing part of the truncated URL.

Why does my string_split implementation not work?

My str_split function returns (or at least I think it does) a char** - so a list of strings essentially. It takes a string parameter, a char delimiter to split the string on, and a pointer to an int to place the number of strings detected.
The way I did it, which may be highly inefficient, is to make a buffer of x length (x = length of string), then copy element of string until we reach delimiter, or '\0' character. Then it copies the buffer to the char**, which is what we are returning (and has been malloced earlier, and can be freed from main()), then clears the buffer and repeats.
Although the algorithm may be iffy, the logic is definitely sound as my debug code (the _D) shows it's being copied correctly. The part I'm stuck on is when I make a char** in main, set it equal to my function. It doesn't return null, crash the program, or throw any errors, but it doesn't quite seem to work either. I'm assuming this is what is meant be the term Undefined Behavior.
Anyhow, after a lot of thinking (I'm new to all this) I tried something else, which you will see in the code, currently commented out. When I use malloc to copy the buffer to a new string, and pass that copy to aforementioned char**, it seems to work perfectly. HOWEVER, this creates an obvious memory leak as I can't free it later... so I'm lost.
When I did some research I found this post, which follows the idea of my code almost exactly and works, meaning there isn't an inherent problem with the format (return value, parameters, etc) of my str_split function. YET his only has 1 malloc, for the char**, and works just fine.
Below is my code. I've been trying to figure this out and it's scrambling my brain, so I'd really appreciate help!! Sorry in advance for the 'i', 'b', 'c' it's a bit convoluted I know.
Edit: should mention that with the following code,
ret[c] = buffer;
printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
it does indeed print correctly. It's only when I call the function from main that it gets weird. I'm guessing it's because it's out of scope ?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEBUG
#ifdef DEBUG
#define _D if (1)
#else
#define _D if (0)
#endif
char **str_split(char[], char, int*);
int count_char(char[], char);
int main(void) {
int num_strings = 0;
char **result = str_split("Helo_World_poopy_pants", '_', &num_strings);
if (result == NULL) {
printf("result is NULL\n");
return 0;
}
if (num_strings > 0) {
for (int i = 0; i < num_strings; i++) {
printf("\"%s\" \n", result[i]);
}
}
free(result);
return 0;
}
char **str_split(char string[], char delim, int *num_strings) {
int num_delim = count_char(string, delim);
*num_strings = num_delim + 1;
if (*num_strings < 2) {
return NULL;
}
//return value
char **ret = malloc((*num_strings) * sizeof(char*));
if (ret == NULL) {
_D printf("ret is null.\n");
return NULL;
}
int slen = strlen(string);
char buffer[slen];
/* b is the buffer index, c is the index for **ret */
int b = 0, c = 0;
for (int i = 0; i < slen + 1; i++) {
char cur = string[i];
if (cur == delim || cur == '\0') {
_D printf("Copying content of buffer to ret[%i]\n", c);
//char *tmp = malloc(sizeof(char) * slen + 1);
//strcpy(tmp, buffer);
//ret[c] = tmp;
ret[c] = buffer;
_D printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
//free(tmp);
c++;
b = 0;
continue;
}
//otherwise
_D printf("{%i} Copying char[%c] to index [%i] of buffer\n", c, cur, b);
buffer[b] = cur;
buffer[b+1] = '\0'; /* extend the null char */
b++;
_D printf("Buffer is now equal to: \"%s\"\n", buffer);
}
return ret;
}
int count_char(char base[], char c) {
int count = 0;
int i = 0;
while (base[i] != '\0') {
if (base[i++] == c) {
count++;
}
}
_D printf("Found %i occurence(s) of '%c'\n", count, c);
return count;
}
You are storing pointers to a buffer that exists on the stack. Using those pointers after returning from the function results in undefined behavior.
To get around this requires one of the following:
Allow the function to modify the input string (i.e. replace delimiters with null-terminator characters) and return pointers into it. The caller must be aware that this can happen. Note that supplying a string literal as you are doing here is illegal in C, so you would instead need to do:
char my_string[] = "Helo_World_poopy_pants";
char **result = str_split(my_string, '_', &num_strings);
In this case, the function should also make it clear that a string literal is not acceptable input, and define its first parameter as const char* string (instead of char string[]).
Allow the function to make a copy of the string and then modify the copy. You have expressed concerns about leaking this memory, but that concern is mostly to do with your program's design rather than a necessity.
It's perfectly valid to duplicate each string individually and then clean them all up later. The main issue is that it's inconvenient, and also slightly pointless.
Let's address the second point. You have several options, but if you insist that the result be easily cleaned-up with a call to free, then try this strategy:
When you allocate the pointer array, also make it large enough to hold a copy of the string:
// Allocate storage for `num_strings` pointers, plus a copy of the original string,
// then copy the string into memory immediately following the pointer storage.
char **ret = malloc((*num_strings) * sizeof(char*) + strlen(string) + 1);
char *buffer = (char*)&ret[*num_strings];
strcpy(buffer, string);
Now, do all your string operations on buffer. For example:
// Extract all delimited substrings. Here, buffer will always point at the
// current substring, and p will search for the delimiter. Once found,
// the substring is terminated, its pointer appended to the substring array,
// and then buffer is pointed at the next substring, if any.
int c = 0;
for(char *p = buffer; *buffer; ++p)
{
if (*p == delim || !*p) {
char *next = p;
if (*p) {
*p = '\0';
++next;
}
ret[c++] = buffer;
buffer = next;
}
}
When you need to clean up, it's just a single call to free, because everything was stored together.
The string pointers you store into the res with ret[c] = buffer; array point to an automatic array that goes out of scope when the function returns. The code subsequently has undefined behavior. You should allocate these strings with strdup().
Note also that it might not be appropriate to return NULL when the string does not contain a separator. Why not return an array with a single string?
Here is a simpler implementation:
#include <stdlib.h>
char **str_split(const char *string, char delim, int *num_strings) {
int i, n, from, to;
char **res;
for (n = 1, i = 0; string[i]; i++)
n += (string[i] == delim);
*num_strings = 0;
res = malloc(sizeof(*res) * n);
if (res == NULL)
return NULL;
for (i = from = to = 0;; from = to + 1) {
for (to = from; string[to] != delim && string[to] != '\0'; to++)
continue;
res[i] = malloc(to - from + 1);
if (res[i] == NULL) {
/* allocation failure: free memory allocated so far */
while (i > 0)
free(res[--i]);
free(res);
return NULL;
}
memcpy(res[i], string + from, to - from);
res[i][to - from] = '\0';
i++;
if (string[to] == '\0')
break;
}
*num_strings = n;
return res;
}

C: What's wrong with my reverse string function?

Been solving leetcode puzzles and thought I solved this one pretty quickly, yet I am running into a strange error. My output is matching the expected output, so I have no idea why it's rejecting my solution based off the following test case.
char* reverseString(char* s)
{
/* Sample input: "Hello"
Sample output: "olleh"
*/
char * reversed_string;
char temp[1];
int length = 0;
int i;
if(s == NULL)
return NULL;
length = strlen(s);
/* While string is not null, increment pointer */
while(*s != NULL)
{
s = s + 1;
}
/* Allocate reversed string based off length of original string */
reversed_string = malloc(length + 1);
/* Traverse backwards for length of string */
/* Copy each letter to temp */
/* Concatenate each letter to reversed_string */
for(i = 0; i < length; i++)
{
s = s - 1;
strncpy(temp, s, 1);
strcat(reversed_string, temp);
}
reversed_string[length] = '\0';
/* Return reversed string */
return reversed_string;
}
MOutput = My Output
EOutput = Expected Output
Input: "?CZU.9Iw8G3K?fse,b7 m;0?f :`c9d!D'`Pem0'Du0;9i` 03F,: 7,oPw'T'5`1g!iwR5J71iJ\"f;r6L;qZaDGx?cvkS 8\"UY2u`YC P3CM y`4v 1q7P;Zd1.;:RA!oYh;!2W8xMfMx8W2!;hYo!AR:;.1dZ;P7q1 v4`y MC3P CY`u2YU\"8 Skvc?xGDaZq;L6r;f\"Ji17J5Rwi!g1`5'T'wPo,7 :,F30 `i9;0uD'0meP`'D!d9c`: f?0;Z 7b,esf?K3G8wI9.UmC?"
MOutput: "?CmU.9Iw8G3K?fse,b7 Z;0?f :`c9d!D'`Pem0'Du0;9i` 03F,: 7,oPw'T'5`1g!iwR5J71iJ"f;r6L;qZaDGx?cvkS 8"UY2u`YC P3CM y`4v 1q7P;Zd1.;:RA!oYh;!2W8xMfMx8W2!;hYo!AR:;.1dZ;P7q1 v4`y MC3P CY`u2YU"8 Skvc?xGDaZq;L6r;f"Ji17J5Rwi!g1`5'T'wPo,7 :,F30 `i9;0uD'0meP`'D!d9c`: f?0;m 7b,esf?K3G8wI9.UZC?"
EOutput: "?CmU.9Iw8G3K?fse,b7 Z;0?f :`c9d!D'`Pem0'Du0;9i` 03F,: 7,oPw'T'5`1g!iwR5J71iJ"f;r6L;qZaDGx?cvkS 8"UY2u`YC P3CM y`4v 1q7P;Zd1.;:RA!oYh;!2W8xMfMx8W2!;hYo!AR:;.1dZ;P7q1 v4`y MC3P CY`u2YU"8 Skvc?xGDaZq;L6r;f"Ji17J5Rwi!g1`5'T'wPo,7 :,F30 `i9;0uD'0meP`'D!d9c`: f?0;m 7b,esf?K3G8wI9.UZC?"
Anyone spot what might be wrong with my function? Is there undefined behavior anywhere?
You need to allocate 2 chars for temp and initialize with 0's, so change it to
char temp[2] = { 0 };
Also, initialize reversed_string after allocating memory to it, so that first strcat works properly.
reversed_string = malloc(length + 1);
reversed_string[0] = '\0';
this part of the posted code:
for(i = 0; i < length; i++)
{
s = s - 1;
strncpy(temp, s, 1);
strcat(reversed_string, temp);
}
is not doing the right thing.
It should be copying one byte at a time from the end of the original string to the beginning of the reversed string.
Suggest the following code which:
cleanly compiles
performs the desired functionality
is a complete program
because it is going to be used in one of the online coding contests does not perform any error checking.
and now, the code
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// use meaningful variable and parameter names
char* reverseString(char* original)
{
size_t length = strlen( original );
/* Allocate reversed string based off length of original string */
char *reversed = NULL;
reversed = malloc(length + 1);
char *dest = reversed;
char *source = &(original[ strlen(original) -1 ]);
for( ; source >= original; source--)
{
*dest = *source;
dest++;
}
*dest = '\0';
/* Return reversed string */
return reversed;
} // end function: reverseString
int main( void )
{
char inBuffer[ 4096 ];
fgets( inBuffer, sizeof(inBuffer), stdin );
// eliminate trailing newline if it exists
char * newline = NULL;
if( NULL != (newline = strstr( inBuffer, "\n" ) ) )
{
*newline = '\0';
}
char * newString = reverseString( inBuffer );
printf( "%s\n\n", newString );
} // end function: main

Weird behaviour of C strcpy

I wrote a tiny program in C. It compile perfectly and works, when I compile it through my Makefile and clang, however, in Xcode this function behaves not like it should (or it behaves like it should and clang is ignoring it).
size_t getUrlForArgAndPlatform(char **dest, const char *arg, const char *platform) {
int isLinux = strcmp(platform, "Linux");
int isOSX = strcmp(platform, "Darwin");
char *platformUrlDelimiter = malloc(6 + 1);
if (isLinux == 0) {
strcpy(platformUrlDelimiter, "linux");
} else if (isOSX == 0) {
strcpy(platformUrlDelimiter, "osx");
} else {
strcpy(platformUrlDelimiter, "common");
}
int length = (int) strlen(kBaseUrl);
length += strlen(platformUrlDelimiter);
length += strlen(arg);
length += 5;
char *buffer = (char *) malloc(length);
if (buffer == NULL) {
exit(EXIT_FAILURE);
}
strcpy(buffer, kBaseUrl);
strcat(buffer, "/");
strcat(buffer, platformUrlDelimiter);
strcat(buffer, "/");
strcat(buffer, arg);
strcat(buffer, ".md");
*dest = malloc(strlen(buffer) + 1);
strcpy(*dest, buffer);
free(platformUrlDelimiter);
free(buffer);
return strlen(buffer) + 1;
}
It works 4 times out of 10. In the other 6 times, Xcode is telling me it's failing at strcpy(*dest, buffer) with a SIGBRT. If I take a look at the debugger, I see that buffer contains the same string twice. Why?
The size you have calculated for buffer is not quite correct:
int length = (int) strlen(kBaseUrl);
length += strlen(platformUrlDelimiter);
length += strlen(arg);
length += 5;
The last part should be '+6' because you need space for 2 times "/", for ".md" AND for the terminating NUL.
(The incorrect length has already been mentioned.)
There is a lot of pointless copying going on in your code. Since you don't modify platformUrlDelimiter, there is no need to copy it twice. This is much simpler:
const char *platformUrlDelimiter = "common";
if (isLinux == 0) {
platformUrlDelimiter = "linux";
} else if (isOSX == 0) {
platformUrlDelimiter = "osx";
}
And remove the call to free (platformUrlDelimiter).
Every call to strcat() (probably) has to needlessly loop through the buffer. I would write:
int length = strlen(kBaseUrl)
+ strlen(platformUrlDelimiter)
+ strlen(arg)
+ 6;
*dest = malloc(length);
if (*dest) {
sprintf (*dest, "%s/%s/%s.md", kBaseUrl, platformUrlDelimiter, arg);
} else {
/* Error */
}
return length;
Now there is no need to use buffer at all.
Also, there's a real bug right at the end of your code:
free(buffer);
return strlen(buffer) + 1;
You must not use the value of buffer in any way after it has been freed.

how to remove extension from file name?

I want to throw the last three character from file name and get the rest?
I have this code:
char* remove(char* mystr) {
char tmp[] = {0};
unsigned int x;
for (x = 0; x < (strlen(mystr) - 3); x++)
tmp[x] = mystr[x];
return tmp;
}
Try:
char *remove(char* myStr) {
char *retStr;
char *lastExt;
if (myStr == NULL) return NULL;
if ((retStr = malloc (strlen (myStr) + 1)) == NULL) return NULL;
strcpy (retStr, myStr);
lastExt = strrchr (retStr, '.');
if (lastExt != NULL)
*lastExt = '\0';
return retStr;
}
You'll have to free the returned string yourself. It simply finds the last . in the string and replaces it with a null terminator character. It will handle errors (passing NULL or running out of memory) by returning NULL.
It won't work with things like /this.path/is_bad since it will find the . in the non-file portion but you could handle this by also doing a strrchr of /, or whatever your path separator is, and ensuring it's position is NULL or before the . position.
A more general purpose solution to this problem could be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// remove_ext: removes the "extension" from a file spec.
// myStr is the string to process.
// extSep is the extension separator.
// pathSep is the path separator (0 means to ignore).
// Returns an allocated string identical to the original but
// with the extension removed. It must be freed when you're
// finished with it.
// If you pass in NULL or the new string can't be allocated,
// it returns NULL.
char *remove_ext (char* myStr, char extSep, char pathSep) {
char *retStr, *lastExt, *lastPath;
// Error checks and allocate string.
if (myStr == NULL) return NULL;
if ((retStr = malloc (strlen (myStr) + 1)) == NULL) return NULL;
// Make a copy and find the relevant characters.
strcpy (retStr, myStr);
lastExt = strrchr (retStr, extSep);
lastPath = (pathSep == 0) ? NULL : strrchr (retStr, pathSep);
// If it has an extension separator.
if (lastExt != NULL) {
// and it's to the right of the path separator.
if (lastPath != NULL) {
if (lastPath < lastExt) {
// then remove it.
*lastExt = '\0';
}
} else {
// Has extension separator with no path separator.
*lastExt = '\0';
}
}
// Return the modified string.
return retStr;
}
int main (int c, char *v[]) {
char *s;
printf ("[%s]\n", (s = remove_ext ("hello", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("hello.", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("hello.txt", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("hello.txt.txt", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("/has.dot/in.path", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', 0))); free (s);
return 0;
}
and this produces:
[hello]
[hello]
[hello]
[hello.txt]
[/no.dot/in_path]
[/has.dot/in]
[/no]
Use rindex to locate the "." character. If the string is writable, you can replace it with the string terminator char ('\0') and you're done.
char * rindex(const char *s, int c);
DESCRIPTION
The rindex() function locates the last character matching c (converted to a char) in the null-terminated string s.
If you literally just want to remove the last three characters, because you somehow know that your filename has an extension exactly three chars long (and you want to keep the dot):
char *remove_three(const char *filename) {
size_t len = strlen(filename);
char *newfilename = malloc(len-2);
if (!newfilename) /* handle error */;
memcpy(newfilename, filename, len-3);
newfilename[len - 3] = 0;
return newfilename;
}
Or let the caller provide the destination buffer (which they must ensure is long enough):
char *remove_three(char *dst, const char *filename) {
size_t len = strlen(filename);
memcpy(dst, filename, len-3);
dst[len - 3] = 0;
return dst;
}
If you want to generically remove a file extension, that's harder, and should normally use whatever filename-handling routines your platform provides (basename on POSIX, _wsplitpath_s on Windows) if there's any chance that you're dealing with a path rather than just the final part of the filename:
/* warning: may modify filename. To avoid this, take a copy first
dst may need to be longer than filename, for example currently
"file.txt" -> "./file.txt". For this reason it would be safer to
pass in a length with dst, and/or allow dst to be NULL in which
case return the length required */
void remove_extn(char *dst, char *filename) {
strcpy(dst, dirname(filename));
size_t len = strlen(dst);
dst[len] = '/';
dst += len+1;
strcpy(dst, basename(filename));
char *dot = strrchr(dst, '.');
/* retain the '.' To remove it do dot[0] = 0 */
if (dot) dot[1] = 0;
}
Come to think of it, you might want to pass dst+1 rather than dst to strrchr, since a filename starting with a dot maybe shouldn't be truncated to just ".". Depends what it's for.
I would try the following algorithm:
last_dot = -1
for each char in str:
if char = '.':
last_dot = index(char)
if last_dot != -1:
str[last_dot] = '\0'
Just replace the dot with "0". If you know that your extension is always 3 characters long you can just do:
char file[] = "test.png";
file[strlen(file) - 4] = 0;
puts(file);
This will output "test". Also, you shouldn't return a pointer to a local variable. The compiler will also warn you about this.
To get paxdiablo's second more general purpose solution to work in a C++ compiler I changed this line:
if ((retstr = malloc (strlen (mystr) + 1)) == NULL)
to:
if ((retstr = static_cast<char*>(malloc (strlen (mystr) + 1))) == NULL)
Hope this helps someone.
This should do the job:
char* remove(char* oldstr) {
int oldlen = 0;
while(oldstr[oldlen] != NULL){
++oldlen;
}
int newlen = oldlen - 1;
while(newlen > 0 && mystr[newlen] != '.'){
--newlen;
}
if (newlen == 0) {
newlen = oldlen;
}
char* newstr = new char[newlen];
for (int i = 0; i < newlen; ++i){
newstr[i] = oldstr[i];
}
return newstr;
}
Get location and just copy up to that location into a new char *.
i = 0;
n = 0;
while(argv[1][i] != '\0') { // get length of filename
i++; }
for(ii = 0; i > -1; i--) { // look for extension working backwards
if(argv[1][i] == '.') {
n = i; // char # of exension
break; } }
memcpy(new_filename, argv[1], n);
This is simple way to change extension name.
....
char outputname[255]
sscanf(inputname,"%[^.]",outputname); // foo.bar => foo
sprintf(outputname,"%s.txt",outputname) // foo.txt <= foo
....
With configurable minimum file length and configurable maximum extension length. Returns index where extension was changed to null character, or -1 if no extension was found.
int32_t strip_extension(char *in_str)
{
static const uint8_t name_min_len = 1;
static const uint8_t max_ext_len = 4;
/* Check chars starting at end of string to find last '.' */
for (ssize_t i = sizeof(in_str); i > (name_min_len + max_ext_len); i--)
{
if (in_str[i] == '.')
{
in_str[i] = '\0';
return i;
}
}
return -1;
}
I use this code:
void remove_extension(char* s) {
char* dot = 0;
while (*s) {
if (*s == '.') dot = s; // last dot
else if (*s == '/' || *s == '\\') dot = 0; // ignore dots before path separators
s++;
}
if (dot) *dot = '\0';
}
It handles the Windows path convention correctly (both / and \ can be path separators).

Resources