How to parse json using sscanf in C? - c

I have the following string in the form of json:
{"num":1,"data":{"city":"delhi"}}
I need to get the value of "num" key using sscanf. Here is my attempt. I know it's incorrect. But I don't know how to do it.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *str = "{\"num\":1,\"data\":{\"city\":\"delhi\"}}";
char *ret = malloc(sizeof(char) * 10);
sscanf(str, "{\"num\":%s, %s" , ret);
printf("%s", ret);
return 0;
}
Any suggestions?

sscanf(str, "{\"num\":%s, %s" , ret);
is wrong, first you have two "%s" but you give only one location to save string (ret), and it does not extract as you expect
you want
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *str = "{\"num\":1,\"data\":{\"city\":\"delhi\"}}";
char *ret = malloc(10); /* by definition sizeof(char) is 1 */
if (sscanf(str, "{\"num\":%9[^,]" , ret) == 1)
printf("value is '%s'\n", ret);
free(ret);
return 0;
}
Compilation and execution
/tmp % gcc -Wall p.c
/tmp % ./a.out
value is '1'
/tmp %
but to use scanf to parse is limited

Related

Can sscanf in C write on char* instead of char[]?

I'm looking for the simplest way in standard C to parse a string. The number of words inside the string is fixed, but the length of each single word is not. The code will be running on a microprocessor with limited memory so I can't just allocate an overkill buffer, I'd like to allocate just the memory that I need.
The following code works, but I'd like the single words to be char* . Is there some way around this?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char * my_words = "foo bar 1 2";
char word1[20];
char word2[20];
char word3[20];
char word4[20];
int match = sscanf(my_words,"%s %s %s %s",word1,word2,word3,word4);
printf("Matches: %d\r\n",match);
printf("%s\r\n",word1);
printf("%s\r\n",word2);
printf("%s\r\n",word3);
printf("%s\r\n",word4);
return 0;
}
Thank you
For parsing you can use strtok() function. A simple approach can be like that also you can modify it
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char const *my_words = "foo bar 1 2";
char *str = malloc(1 + strlen(my_words));
strcpy(str, my_words);
int countWord = 0;
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ");
++countWord;
}
printf("Total words = %d\n", countWord);
return 0;
}
The answer depends on how simple and standard your code should be.
If your target supports POSIX 2008 (recent GNU libc does), then you could use m modifier as docs suggest to allocate just enough space to read data.
But if you have to stay with ANSI C, then probably you're stuck to functions like strtok/strtok_r or alike.
And in the event you must roll your own, the algorithm goes like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
/* your input, I'll presume its constant */
const char *input = " foo bar 1 2 ";
/* here is your array of char*.
* you indicate the number of words is fixed */
char *words[4];
/* the algo */
size_t b = 0;
size_t e = 0;
size_t l = strlen(input);
int w = 0;
while (b < l) {
b += strspn(input + b, " ");
e = b + strcspn(input + b, " ");
words[w] = malloc(e - b + 1);
strncpy(words[w], input + b, e - b);
w++;
b = e+1;
}
/* debugging, outputs in reverse order */
while (w--) {
printf("%s\n", words[w]);
free(words[w]);
}
exit(EXIT_SUCCESS);
}
Obviously, you'd want to add error checking.

How to format and merge strings

I am trying to format and merge the strings (with sprintf) and then print them with printf. But it does not work, and I have no clue why.
The error is that string is not initialised.
int main() {
char wochentag[] = "Freitag";
int tag = 13;
char monat[] = "Mai";
int jahr = 1927;
char *string;
char *array=(char *) malloc(26*sizeof(char));
sprintf (string,"%s" "%d" "%s" "%d",wochentag,tag,monat,jahr);
printf("%s\n", string);
free(array);
return 0;
}
The following fixes achieve what you're trying to do:
char *array=(char *) malloc(26*sizeof(char));
A pointer to char is char*, not *char.
char *array=(char *) malloc(26*sizeof(char));
sprintf (array,"%s %d %s",wochentag,tag,monat);
printf("%s\n", array);
Since you allocate memory to your array variable, that's what you should use in sprintf and printf, right? Also note that the correct use of sprintf is with quotation marks.
This is the fixed code:
int main() {
char wochentag[] = "Freitag";
int tag = 13;
char monat[] = "Mai";
int jahr = 1927;
char *string;
char *array=(char *) malloc(26*sizeof(char));
sprintf (array,"%s %d %s",wochentag,tag,monat);
printf("%s\n", array);
free(array);
return 0;
}
the following code compiles cleanly, removes code clutter, performs error checking, includes the needed header files, is appropriately indented for readability and works correctly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char wochentag[] = "Freitag";
int tag = 13;
char monat[] = "Mai";
int jahr = 1927;
char *string=malloc(26);
if( NULL == string)
{ // then malloc failed
perror( "malloc for 26 bytes failed");
exit( EXIT_FAILURE);
}
// implied else, malloc successful
sprintf (string,"%s%d%s%d",wochentag,tag,monat,jahr);
printf("%s\n", string);
free(string);
return 0;
}

How to extract various integers from a string in C?

I was wondering how to extract various numbers from a string. I understand that strtol works, however it appears to only work for the first digit.
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
long v1, v2, v3;
char str[20] = "16,23";
char *d;
v1 = strtol(str, &d, 10);
v2 = strtol(str, &d, 10);
printf("string is %s\nv1 is:%i\nv2 is:%d\n",str , v1,v2);
return 0;
}
In this example I would like to output v1 = 16 and v2 = 23.
Another example, if the str was "12,23,34", I would like v3= 34
Thanks in advance :)
You can have many approaches. One of them is to make use of the endptr, populated by the previous strtol() call as the source of the next strtol().
Otherwise, for a better and flexible approach, you also have an option of using strtok() with a predefined delimiter (the , here) to get the tokens one by one and convert them to int or long (as you wish) until strtok() returns NULL.
Use long strtol(const char * nptr, char ** endptr, int base). The endptr allows for easy subsequent parsing as that is where parsing stopped.
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
int string_to_longs(const char *s) {
#define N 3
long v[N];
int i;
for (i=0; i<N; i++) {
errno = 0;
char *end;
v[i] = strtol(s, &end, 10);
if (errno) return -1; // overflow
if (s == end) return -1; // no conversion
printf("v[%d] = %ld\n", i, v[i]);
if (*end == 0) break; // we are done
if (*end != ',') return -1; // missing comma
s = (const char *) (end + 1);
}
return i;
}
int main(void) {
string_to_longs("16,23");
string_to_longs("12,23,34");
return 0;
}
strtol just converts a character array to a long int. It stops when it finds the first character that wouldn't make sense into interpreting an integer from.
There is a function in string.h named strtok which helps you tokenize a string.
Beware that strtok mutates the original character array contents.

Convert to ascii to hex without using print

So I want to be able to somehow change a string into hex like so: "ab.c2" --> "61622e6332". All the help I've found online shows how to do it by using print, but I don't want to use print because it doesn't store the hex value.
What I know so far is that if you take a char and cast it to an int you get the ascii value and with that I can get the hex, which is where I'm stumped.
Here's one way to do it, a complete program but the "meat" is in the tohex function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * tohex (unsigned char *s) {
size_t i, len = strlen (s) * 2;
// Allocate buffer for hex string result.
// Only output if allocation worked.
char *buff = malloc (len + 1);
if (buff != NULL) {
// Each char converted to hex digit string
// and put in correct place.
for (i = 0; i < len ; i += 2) {
sprintf (&(buff[i]), "%02x", *s++);
}
}
// Return allocated string (or NULL on failure).
return buff;
}
int main (void) {
char *input = "ab.c2";
char *hexbit = tohex (input);
printf ("[%s] -> [%s]\n", input, hexbit);
free (hexbit);
return 0;
}
There are of course other ways to achieve the same result, such as avoiding memory allocation if you can ensure you provide your own buffer that's big enough, something like:
#include <stdio.h>
void tohex (unsigned char *in, char *out) {
while (*in != '\0') {
sprintf (out, "%02x", *in++);
out += 2;
}
}
int main (void) {
char input[] = "ab.c2";
char output[sizeof(input) * 2 - 1];
tohex (input, output);
printf ("[%s] -> [%s]\n", input, output);
return 0;
}

How can I stop scanf-ing input after a certain character?

I'm working on a function that takes filepaths and dices them up into smaller sections.
For example, if the input parameter was "cd mypath/mystuff/stack/overflow/string", I want to be able to return "cd" "mypath", "mystuff", "stack", "overflow", and "string" in succession.
While I could simply continually use "getchar", appending the results to an ever-increasing string, stopping when getchar returns a '/', I feel like there must be a more elegant way to achieve the same functionality.
Any ideas?
You can use the char * strtok ( char * str, const char * delimiters ); using / as separator.
An example here:
http://www.cplusplus.com/reference/clibrary/cstring/strtok/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s path\n", argv[0]);
exit(EXIT_FAILURE);
}
char* saveptr = NULL;
for (char* str = argv[1]; ; str = NULL) {
char *token = strtok_r(str, "/", &saveptr);
if (token == NULL)
break;
printf("%s\n", token);
}
return 0;
}
Example
clang -Wall *.c && ./a.out mypath/mystuff/stack/overflow/string
mypath
mystuff
stack
overflow
string
Here's an example of how sscanf() can stop after a certain character.
sscanf("abcd/efgh", "%[^/]", &buf);
printf("%s\n", buf);
Should produce
abcd
EDIT: You could try something like this to advance sscanf() input. I have not tested this for various edge cases, but it should get the idea across.
char *str = "abcd/efgh/ijk/xyz";
while (sscanf(str, "%[^/]%n", &buf, &n)) {
printf("%s\n", buf);
str += n;
if (*str == '\0')
break;
++str;
}
should produce
abcd
efgh
ijk
xyz
Here is an example using regcomp, regexec. Compile and run it with the first arg being the character you are searching on, while the second arg is the string to search.
For example, a.out X abcXdefXghiXjkl will print abc def ghi jkl on separate lines.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <regex.h>
int
main(int argc, char *argv[]) {
int len;
char *cp;
char *token;
regex_t preg;
regmatch_t match;
if (regcomp(&preg, argv[1], REG_EXTENDED) != 0) {
return 0;
}
for (cp = argv[2]; *cp != '\0'; cp += len) {
len = (regexec(&preg, cp, 1, &match, 0) == 0) ? match.rm_eo : strlen(cp);
token = malloc(len);
strncpy(token, cp, len);
printf("%s\n", token);
}
return 0;
}

Resources