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

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.

Related

Writing concise code in C

strcat(msg, ": ");
strcat(msg, buf);
Is there a way to do this in one line? I want to make my code cleaner and reduce clutter
Instead of doing multiple concatenations, try creating a formatted string. Try something like this:
#include <stdio.h>
#include <string.h>
int main()
{
char *before_colon = "Text before colon";
char *after_colon = "Text after colon";
// Make a string that is the size of both formatted strings, plus a
// character for the space, colon, and null character.
char final_string[strlen(before_colon) + strlen(after_colon) + 3];
// This works just like any other C formatted function (i.e printf, scanf)
sprintf(final_string, "%s: %s", before_colon, after_colon);
printf("%s\n", final_string);
}
output:
Text before colon: Text after colon
Here's a modified code of Charlie Sale with its own function to count characters in the string. Thus, StrLen is called in array declaration.
#include <stdio.h>
#include <string.h>
int StrLen(char* PtrFirstChar)
{
int n = 0;
while(*(PtrFirstChar++)) // will evaluate to FALSE when '\0' reached
n++;
return n;
}
int main()
{
char *before_colon = "Text before colon";
char *after_colon = "Text after colon";
// Make a string that is the size of both formatted strings, plus a
// character for the space, colon, and null character.
char final_string[StrLen(before_colon) + StrLen(after_colon) + 3];
// This works just like any other C formatted function (i.e printf, scanf)
sprintf(final_string, "%s: %s", before_colon, after_colon);
printf("%s\n", final_string);
}
You could write your own variant of strcat!
I'm going to use strncat as a basis because strcat is a really bad idea:
#include <stddef.h> /* for size_t */
#include <stdarg.h> /* for va_* */
char *
mstrncat(char *d, size_t maxlen, ...)
{
va_list ap;
va_start(ap, maxlen);
char *ret = d;
/* Fast-forward */
for (; *d && maxlen; ++d, --maxlen);
/* Reserve a space for the terminator */
if (maxlen)
--maxlen;
const char *p;
/* Concatenate parameters one by one */
while (maxlen && (p = va_arg(ap, const char *))) {
while (*p && maxlen--)
*d++ = *p++;
}
/* Terminate the string */
*d = 0;
va_end(ap);
return ret;
}
You can use it like this:
#include <stdio.h>
int
main()
{
char test[128]="test";
mstrncat(test, sizeof(test), "1", "two", "3", NULL);
puts(test);
return 0;
}

Separating two values in a char and assigning them to varables

New to C and Arduino programming, my issue, how to split char c into two separate integer variables? char c's value looks like this 140,100, both numbers are integers. I need to create two integer variables, SpeedX and SpeedY from this data so that SpeedX = 140 and SpeedY = 100. Thanks for your help!
how to split char c into two separate integer variables?
First of all, what you have is a string, not a char.
You can use the strtok() function to split a string (and specify the delimiter to use), like this :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char nums[] = "140,100";
char *str;
int num;
int SpeedX, SpeedY, i = 0;
str = strtok (nums, ",");
while (str != NULL)
{
num = atoi(str);
if (i == 0)
SpeedX = num;
else if (i == 1)
SpeedY = num;
str = strtok (NULL, ",");
i++;
}
return 0;
}
NOTE : If, generally speaking about such a problem to solve in c and not in arduino, you do not know for sure that you will have only two elements in nums, you should use dynamic memory allocation (read more about it here), and modify your code like this :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char nums[] = "140,100,500";
char *str;
int num;
int len, i = 0;
int *p;
str = strtok (nums, ",");
len = strlen(str);
p = malloc((len-1)*sizeof(int));
while (str != NULL)
{
num = atoi(str);
*(p+i) = num;
i++;
str = strtok (NULL, ",");
}
for (i = 0; i < len-1; i++)
printf("%d\n", *(p+i));
return 0;
}
The char you are talking about is most likely string or char array, which is really a pointer to a number of chars. A char can only store 1 character.
The function strtol defined in stdlib.h takes three arguments:
A char pointer to the beginning of your string (or the place you want to start looking
A pointer to a char pointer that will be updated to point to the first char after the end of the number
The base the number is represented in, most likely 10 in your case
Your data type is probably char* or char[50], because char can only represent one character, not a sequence of characters like "140,100".
Anyway, use sscanf, which scans a string and extracts values according to a given format:
char *myNums = "140,100";
int first,second;
int elementsRead = sscanf(myNums,"%d,%d", &first, &second);
// note: elementsRead is `2` if both format specifiers `%d` and `%d` have been scanned successfully

Passing an array of pointers by reference

I'm trying to get commands from the keyboard in a similiar fashion as command line args int main( int argc, char *argv[] )but in a separate function. When I parse and print them within the scope of the getCmd() function all looks and behaves as intended, but as soon as they return to the main function they become a bunch of garbage. My questions are below the code.
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
void getCmd(char *cmd, char *args[])
{
char input[81] = { 0 };
char *next_token = NULL;
printf("$ ");
fgets(input, 81, stdin);
input[strcspn(input, "\n")] = 0;
cmd = strtok_s(input, " ", &next_token);
if (!strcmp(cmd, "mv"))
{
args[0] = strtok_s(NULL, " ", &next_token);
args[1] = strtok_s(NULL, " ", &next_token);
printf("\n\n%s\n%s\n%s\n\n", cmd, args[0], args[1]);
}
}
int main(void)
{
char *cmd = NULL, *args[5];
cmd = (char *)calloc(20,sizeof(char));
for (size_t i = 0; i < (size_t)5; i++)
{
args[i] = (char *)calloc(20,sizeof(char));
}
getCmd(cmd, args);
printf("\n\n%s \n%s\n%s", cmd, args[0], args[1]);
return 0;
}
I don't think its relevant but I'm using VS 2015 Community with the Visual C++ compiler on a 64 bit processor, Windows 7 OS.
My questions:
How should I pass the cmd and args[] by reference?
Are there any widely accepted idioms that deal with this sort of situations?
I've looked trough a few of the similiar questions and couldn't find a solution that works in this context, if the question is a duplicate, tell me and I'll close it.Since I'm new to stackoverflow any question formatting tips would be greatly appreciated. Cheers! (:
There are a number of different ways to approach the problem. While you are free to dynamically allocate memory for cmd and your args array, there is really no need, for this limited amount of memory you can use a static declaration for all. There is no need for a separate input array, you cause use cmd for that purpose and then tokenize cmd. This provides the benefit of leaving the first token nul-terminated in cmd after strtok is called.
note: in the example below, strtok is used, strtok_s was an optional compiler addition in C11, and unfortunately, I don't have a compiler that implements that option, so I test with strtok. You can easily make the change for VS.
#include <stdio.h>
#include <string.h>
enum { NARGS = 5, MAXC = 128 };
size_t getcmd (char *cmd, char (*args)[MAXC]);
int main (void) {
char cmd[MAXC] = "", args[NARGS][MAXC] = { "" };
size_t i, n;
if (!(n = getcmd (cmd, args))) return 1;
printf (" %s", cmd);
for (i = 0; i < n; i++)
printf(" %s", args[i]);
putchar ('\n');
return 0;
}
size_t getcmd (char *cmd, char (*args)[MAXC])
{
char *delim = " ,.\t\n";
char *p = NULL;
size_t idx = 0;
printf ("$ ");
if (!fgets (cmd, MAXC, stdin)) {
fprintf (stderr, "error: invalid input.\n");
return 0;
}
strtok (cmd, delim); /* terminate after 1st token */
for (p = strtok (NULL, delim); p; p = strtok (NULL, delim)) {
strncpy (args[idx++], p, MAXC); /* limit to avail chars */
if (idx == NARGS) break; /* limit to available bounds */
}
return idx;
}
Note above, the return type of getcmd is size_t. Always choose a meaningful type to return an indication of success/failure as well as returning some needed information (the number of arguments here). Also note the C-Style Guide disfavors camelCase variable/function names preferring all lower-case instead. Leave camelCase names for C++. See e.g. NASA - C Style Guide, 1994
Example Use/Output
$ ./bin/getcmd
$ mv /this/here/file /that/there/file
mv /this/here/file /that/there/file
$ ./bin/getcmd
$ mv -i --strip-trailing-slashes /this/here/file /that/there/file
mv -i --strip-trailing-slashes /this/here/file /that/there/file
Look it over and let me know if you have any additional questions.
strtok_s() return pointer into the buffer it's parsing (input here).
input lives on getCmd() stack. It dies the moment getCmd() returns. From then on the addresses pointing into input and that had been stored in args's elements does not point to valid memory any more.
The code needs allocate fresh memory and copy what strtok_s() returned a pointer to.
Have a look on ow this can be done:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getCmd(char **pcmd, char *args[], size_t s)
{
char input[81] = { 0 };
char *next_token = NULL;
printf("$ ");
fgets(input, 81, stdin);
input[strcspn(input, "\n")] = 0;
(*pcmd) = _strdup(strtok_s(input, " ", &next_token));
if (!strcmp(*pcmd, "mv"))
{
args[0] = _strdup(strtok_s(NULL, " ", &next_token));
args[1] = _strdup(strtok_s(NULL, " ", &next_token));
printf("\n\n%s\n%s\n%s\n\n", *pcmd, args[0], args[1]);
}
}
#define ARGS_MAX (5)
int main(void)
{
char *cmd, *args[ARGS_MAX] = {0};
getCmd(&cmd, args, ARGS_MAX);
printf("\n\n%s \n%s\n%s", cmd, args[0], args[1]);
/* Clean up. */
free(cmd);
for (size_t i = 0; i < ARGS_MAX; ++i)
{
free(args[i]);
}
return 0;
}

How to split char* into 3 char* at delimiter?

I want to split a char* time = "15:18:13"; into char* hour;, char* minute; and char* seconds;.
The problem is that I don't know how. I am like to try a Pebble Watchface. I already used char* hour = strok(time, ":"); but the first parameter needs to be a char[] but time is a char*.
Does anyone know how to do this?
Based on alk's comment, the approach one can use is sscanf, like this:
#include <string.h>
int main ()
{
char* str = "15:18:13";
int a, b, c;
sscanf(str, "%d:%d:%d", &a, &b, &c);
printf("%d %d %d\n", a, b, c);
return 0;
}
However, the following is a more general solution.
Use strtok.
You can store them in an array, like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
char str[] ="15:18:13";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str,":");
char* a[3];
int i = 0;
while (pch != NULL)
{
a[i] = malloc( (strlen(pch)+1) * sizeof(char));
strcpy(a[i++], pch);
printf ("%s\n",pch);
pch = strtok (NULL, ":");
}
for(i = 0 ; i < 3 ; i++)
printf ("%s\n",a[i]);
return 0;
}
strdup, as suggested by Deduplicator, can also help, but it is not standard, thus I suggest to avoid (or implement your own, not that hard). :)
Moreover, strtok_s that Deduplicator mentions is not provided in C.
Reply to OP's comment below:
Question: str is char str[] but time is a char*. Can I convert this?
You can assign it to an array like this:
#include <stdio.h>
int main ()
{
char* from = "15:18:13";
char to[strlen(from) + 1]; // do not forget +1 for the null character!
strcpy(to, from);
printf("%s\n", to);
return 0;
}
GIFT: I suggest you read the first answer from here.
It provides a smooth explanation to char* and char[].
Don't need those api. Pebble has own api for time out: strftime
tm *tmNext = localtime(&timeRequestTmp);
static char strformatForTimeNext[10];
strftime(strformatForTimeNext,10,"%H:%M",tmNext);
text_layer_set_text(layer_nextTime, strformatForTimeNext);

How do I concatenate two strings in C?

How do I add two strings?
I tried name = "derp" + "herp";, but I got an error:
Expression must have integral or enum type
C does not have the support for strings that some other languages have. A string in C is just a pointer to an array of char that is terminated by the first null character. There is no string concatenation operator in C.
Use strcat to concatenate two strings. You could use the following function to do it:
#include <stdlib.h>
#include <string.h>
char* concat(const char *s1, const char *s2)
{
char *result = malloc(strlen(s1) + strlen(s2) + 1); // +1 for the null-terminator
// in real code you would check for errors in malloc here
strcpy(result, s1);
strcat(result, s2);
return result;
}
This is not the fastest way to do this, but you shouldn't be worrying about that now. Note that the function returns a block of heap allocated memory to the caller and passes on ownership of that memory. It is the responsibility of the caller to free the memory when it is no longer needed.
Call the function like this:
char* s = concat("derp", "herp");
// do things with s
free(s); // deallocate the string
If you did happen to be bothered by performance then you would want to avoid repeatedly scanning the input buffers looking for the null-terminator.
char* concat(const char *s1, const char *s2)
{
const size_t len1 = strlen(s1);
const size_t len2 = strlen(s2);
char *result = malloc(len1 + len2 + 1); // +1 for the null-terminator
// in real code you would check for errors in malloc here
memcpy(result, s1, len1);
memcpy(result + len1, s2, len2 + 1); // +1 to copy the null-terminator
return result;
}
If you are planning to do a lot of work with strings then you may be better off using a different language that has first class support for strings.
#include <stdio.h>
int main(){
char name[] = "derp" "herp";
printf("\"%s\"\n", name);//"derpherp"
return 0;
}
David Heffernan explained the issue in his answer, and I wrote the improved code. See below.
A generic function
We can write a useful variadic function to concatenate any number of strings:
#include <stdlib.h> // calloc
#include <stdarg.h> // va_*
#include <string.h> // strlen, strcpy
char* concat(int count, ...)
{
va_list ap;
int i;
// Find required length to store merged string
int len = 1; // room for NULL
va_start(ap, count);
for(i=0 ; i<count ; i++)
len += strlen(va_arg(ap, char*));
va_end(ap);
// Allocate memory to concat strings
char *merged = calloc(sizeof(char),len);
int null_pos = 0;
// Actually concatenate strings
va_start(ap, count);
for(i=0 ; i<count ; i++)
{
char *s = va_arg(ap, char*);
strcpy(merged+null_pos, s);
null_pos += strlen(s);
}
va_end(ap);
return merged;
}
Usage
#include <stdio.h> // printf
void println(char *line)
{
printf("%s\n", line);
}
int main(int argc, char* argv[])
{
char *str;
str = concat(0); println(str); free(str);
str = concat(1,"a"); println(str); free(str);
str = concat(2,"a","b"); println(str); free(str);
str = concat(3,"a","b","c"); println(str); free(str);
return 0;
}
Output:
// Empty line
a
ab
abc
Clean-up
Note that you should free up the allocated memory when it becomes unneeded to avoid memory leaks:
char *str = concat(2,"a","b");
println(str);
free(str);
I'll assume you need it for one-off things. I'll assume you're a PC developer.
Use the Stack, Luke. Use it everywhere. Don't use malloc / free for small allocations, ever.
#include <string.h>
#include <stdio.h>
#define STR_SIZE 10000
int main()
{
char s1[] = "oppa";
char s2[] = "gangnam";
char s3[] = "style";
{
char result[STR_SIZE] = {0};
snprintf(result, sizeof(result), "%s %s %s", s1, s2, s3);
printf("%s\n", result);
}
}
If 10 KB per string won't be enough, add a zero to the size and don't bother, - they'll release their stack memory at the end of the scopes anyway.
You should use strcat, or better, strncat. Google it (the keyword is "concatenating").
You cannot add string literals like that in C. You have to create a buffer of size of string literal one + string literal two + a byte for null termination character and copy the corresponding literals to that buffer and also make sure that it is null terminated. Or you can use library functions like strcat.
Concatenate Strings
Concatenating any two strings in C can be done in atleast 3 ways :-
1) By copying string 2 to the end of string 1
#include <stdio.h>
#include <string.h>
#define MAX 100
int main()
{
char str1[MAX],str2[MAX];
int i,j=0;
printf("Input string 1: ");
gets(str1);
printf("\nInput string 2: ");
gets(str2);
for(i=strlen(str1);str2[j]!='\0';i++) //Copying string 2 to the end of string 1
{
str1[i]=str2[j];
j++;
}
str1[i]='\0';
printf("\nConcatenated string: ");
puts(str1);
return 0;
}
2) By copying string 1 and string 2 to string 3
#include <stdio.h>
#include <string.h>
#define MAX 100
int main()
{
char str1[MAX],str2[MAX],str3[MAX];
int i,j=0,count=0;
printf("Input string 1: ");
gets(str1);
printf("\nInput string 2: ");
gets(str2);
for(i=0;str1[i]!='\0';i++) //Copying string 1 to string 3
{
str3[i]=str1[i];
count++;
}
for(i=count;str2[j]!='\0';i++) //Copying string 2 to the end of string 3
{
str3[i]=str2[j];
j++;
}
str3[i]='\0';
printf("\nConcatenated string : ");
puts(str3);
return 0;
}
3) By using strcat() function
#include <stdio.h>
#include <string.h>
#define MAX 100
int main()
{
char str1[MAX],str2[MAX];
printf("Input string 1: ");
gets(str1);
printf("\nInput string 2: ");
gets(str2);
strcat(str1,str2); //strcat() function
printf("\nConcatenated string : ");
puts(str1);
return 0;
}
Without GNU extension:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
const char str1[] = "First";
const char str2[] = "Second";
char *res;
res = malloc(strlen(str1) + strlen(str2) + 1);
if (!res) {
fprintf(stderr, "malloc() failed: insufficient memory!\n");
return EXIT_FAILURE;
}
strcpy(res, str1);
strcat(res, str2);
printf("Result: '%s'\n", res);
free(res);
return EXIT_SUCCESS;
}
Alternatively with GNU extension:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
const char str1[] = "First";
const char str2[] = "Second";
char *res;
if (-1 == asprintf(&res, "%s%s", str1, str2)) {
fprintf(stderr, "asprintf() failed: insufficient memory!\n");
return EXIT_FAILURE;
}
printf("Result: '%s'\n", res);
free(res);
return EXIT_SUCCESS;
}
See malloc, free and asprintf for more details.
#include <string.h>
#include <stdio.h>
int main()
{
int a,l;
char str[50],str1[50],str3[100];
printf("\nEnter a string: ");
scanf("%s",str);
str3[0]='\0';
printf("\nEnter the string which you want to concat with string one: ");
scanf("%s",str1);
strcat(str3,str);
strcat(str3,str1);
printf("\nThe string is %s\n",str3);
}
using memcpy
char *str1="hello";
char *str2=" world";
char *str3;
str3=(char *) malloc (11 *sizeof(char));
memcpy(str3,str1,5);
memcpy(str3+strlen(str1),str2,6);
printf("%s + %s = %s",str1,str2,str3);
free(str3);
my here use asprintf
sample code:
char* fileTypeToStr(mode_t mode) {
char * fileStrBuf = NULL;
asprintf(&fileStrBuf, "%s", "");
bool isFifo = (bool)S_ISFIFO(mode);
if (isFifo){
asprintf(&fileStrBuf, "%s %s,", fileStrBuf, "FIFO");
}
...
bool isSocket = (bool)S_ISSOCK(mode);
if (isSocket){
asprintf(&fileStrBuf, "%s %s,", fileStrBuf, "Socket");
}
return fileStrBuf;
}
In C, you don't really have strings, as a generic first-class object. You have to manage them as arrays of characters, which mean that you have to determine how you would like to manage your arrays. One way is to normal variables, e.g. placed on the stack. Another way is to allocate them dynamically using malloc.
Once you have that sorted, you can copy the content of one array to another, to concatenate two strings using strcpy or strcat.
Having said that, C do have the concept of "string literals", which are strings known at compile time. When used, they will be a character array placed in read-only memory. It is, however, possible to concatenate two string literals by writing them next to each other, as in "foo" "bar", which will create the string literal "foobar".

Resources