What is the best way to compare multiple strings in C? [closed] - c

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 4 years ago.
Improve this question
I'm trying to find the best way to compare multiple strings in C.
Currently, I'm using strcmp(); function, but it's turning out to be too many if
statements. I was also using ternary operator but unfortunately for me, it doesn't help.
Is there any better solution?
Here is example code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char command[] = "First Second Third";
char * del = " ";
char * token;
char * strings[3];
int n = 0;
token = strtok(command, del);
while (token != NULL){
strings[n] = token;
token = strtok(NULL, del);
n++;
}
// Now, strings[0] = "First", strings[1] = "Second", and strings[2] = "Third"
// Only examine strings[1] and strings[2] after we know strings[0] = "First".
if (strcmp("First", strings[0]) == 0){
//do something
if (strcmp("Second", strings[1]) == 0){
//do something
if (strcmp("Third", strings[2]) == 0){
//do something
printf("CORRECT");
//do something
}
}
}
return 0;
}

OP's code has some problems
while (token != NULL) has no limit to 3 loops. Code may attempt strings[3] = token;
// while (token != NULL){
while (n < 3 && token != NULL){
Code uses strings[0], strings[1], strings[2] without first insuring that many tokens were parsed.
// strcmp("First", strings[0]) == 0
(n > 0 && strcmp("First", strings[0]) == 0)
Code saves a pointer to the original string. Once strtok() is call again, the prior token can be lost/changed.
The "best" way involves hashing the key and targets, yet that is a lot to explain here.
Alternative: With such a simple match needed as in OP's example, code could use "%n" to record the offset of the scan.
int n1 = 0, n2 = 0, n3 = 0;
sscanf(command, "First %n Second %n Third %n", &n1, &n2, &n3);
if (n1) { // n1 is non-zero if scan matched "First"
//do something
if (n2) { // n2 is non-zero if scan matched "First Second"
//do something
if (n3) {
//do something
printf("CORRECT");
//do something
}
}
}

It looks like your if statements should be using strcmp() instead of the posted strtok(). Also, I think you are looking for something more like if ... else if ... else if ..., rather than nested if's.
Depending on the real program you're writing (vs. posted), you could also use a switch() based on the first character in the string to compare. It amounts to a crude hashing function. (you may want to learn more about hashing to adapt what you have to what you want).
EDIT:
Maybe this is what you're getting at:
.
.
.
if (strcmp(strings[0], "cmdA") == 0)
processCmdA(strings[1], strings[2]);
else
if (strcmp(strings[0], "cmdB") == 0)
processCmdB(strings[1], strings[2]);
else
if (strcmp(strings[0], "cmdC") == 0)
processCmdC(strings[1], strings[2]);
.
.
.
void processCmdA(char* optionA, char* inputFileName)
{
if (strcmp(optionA, "parse") == 0)
parseFile(inputFileName);
else
if (strcmp(optionA, "cksum") == 0)
computeCheckSum(inputFileName);
else
.
.
.
}
void parseFile(char* inputFileName)
{
// do parse stuff
}
.
.
.

Related

SOLVED-what am I doing wrong that strtok does right in splitting a string

Previous question was : what am I doing wrong that strtok does right in splitting a string. Also separating the strtok to a function suddenly doesn't produce correct result?
This is the first time that I ask a question in stackoverflow so forgive me if this is wordy and incoherent. The last part of the question is elaborated at the bottom part of this question body.
So, I was doing a course assessment assigned by my college, in that, one question is :
Remove duplicate words and print only unique words
Input : A single sentence in which each word separated by a space
Output : Unique words separated by a space [Order of words should be same as in input]
Example:
Input : adc aftrr adc art
Output : adc aftrr art
Now, I have the solution which is to split the string on whitespaces and adding the word to a array(set) if it is not already exists, but it is the implementation part that makes me to plug my hair out
#include <stdio.h>
#include <string.h>
#define MAX 20
int exists(char words[][MAX], int n, char *word){ // The existence check function
for(int i=0;i < n;i++)
if(strcmp(words[i],word) == 0)
return 1;
return 0;
}
void removeDuplicateOld(char*);
void removeDuplicateNew(char*);
int main(){
char sentence[MAX*50] = {0}; //arbitary length
fgets(sentence,MAX*50,stdin);
sentence[strcspn(sentence,"\n")]=0;
printf("The Old One : \n");
removeDuplicateOld(sentence);
printf("\nThe New One : \n");
removeDuplicateNew(sentence);
}
The fucntion that uses strtok to split string :
void removeDuplicateNew(char *sentence){
char words[10][MAX] = {0};
int wi=0;
char *token = strtok(sentence," ");
while(token != NULL){
if(exists(words,wi,token)==0) {
strcpy(words[wi++],token);
}
token = strtok(NULL," ");
}
for(int i=0;i<wi;i++) printf("%s ",words[i]);
}
The old function that uses my old method (which is constructing a word until I hit whitespace) :
void removeDuplicateOld(char *sentence){
char objects[10][MAX] = {0}; //10 words with MAX letters
char tword[MAX];
int oi=0, si=0, ti=0;
while(sentence[si]!='\0'){
if(sentence[si] != ' ' && sentence[si+1] != '\0')
tword[ti++] = sentence[si];
else{
if(sentence[si+1] == '\0')
tword[ti++]=sentence[si];
tword[ti]='\0';
if(exists(objects,oi,tword) == 0){
strcpy(objects[oi++],tword);
}
ti=0; // The buffer[tword] is made to be overwritten
}
si++;
}
for(int i=0;i<oi;i++)
printf("%s ",objects[i]);
}
Solved : changed if(sentence[si+1] == '\0') to if(sentence[si+1] == '\0' && sentence[si]!=' ')
Here is the output :
input : abc def ghi abc jkl ghi
The Old One :
abc def ghi jkl
The New One :
abc def ghi jkl
Note trailing whitespaces in input and output is not checked as their own driver code doesn't properly handle them while strtok method does and it passes all tests.
Now both methods seems to be producing same results but they are indeed producing different outputs according to test cases and in top of that separating strtok method as a separate function[removeDuplicateNew] fails one test case while writing it in main method itself passes all test, see these results :
Old Method Test Case results
Strtok Method as Separate Function Test Case Results
Following Was Moved To A separate Question Thread
When Coded in main method itself :
int main(){
char sentence[MAX*50] = {0}; //arbitary length
fgets(sentence,MAX*50,stdin);
sentence[strcspn(sentence,"\n")] = 0;
char words[10][MAX] = {0};
int wi=0;
char *token = strtok(sentence," ");
while(token != NULL){
if(exists(words,wi,token)==0) {
strcpy(words[wi++],token);
}
token = strtok(NULL," ");
}
for(int i=0;i<wi;i++) printf("%s ",words[i]);
}
Strtok Method as inline code Test Case Results
For the record, it is the same code just placed in main method, so what the heck happens here that when I separate it as a function and pass the string as argument it suddenly isn't working properly.
Also any advice on my question building, wording is appreciated.
Your code...
void removeDuplicateOld(char *sentence){
char objects[10][MAX] = {0}; //10 words with MAX letters
char tword[MAX];
int oi=0, si=0, ti=0;
while(sentence[si]!='\0'){
if(sentence[si] != ' ' && sentence[si+1] != '\0')
tword[ti++] = sentence[si];
else{
// right here have hit SP.
// if SP followed by '\0'
// then append SP to my word... wrong! <=====
if(sentence[si+1] == '\0')
tword[ti++]=sentence[si];
tword[ti]='\0';
This is why the library function strtok() works better than hand rolled code.It has been tested and proven to work as it says it does.
There's a better way to use strtok()
for( char *p = sentence; (p = strtok( p, " \n") ) != NULL; p = NULL )
if( exists( words, wi, p ) == 0 )
strcpy( words[wi++], p );
That's all you need. strtok() will even trim the LF off the buffer for you, no extra charge.
Final suggestion: Instead of a fixed-sized array of pointers to words, you might consider a linked-list (LL) that can easily grow. The function that would append a new word to the end of the list can quietly eat the word if it turns out to be a duplicate found while traversing to append to the end of the LL.

How to properly empty a string (array of chars) in C? [duplicate]

This question already has answers here:
Proper way to empty a C-String
(7 answers)
Closed 3 years ago.
I used a string called "comando" as the input. I copy the first word of "comando" into "comandoParz" and I use "comandoParz" as the parameter to call a specific function.
The first call works fine, but the second one gives the same output as the first one.
Maybe it's because I need to empty the array "comando" and "comandParz" but I tried a few solutions and none of them seemed to work.
I'll include the full code below.
(Sorry if I added too much code; I'm still trying to figure out the best way to post here.)
I tried adding strcpy(comandoParz, "") and strcpy(comando, "") (they are not active in the code I posted below) and the first input works but the other ones don't give any output.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void addrel() {
printf("\nAGGIUNGI RELAZIONE:\n\n");
}
void delrel() {
printf("\nELIMINA RELAZIONE:\n\n");
}
void addent() {
printf("\nAGGIUNGI UTENTE:\n\n");
}
void delent() {
printf("\nELIMINA UTENTE:\n\n");
}
int main() {
int i = 0;
char comando[100];
char comandoParz[10];
START:
/*strcpy(comando, "");
strcpy(comandoParz, "");*/
printf("Input: ");
fgets(comando, sizeof(comando), stdin);
while(comando[i] != '\0') {
comandoParz[i] = comando[i];
i++;
}
i++;
if(strcmp(comandoParz, "delent\n") == 0) {
delent();
} else {
if(strcmp(comandoParz, "addent\n") == 0) {
addent();
} else {
if(strcmp(comandoParz, "addrel\n") == 0) {
addrel();
} else {
if(strcmp(comandoParz, "delrel\n") == 0) {
delrel();
}
}
}
}
goto START;
}
For example, the first input may be "addrel" and the output will be "AGGIUNGI RELAZIONE:". The second input may be "delent" and the answer should be "ELIMINA UTENTE:" but it will be "AGGIUNGI RELAZIONE" as the first call.
The code needs to re-initialise i to 0 within the START-loop.
A C-string need to be terminated with a 0.
The copy-loop needs to take care to not overwrite the destination array when copying.
Taking all of the above into account this
while(comando[i] != '\0') {
comandoParz[i] = comando[i];
i++;
}
i++;
could look like:
i = 0;
while (comando[i] != '\0'
&& i < (sizeof comandoParz -1)) /* one less for the '\0'-terminator */
{
comandoParz[i] = comando[i];
i++;
}
comandoParz[i] = '\0'; /* place the '\0'-terminator */
To answer you question
how to properly empy a string
You could memset it with '\0':
memset(comandoParz, '\0', sizeof comandoParz);
Although for all methods expecting a C-string putting a '\0' into the array element where you want the string to end would do.
So to "empty" it just do, placing a '\0' in its 1st element:
comandoParz[0] = '\0';

How to replace a multi-character character constant with one character [closed]

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 3 years ago.
Improve this question
I'm attempting to replace '%7C' with a '|' in C but i'm getting a multi-character character constant warning. I was wondering if it was possible to do this and if so how? I was attempting using the code below but it gave this warning.
Parse.c
char *parse(char *command){
char * newCommand = (char *)malloc(sizeof(char)*35);
newCommand = strtok(command, " ");
newCommand = strtok(NULL, "/run?command= ");
for(int i = 0; i<strlen(newCommand); i++){
if(newCommand[i] == '+')
{
newCommand[i] = ' ';
}
if(newCommand[i] == '%7C')
{
newCommand[i] = '|';
}
}
return newCommand;
}
Multi-character constants are not portable and should generally be avoided. Your code comes under the 'general' category.
Part of the solution to your problem is to do a string comparison (with strncmp):
if (strncmp(&newCommand[i], "%7C", 3) == 0)
{
newCommand[i] = '|';
}
However, you also need to remove the 7C. That requires more surgery on the loop:
int tgt = 0;
int len = strlen(newCommand);
for (int src = 0; src < len; src++)
{
if (newCommand[src] == '+')
{
newCommand[tgt++] = ' ';
}
else if (strncmp(newCommand[i], "%7C", 3) == 0)
{
newCommand[tgt++] = '|';
src += 2;
}
else
newCommand[tgt++] = newCommand[src];
}
newCommand[tgt] = '\0';
This maintains two indexes into the newCommand array, one from which you're reading (src) and one to which you're writing (tgt — dst would be an alternative name). The src += 2; skips over the 7C after replacing % with |.
Uncompiled code!
Also, in your function you have:
char *newCommand = (char *)malloc(sizeof(char)*35);
newCommand = strtok(command, " ");
This immediately leaks the allocated memory. Maybe you need to use strdup() or:
char *newCommand = malloc(strlen(command) + 1);
if (newCommand == NULL) …report error and bail out…
strcpy(newCommand, command);
And the next line:
newCommand = strtok(NULL, "/run?command= ");
splits on any sequence of any of the characters in the constant string; it does not look for that string. If you want to look for the string, then you need strstr() instead, and you need to run strtok() first, perhaps, to get the right starting point (maybe newCommand = strtok(NULL, ""), then char *end = strstr(newCommand, "/run?command= "); — and check for null pointers returned.
With the revised allocation, you need a new symbol to record the pointers returned by strtok() — such as char *token;.
All in all, there's a lot of work needed on your code.

C - Can not use strcpy with strtok

I am working with a function that accept full name as input and print them out in order: first name, middle name, last name.
The problem is my while loop seem to go infinite and i haven't figured out how to fix it.
Here is my function:
void order(char ar[])
{
int i, count=0, a=1;
char* token;
char last[20], middle[20], first[20],c;
char s[]=" ";
for(i=0;i<strlen(ar)-1;i++)
if(ar[i]==' ') count++;
token=strtok(ar,s);
strcpy(last,token);
while(token!=NULL)
{
token=strtok(NULL,s);
if(a<count) strcpy(middle,token);
else strcpy(first,token);
a++;
}
printf("%s %s %s",first,middle,last);
}
When first is copied (i.e. all tokens identified), while loop will be entered once again(note that token is not NULL, but points to first in this case). Now strtok() will be called and it will return NULL. Code will go to the else part and try to strcpy(), which is causing the problem.
Check for NULL before strcpy(). I have done this like this and it works fine.
while(token!=NULL)
{
token=strtok(NULL,s);
if (token == NULL)
break;
if(a<count) strcpy(middle,token);
else strcpy(first,token);
a++;
}
This is more like how I'd present the code in the question — before I passed it through a compiler:
void order(char ar[])
{
int count = 0, a = 1;
char *token;
char last[20], middle[20], first[20];
char s[] = " ";
for (int i = 0; i < strlen(ar) - 1; i++)
{
if (ar[i] == ' ')
count++;
}
token = strtok(ar, s);
strcpy(last, token);
while (token != NULL)
{
token = strtok(NULL, s);
if (a < count)
strcpy(middle, token);
else
strcpy(first, token);
a++;
}
printf("%s %s %s", first, middle, last);
}
You can argue about brace placement — I use Allman but 1TBS is a widely used alternative style. I recommend choosing one of those two. I put braces around the body of a loop if it is more than one line; YMMV.
Use more white space around operators. Don't define unused variables (c vanished). I placed int i in the for loop. I would probably define one variable per line, and I probably wouldn't use the count or a.
The call to strlen() should not be in the condition of the loop. I'm not sure how you'd cope with my name — I don't have a middle name. I think you could avoid the while loop altogether. I'm not convinced you need the for loop either. Those are a separate discussion.
I'm not attempting to fix the algorithmic problem — this is not an answer to the question in the question (so it shouldn't be accepted), but is an answer to the question in the comment, and since code cannot be formatted in comments, I can't answer it sanely in a comment; hence, this 'not an answer' but 'intended to be helpful to an auxilliary question from the OP'.

C Multiple Strtok to determine delimiter

I'm writing a C program where the user can input a string of 1-3 digits followed by a backslash and then another 1-3 digits or they can enter 1-3 digits, followed by a comma, then another 1-3 digits and there is no limit to how many times they can iterate this.
I need to determine whether the input delimiter is a backslash or comma (to determine what to do with the numbers) and put the numbers into an array.
The way I was thinking of doing this was to using strtok as follows. The string is inputted as char *token.
op_tok1 = strtok(token, "\\");
if(op_tok1 != NULL)
{
/* Process numbers */
return;
}
op_tok2 = strtok(token, ",");
if(op_tok2 != NULL)
{
/* Process other numbers */
return;
}
This works for anything delimetered with a backslash, but not with a comma. I believe this is because strtok messes with the token variable. Is this true? Is there a better way to go about this? thanks!
There are certainly ways I'd consider better. If you can depend reasonably well on the format of the input (i.e., really being three digits followed by one of the allowed delimiters), you could do something like:
char *pos = 0;
while (2 == sscanf(input+pos, "%d%c", &number, &delimiter)) {
if ('\\' == delimiter)
process_backslash(number);
else if (',' == delimiter)
process_comma(number);
else
error_invalid_delimiter(delimiter);
pos += 4;
}
Others have posted better solutions - strtok is not really suitable for this task. However, answering the first question - is strtok changing the underlying string, Yes (it's evil in my mind how it works. Many a young player has fallen into this trap):
strtok replaces token with \0 (Null terminator) and passes the start of the string. Subsequent calls to strtok(NULL, <token>) continue scanning the string, looking for the next token, which does not need to be the same.
Therefore you could do:
op_tok1 = strtok(token, "\\");
if(op_tok1 != NULL)
{
/* Process numbers */
return;
}
op_tok2 = strtok(NULL, ",");
if(op_tok2 != NULL)
{
/* Process other numbers */
return;
}
Also beware it is not thread safe.
Why not just use scanf()?
~/tmp$ cat test.c
#include <stdio.h>
int main(int argc, char ** argv) {
int i;
char c;
while (2 == scanf("%d%[\\.]",&i,&c)) {
printf("Int %d\nChar %c\n", i, c);
}
}
... worked for me.
~/tmp$ gcc test.c && echo "123.456\789.4" | ./a.out
Int 123
Char .
Int 456
Char \
Int 789
Char .
~/tmp$

Resources