Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I have an input string in the format: name1#float1 name2#float2 ... nameN#floatN. It's always delimited by '#' and ' '.
Example:
ash#19.96 ram#12.3 driver#10.2
It should display the name of the person having the smallest values.
How can it be solved?
Output:
driver
You can combone the strtok and sscanf functions to solve your problem.
For first, you have to tokenize your string using ' ' (white-space) as delimiter, then extract the number from each token to find the token with smallest number. Every time you find a token with smaller number than the current, extract the name from that token and store the new possibly smallest number. Here is an example:
#include <string.h>
#include <stdio.h>
#include <float.h>
int main() {
char str[] = "ash#19.96 ram#12.3 driver#10.2";
char result[256] = { 0 };
char *token, *sep_ptr;
float value, min = FLT_MAX; /* set 'min' to the maximum of float */
/* tokenize 'str' and walk through the tokens */
for(token = strtok(str, " "); token != NULL; token = strtok(NULL, " ")) {
value = FLT_MAX;
/* process the current token if it contains a '#' character */
if(sep_ptr = strchr(token, '#')) {
sscanf(sep_ptr, "#%f", &value); /* extract value */
/* check if the new number is smaller than current 'min' */
if(value < min) {
strcpy(result, (*sep_ptr = '\0', token)); /* extract name */
min = value;
}
}
}
puts(result);
return 0;
}
The (*sep_ptr = '\0', token) part of the code above simply replaces the '#' character to null character before performing the copy from token to result. (The mentioned expression uses the comma operator.)
The following code implements a getSubstrSmallestNumber() function which uses the strtok() function which changes the input buffer. If you don't want that you can first copy the string. The good point about changing the input buffer is that no memory allocation is needed for the found sub string. The strtok() writes a null terminator '\0' where the specified delimiter is found if it's called.
The getSubstrSmallestNumber() function can called with specific delimiters, here '#' and ' '. Each number will be converted to double with strtod() and checked if it smaller than before. If it's smaller the corresponding token will be saved. After the while loop finished (if strtok() found no more tokens) the saved token (smallest double) will be returned.
Note that the code does no error checking this should be considered to be implemented as well.
Full code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
char* getSubstrSmallestNumber(char* input, char* numberDelimiter, char* strDelimiter)
{
char* tokenStr;
double minNumber = DBL_MAX;
char* minToken = NULL;
for (tokenStr = strtok(input, numberDelimiter);
tokenStr != NULL;
tokenStr = strtok(NULL, numberDelimiter))
{
char* numberStr = strtok(NULL, strDelimiter);
double number = strtod(numberStr, NULL);
if (number < minNumber)
{
minNumber = number;
minToken = tokenStr;
}
}
return minToken;
}
int main()
{
char input[] = "ash#19.96 ram#12.3 driver#10.2";
printf("<%s>\n", getSubstrSmallestNumber(input, "#", " "));
return 0;
}
Output:
<driver>
I put '<' and '>' around the string in the printf() call to show that the returned string by getSubstrSmallestNumber() is really just driver and nothing more like a space for example.
You can use strrok available in C string library and for parsing float you can use atof function but the atof function seems to be unreliable in some cases, though. In that case you can write your own implementation according to your needs.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <float.h>
#define SIZE 1000
int main() {
char input[SIZE], smallest[SIZE], name[SIZE];
const char s[2] = "#";
char *token;
float val = FLT_MAX;
while(scanf("%s", input) != EOF) {
token = strtok(input, s);
strcpy(name, token);
token = strtok(NULL, s);
float curVal = atof(token);
if(curVal < val) {
val = curVal;
strcpy(smallest, name);
}
}
printf("Smallest value : %s\n", smallest);
return 0;
}
When i tested it seemed to show correct output:
~/Documents/src : $ ./a.out
ram#12.3
driver#10.2
ash#19.96
Smallest value : driver
Hope that helps!
Related
I'm having some trouble when parsing a text file. Each line of the text has a name followed after three float values. All of them are separated by a blankspace. What I want is to store the name in a string and the numbers in an array. I know I have to read each line using fgets and then strtok but the thing is I don't understand how strtok works. Do I have to call strtok four times? How do I assign each "piece" to my variables ?
Thank you for your time!
The strtok will search for the given tokens in a string. You must call it until the it returns NULL.
char *strtok(char *str, const char *delim)
The first call is done passing the string (char*) as the argument str and the remaining times are done passing NULL, as this will define that it should keep looking for the next token from that point onwards.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char line[] = "name 1.45 2.55 3.65";
char* name;
double values[3] = { 0 };
char* ptr = NULL;
int i = 0;
ptr = strtok(line, " "); // Search for the first whitespace
if (ptr != NULL) // Whitespace found
{
/* 'line' is now a string with all the text until the whitespace,
with terminating null character */
name = calloc(1, strlen(line));
strcpy(name, line);
while ((i < 3) && (ptr != NULL))
{
ptr = strtok(NULL, " "); // Call 'strtok' with NULL to continue until the next whitespace
if (ptr != NULL) // Whitespace found
{
/* 'ptr' has now the text from the previous token to the found whitespace,
with terminating null character */
values[i] = atof(ptr); // Convert to double
}
i++;
}
}
printf("%s %lf %lf %lf\n", name, values[0], values[1], values[2]);
}
Say I have char ch[] = "/user/dir1/file.txt";
I want to use a loop such that:
1st iteration:
prints: "user"
2nd iteration:
prints: "dir1"
3rd iteration:
prints: "file1.txt"
reach the end of string. Exists the loop
You have to use strtok or its threadsafe version if you are developing a multithreaded program:
#include<stdio.h>
#include <string.h>
int main() {
char ch[] = "/user/dir1/file.txt";
// Extract the first token
char * token = strtok(ch, "/");
// loop through the string to extract all other tokens
while( token != NULL ) {
printf( "%s\n", token ); //printing each token
token = strtok(NULL, " ");
}
return 0;
}
A "simple", portable, thread-safe solution that does not modify the string, as the approach using strtok() does. So the approach below can be applied to literals as well!
#include <stdio.h>
#include <string.h>
int main(void)
{
const char * s = "/user/dir1/file.txt";
for (const char * ps = s, *pe;
pe = strchr(ps, '/'), ps != pe ?printf("%.*s\n", (int) (pe - ps), ps) :0, pe;
ps = pe + 1);
}
The only limitation this code is facing, is that the tokens within the string to be parsed may not be longer then INT_MAX characters.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
My input string is 1,2,3:4,5,6:7,5,8
First I need to split 1, 2, 3 set wit : delimiter. Then again I need to split 1 2 3 with , delimiter. So I need to do outer split and inner split till the end of input string.. Please explain me with some example
As coderredoc says, strtok is the function you need.
#include <string.h>
char *strtok(char *str, const char *delim);
But strtok have some
quirks you have to remember:
Only in the first call you have to pass the source (str), subsequent
calls of strtok must be passed with NULL
Strtok modifies the source. Do not use unmodifiable strings or string
literal ("this is a string literal")
Because of the previous point, you should always do a copy of the source.
Simple example
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
const char *text = "Hello:World:!";
char *tmp = strdup(text);
if(tmp == NULL)
return 1; // no more memory
char *token = strtok(tmp, ":");
if(token == NULL)
{
free(tmp);
printf("No tokens\n");
return 1;
}
printf("Token: '%s'\n", token);
while(token = strtok(NULL, ":"))
printf("Token: '%s'\n", token);
free(tmp);
return 0;
}
Expected output
Token: 'Hello'
Token: 'World'
Token: '!'
Update
If you need to nest strtok, you should use strtok_r as mentioned before.
Here is an update of my example above. If I'm not mistaken, input will have
the same format as yours (more or less, mine has different set sizes, but same principle)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
const char *input ="v1,v2,v3,v4,v5:w1,w2,w3,w4,w5:x1,x2,x3,x4:y1,y2,y3,y4,y5,y6";
char *input_copy = strdup(input);
char *set_track, *elem_track; // keep track for strtok_r
char *set, *value;
char *_input = input_copy;
int setnum = 0;
while(set = strtok_r(_input, ":", &set_track))
{
_input = NULL; // for subsequent calls of strtok_r
printf("Set %d contains: ", ++setnum);
// in this case I don't care about strtok messing with the input
char *_set_input = set; // same trick as above
while(value = strtok_r(_set_input, ",", &elem_track))
{
_set_input = NULL; // for subsequent calls of strtok_r
printf("%s, ", value);
}
puts("");
}
free(input_copy);
return 0;
}
Expected output
Set 1 contains: v1, v2, v3, v4, v5,
Set 2 contains: w1, w2, w3, w4, w5,
Set 3 contains: x1, x2, x3, x4,
Set 4 contains: y1, y2, y3, y4, y5, y6,
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm trying to split up a string (typed in by the user at run time) into words (separated by spaces), and put each word into a different slot into an array. So, for example, if I took the string "hello world", array[0] would contain "hello" and array[1] would contain "world". And the last slot (in this case array[2]) would contain NULL. Here's what I have so far, which doesn't seem to be working properly. Any help would be appreciated. (By the way, this is part of a program which will call execvp(argv[0],argv); )
char input[100];
char* argv[20];
char* token;
scanf("%s", input);
//get the first token
token = strtok(input, " ");
int i=0;
//walk through other tokens
while( token != NULL ) {
argv[i] = token;
i++;
token = strtok(NULL, " ");
}
argv[i] = NULL; //argv ends with NULL
You need to allocate memory for each argv[i] and copy the current token to argv[i]:
token = strtok(input, " ");
int i=0;
//walk through other tokens
while( token != NULL ) {
argv[i] = malloc(strlen(token) + 1);
strncpy(argv[i], token, strlen(token));
//argv[i] = token;
i++;
token = strtok(NULL, " ");
}
argv[i] = NULL; //argv ends with NULL
I have created an example of what I think you want. I have used one malloc(3) for the whole
line of strings and another for the array of pointers you will get from the function.
Also, the second parameter of strtok(3) is passed to give more flexibility (the shell normally uses the contents of IFS environment variable to separate arguments so you can use the same algorithm as the shell does) I think you should use " \n\t" at least. It has a main() test function, so it's complete for your purpose.
#include <assert.h> /* man assert(3) */
#include <stdlib.h> /* malloc lives here */
#include <string.h> /* strtok, strdup lives here */
#include <stdio.h> /* printf lives here */
char **split(const char *str, const char *delim)
{
char *aux;
char *p;
char **res;
char *argv[200]; /* place for 200 words. */
int n = 0, i;
assert(aux = strdup(str));
for (p = strtok(aux, delim); p; p = strtok(NULL, delim))
argv[n++] = p;
argv[n++] = NULL;
/* i'll put de strdup()ed string one place past the NULL,
* so you can free(3), once finished */
argv[n++] = aux;
/* now, we need to copy the array, so we can use it outside
* this function. */
assert(res = calloc(n, sizeof (char *)));
for (i = 0; i < n; i++)
res[i] = argv[i];
return res;
} /* split */
int main()
{
char **argv =
split("Put each word of a string into array in C", " ");
int i;
for (i = 0; argv[i]; i++)
printf("[%s]", argv[i]);
puts(""); /* to end with a newline */
free(argv[i+1]);
free(argv);
} /* main */
The sample code just outputs:
$ pru
[Put][each][word][of][a][string][into][array][in][C]
I think I just figured out my problem: I need to use gets() instead of scanf(), because scanf() only gets the first word, up until a space, while I want to be able to get a string containing multiple words separated by spaces.
I need to delete all words that contain digits from the string.
E.g. if input is abdgh 67fgh 32ghj hj dfg43 11 fg, output should be abdgh hj fg.
I thought of using while( text[i] != ' '), but I don't know how to continue it for the rest of the string (after the first whitespace).
I don't have any other idea, and couldn't find anything by googling. Please, help me!
Here, i gave it a try. Works just fine for me. I tried to explain the logic throughout the code via comments. Hope it helps.
#include <stdio.h>
#include <string.h>
int containsNum(char * str);
int main()
{
char str[] = "abdgh 67fgh 32ghj hj dfg43 11 fg"; // input string
char newstr[100] = ""; //new string to create with filtered data
char * pch; //temp string to use in strtok
printf("given string : %s\n",str );
pch = strtok (str," ");
while (pch != NULL)
{
if(!containsNum(pch))// creation of new string with strcat
{ // if the current word not contains any number
strcat(newstr,pch);
strcat(newstr," "); //adding a space between words for readability
}
pch = strtok (NULL, " ");
}
printf("modified string : %s\n", newstr );
return 0;
}
//function containsNum
//gets a string and checks if it has any numbers in it
//returns 1 if so , 0 otherwise
int containsNum(char * str)
{
int i,
size =strlen(str),
flag=0;
for(i=0; i<size ; ++i)
{
if((int)str[i] >=48 && (int)str[i] <=57 ){
flag =1;
break;
}
}
return flag;
}
Regards
Algorithm:
1-You will have to break your input string into smaller components which are also called as tokens. For example: for the string abdgh 67fgh 32ghj hj dfg43 11 fg the tokens could be abdgh, 67fgh, 32ghj, hj, dfg43, 11 and fg.
2- These smaller strings or tokens can be formed using the strtok function which is defined as
char * strtok ( char * str, const char * delimiters );. Thestr in the first argument is the input sting which in the code presented below is string1. The second argument called the delimiters is what actually defines when to divide the input string into smaller pieces(tokens).
For instance, a whitespace as a delimiter will divide the input string whenever a whitespace is encountered, which is how the string is being divided in the code.
3-Since, your program needs to delete those words in the input string which contain digits we can use the isdigit() function to check exactly that.
WORKING CODE:
#include <cstring>
#include <ctype.h>
#include<stdio.h>
int main ()
{
char output[100]="";
int counter;
int check=0; /* An integer variable which takes the value of "1" whenever a digit
is encountered in one of the smaller strings or tokens.
So, whenever check is 1 for any of the tokens that token is to be ignored, that is,
not shown in the output string.*/
char string1[] = "abdgh 67fgh 32ghj hj dfg43 11 fg";
char delimiters[] = " ";//A whitespace character functions as a delimiter in the program
char * token;//Tokens are the sub-strings or the smaller strings which are part of the input string.
token=strtok(string1,delimiters);/*The first strktok call forms the first token/substring which for the input
given would be abdgh*/
while(token!=NULL)/*For the last substring(token) the strtok function call will return a NULL pointer, which
also indicates the last of the tokens(substrings) that can be formed for a given input string.
The while loop finishes when the NULL pointer is encountered.*/
{
for(counter=0;counter<=strlen(token)-1;counter++)/*This for loop iterates through each token element.
Example: In case of abdgh, it will first check for 'a',
then 'b', then 'd' and so on..*/
{
if(isdigit((int)token[counter])>0)/*This is to check if a digit has been encountered inside a token(substring).
If a digit is encountered we make check equal to 1 and break our loop, as
then that token is to be ignored and there is no real need to iterate
through the rest of the elements of the token*/
{
check=1;
break;
}
}
if(check==1) /* Outside the for loop, if check is equal to one that means we have to ignore that token and
it is not to be made a part of the output string. So we just concatenate(join) an
empty string ( represented by " " )with the output string*/
{
strcat(output,"");
check=0;
}
else /*If a token does not contain any digit we simply make it a part of the output string
by concatenating(joining) it with the output string. We also add a space for clarity.*/
{
strcat(output,token);
strcat(output," ");
}
token = strtok( NULL, delimiters ); /*This line of code forms a new token(substring) every time it is executed
inside the while loop*/
}
printf( "Output string is:: %s\n", output ); //Prints the final result
return 0;
}
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
char *filter(char *str){
char *p, *r;
p = r = str;
while(*r){
char *prefetch = r;
bool contain_digit = false;
while(!isspace(*prefetch) && *prefetch){
if(contain_digit)
++prefetch;
else if(isdigit(*prefetch++))
contain_digit = true;
}
if(contain_digit){
r = prefetch;
}else {
while(r < prefetch){
*p++ = *r++;
}
}
if(!*r)
break;
if(p[-1] == *r)
++r;
else
*p++ =*r++;
}
*p = '\0';
return str;
}
int main(void) {
char text[] = "abdgh 67fgh 32ghj hj dfg43 11 fg";
printf("%s\n", filter(text));//abdgh hj fg
return 0;
}