How to compare String to list of items in c - c
I saw another post about similar code but they only compared the string to one other string was wondering if this works / if there's a simple more beginner-friendly way I should learn. Thank you
#include <cs50.h>
#include <stdio.h>
int main(void)
{
string user_imput = get_string("Fruit or Vegetable?, ");
if (strcmp(user_imput, "apple"|| "blueberries" || "cherries" || "bananas" ||"grapes" || "oranges" || "watermelon" ||"lemons" == 0));
{
printf("Fuit!, %s\n", user_imput);
}
else if (strcmp(user_imput, "potatoes" || "tomatoe" || "onions" || "carrot" || "bellpepper" || "lettuce" || "cucumbers" || "broccoli" == 0));
{
printf("Vegtable! %s\n", user_imput);
}
else
{
printf("NA");
}
}```
An easy way to do this is to create a function which searches through an array containing your strings. You then loop through comparing each one individually. If found, we return a truthy value right away. If the loop runs its course and finds nothing, we return a falsy value afterwards.
int is_fruit(string food) {
static const string fruits[] = {
"apple", "blueberries", "cherries" /* ... */
};
for (size_t i = 0; i < (sizeof fruits / sizeof fruits[0]); i++)
if (strcmp(fruits[i], food) == 0)
return 1;
return 0;
}
Then your if ... else looks something like:
if (is_fruit(user_input)) {
/* ... */
} else if (is_vegetable(user_input)) {
/* ... */
} else {
/* ... */
}
A good exercise would be to complete the function above (fill out the array), and write a similar one for vegetables. An even better exercise would be to wring a more generic function that takes a string and any array of strings, and deciding if the array contains the string. Its function prototype would look like this:
int string_array_contains(const string array[], size_t length, const string s);
It would work very similarly to the function above, except you would have to pass in the length of the array, since sizeof cannot be used to determine the length of the array argument.
If functions and arrays are too advanced for you at the moment, then simply know that you must compare each string individually using strcmp. Writing this by hand leads to very long, hard to maintain code:
if (strcmp(user_input, "apple") == 0 || strcmp(user_input, "blueberries") == 0 /* ... */) {
/* ... */
}
Related
If I can't define a variable inside an if-condition, how can I reduce searching through my array multiple times in this code?
I have a function who's body looks a lot like this: if (contains(array, element1) > -1){ // do something } else if (contains(array, element2) > -1) { // do something } else if (contains(array, element3) > -1) { // do someting }... The function contains will loop through my array and check to see if it contains an element that I pass to it and return either its position if it exists, or -1 if it doesn't. In my // do something portion, I need the position of this element in the array. There are a couple of different ways I can do this: I can call my contains() function once more to get the position. I can define several variables that are defined as the return of the contain function, and then check them in my if-else block. So something like: int element1Loc = contains(array, element1); int element2Loc = contains(array, element2); int element3Loc = contains(array, element3); if (element1Loc > -1){ // do something } else if (element2Loc > -1) { // do something } else if (element3Loc > -1) { // do someting }... I can perhaps modify contain to return an int array[2], with array[0] equal to 0 or 1 whether the element is in it or not, and then array[1] qwould equal the actual location, making the code look like thiss: if (contains(array, element1)[0] > -1){ // do something } else if (contains(array, element2)[0] > -1) { // do something } else if (contains(array, element3)[0] > -1) { // do something }... I can say screw the if-else block, save the return of contains in a variable and run several if-statements. Solution one will search through my array at least twice. Solution two will search at least as many times as there are elements I'm looking for. Solution 3 is perhaps the best, but maybe not the most elegant. Solution 4 will run each if statement... What is the best way to search just once? Should I make a function that takes all the things I am looking for and returns an array with the position of each element? Am I overthinking it?
I would modify contains to only use the return value to indicate the error/success of the find, and, if the parameter was found, output the parameter by reference. int contains(int *data, int value, int *loc) { for(int i = 0; i < SIZE; i++) { if(data[i]==value) { *loc = i; return 1; // success } } *loc = -1; return 0; // failure } Now, you can just do: int elem1loc, elem2loc, elem3loc; if(contains(data, val1, &elem1loc)) // use elem1loc... if(contains(data, val2, &elem2loc)) // use elem2loc...
You could pass a pointer to say int which would be populated when the contains function finds an element. Then inside your if block you would be assured that pos is the correct index. Example: int pos; if (contains(array, element1, &pos) > -1) { // Here you can check pos for the position } else if (contains(array, element2, &pos) > -1) { // Here you can check pos as well... }
Here's a solution that doesn't require you to modify contains at all: int pos; if ((pos = contains(array, element1)) > -1) { // do something with pos } else if ((pos = contains(array, element2)) > -1) { // do something with pos } else if ((pos = contains(array, element3)) > -1) { // do something with pos } This works because variable assignment in most imperative languages is an expression.
Printing an array of structs in C
I'm trying to print an array of structs that contain two strings. However my print function does not print more than two indices of the array. I am not sure why because it seems to me that the logic is correct. This is the main function const int MAX_LENGTH = 1024; typedef struct song { char songName[MAX_LENGTH]; char artist[MAX_LENGTH]; } Song; void getStringFromUserInput(char s[], int maxStrLength); void printMusicLibrary(Song library[], int librarySize); void printMusicLibraryTitle(void); void printMusicLibrary (Song library[], int librarySize); void printMusicLibraryEmpty(void); int main(void) { // Announce the start of the program printf("%s", "Personal Music Library.\n\n"); printf("%s", "Commands are I (insert), S (sort by artist),\n" "P (print), Q (quit).\n"); char response; char input[MAX_LENGTH + 1]; int index = 0; do { printf("\nCommand?: "); getStringFromUserInput(input, MAX_LENGTH); // Response is the first character entered by user. // Convert to uppercase to simplify later comparisons. response = toupper(input[0]); const int MAX_LIBRARY_SIZE = 100; Song Library[MAX_LIBRARY_SIZE]; if (response == 'I') { printf("Song name: "); getStringFromUserInput(Library[index].songName, MAX_LENGTH); printf("Artist: "); getStringFromUserInput(Library[index].artist, MAX_LENGTH); index++; } else if (response == 'P') { // Print the music library. int firstIndex = 0; if (Library[firstIndex].songName[firstIndex] == '\0') { printMusicLibraryEmpty(); } else { printMusicLibraryTitle(); printMusicLibrary(Library, MAX_LIBRARY_SIZE); } This is my printing the library function // This function will print the music library void printMusicLibrary (Song library[], int librarySize) { printf("\n"); bool empty = true; for (int i = 0; (i < librarySize) && (!empty); i ++) { empty = false; if (library[i].songName[i] != '\0') { printf("%s\n", library[i].songName); printf("%s\n", library[i].artist); printf("\n"); } else { empty = true; } } }
I think the problem is caused due to setting : empty = true outside the for loop and then checking (!empty) which will evaluate to false. What I am surprised by is how is it printing even two indices. You should set empty = false as you are already checking for the first index before the function call.
The logic has two ways to terminate the listing: 1) if the number of entries is reached, or 2) if any entry is empty. I expect the second condition is stopping the listing before you expect. Probably the array wasn't built as expected (I didn't look at that part), or something is overwriting an early or middle entry.
you gave the definition as: typedef struct song { char songName[MAX_LENGTH]; char artist[MAX_LENGTH]; }Song; the later, you write if (library[i].songName[i] != '\0') which really seems strange: why would you index the songname string with the same index that the lib? so I would naturally expect your print function to be: // This function will print the music library void printMusicLibrary (Song library[], int librarySize) { for (int i = 0; i < librarySize; i ++) { printf("%s\n%s\n\n", library[i].songName, library[i].artist); } } note that you may skip empty song names by testing library[i].songName[0] != '\0' (pay attention to the 0), but I think it would be better not to add them in the list (does an empty song name make sens?) (If you decide to fix that, note that you have an other fishy place: if (Library[firstIndex].songName[firstIndex] == '\0') with the same pattern)
Parsing code for GPS NMEA string
i am trying to parse the incoming GPGGA NMEA GPS string using Arduino uno and below code. What i am trying to do is that i am using only GPGGA NMEA string to get the values of Latitude, longitude and altitude.In my below code, i had put certain checks to check if incoming string is GPGGA or not, and then store the further string in a array which can be further parsed suing strtok function and all the 3 GPS coordinates can be easily find out. But i am unable to figure out how to store only GPGGA string and not the further string.I am using a for loop but it isn't working. I am not trying to use any library.I had came across certain existing codes like this. Here is the GPGGA string information link i am trying to have following functionlity i) Check if incoming string is GPGGA ii) If yes, then store the following string upto EOL or upto * (followed by checksum for the array) in a array, array length is variable(i am unable to find out solution for this) iii) Then parse the stored array(this is done, i tried this with a different array) #include <SoftwareSerial.h> SoftwareSerial mySerial(10,11); // 10 RX / 11 TX void setup() { Serial.begin(9600); mySerial.begin(9600); } void loop() { uint8_t x; char gpsdata[65]; if((mySerial.available())) { char c = mySerial.read(); if(c == '$') {char c1 = mySerial.read(); if(c1 == 'G') {char c2 = mySerial.read(); if(c2 == 'P') {char c3 = mySerial.read(); if(c3 == 'G') {char c4 = mySerial.read(); if(c4 == 'G') {char c5 = mySerial.read(); if(c5 == 'A') {for(x=0;x<65;x++) { gpsdata[x]=mySerial.read(); while (gpsdata[x] == '\r' || gpsdata[x] == '\n') { break; } } } else{ Serial.println("Not a GPGGA string"); } } } } } } } Serial.println(gpsdata); } Edit 1: Considering Joachim Pileborg, editing the for loop in the code. I am adding a pic to show the undefined output of the code. Input for the code: $GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43 $GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75 $GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A $GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70 $GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77 $GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76 $GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45
After a quick check of the linked article on the NMEA 0183 protocol, this jumped out at me: <CR><LF> ends the message. This means, that instead of just read indiscriminately from the serial port, you should be looking for that sequence. If found, you should terminate the string, and break out of the loop. Also, you might want to zero-initialize the data string to begin with, to easily see if there actually is any data in it to print (using e.g. strlen).
You could use some functions from the C library libnmea. Theres functions to split a sentence into values by comma and then parse them.
Offering this as a suggestion in support of what you are doing... Would it not be useful to replace all of the nested if()s in your loop with something like: EDIT added global string to copy myString into once captured char globalString[100];//declare a global sufficiently large to hold you results void loop() { int chars = mySerial.available(); int i; char *myString; if (chars>0) { myString = calloc(chars+1, sizeof(char)); for(i=0;i<chars;i++) { myString[i] = mySerial.read(); //test for EOF if((myString[i] == '\n') ||(myString[i] == '\r')) { //pick this... myString[i]=0;//strip carriage - return line feed(or skip) //OR pick this... (one or the other. i.e.,I do not know the requirements for your string) if(i<chars) { myString[i+1] = mySerial.read() //get remaining '\r' or '\n' myString[i+2]=0;//add null term if necessary } break; } } if(strstr(myString, "GPGGA") == NULL) { Serial.println("Not a GPGGA string"); //EDIT strcpy(globalString, "");//if failed, do not want globalString populated } else { //EDIT strcpy(globalString, myString); } } //free(myString) //somewhere when you are done with it } Now, the return value from mySerial.available() tells you exactly how many bytes to read, you can read the entire buffer, and test for validity all in one.
I have a project that will need to pull the same information out of the same sentence. I got this out of a log file import serial import time ser = serial.Serial(1) ser.read(1) read_val = ("nothing") gpsfile="gpscord.dat" l=0 megabuffer='' def buffThis(s): global megabuffer megabuffer +=s def buffLines(): global megabuffer megalist=megabuffer.splitlines() megabuffer=megalist.pop() return megalist def readcom(): ser.write("ati") time.sleep(3) read_val = ser.read(size=500) lines=read_val.split('\n') for l in lines: if l.startswith("$GPGGA"): if l[:len(l)-3].endswith("*"): outfile=open('gps.dat','w') outfile.write(l.rstrip()) outfile.close() readcom() while 1==1: readcom() answer=raw_input('not looping , CTRL+C to abort') The result is this: gps.dat $GPGGA,225714.656,5021.0474,N,00412.4420,W,0,00,50.0,0.0,M,18.0,M,0.0,0000*5B
Using "malloc" every single time you read a string is an enormous amount of computational overhead. (And didn't see the corresponding free() function call. Without that, you never get that memory back until program termination or system runs out of memory.) Just pick the size of the longest string you will ever need, add 10 to it, and declare that your string array size. Set once and done. There are several C functions for getting substrings out of a string, strtok() using the coma is probably the least overhead. You are on an embedded microcontroller. Keep it small, keep overhead down. :)
#include <stdio.h> #include <string.h> #define GNSS_HEADER_LENGTH 5 #define GNSS_PACKET_START '$' #define GNSS_TOKEN_SEPARATOR ',' #define bool int #define FALSE 0 #define TRUE 1 //To trim a string contains \r\n void str_trim(char *str){ while(*str){ if(*str == '\r' || *str == '\n'){ *str = '\0'; } str++; } } /** * To parse GNSS data by header and the index separated by comma * * $GPGSV,1,1,03,23,39,328,30,18,39,008,27,15,33,035,33,1*5A * $GNRMC,170412.000,V,,,,,,,240322,,,N,V*2D * $GNGGA,170412.000,,,,,0,0,,,M,,M,,*57 * * #data_ptr the pointer points to gps data * #header the header for parsing GPGSV * #repeat_index the header may repeat for many lines * so the header index is for identifying repeated header * #token_index is the index of the parsing data separated by "," * the start is 1 * #result to store the result of the parser input * * #result bool - parsed successfully **/ bool parse_gnss_token(char *data_ptr, char *header, int repeat_index, int token_index, char *result) { bool gnss_parsed_result = FALSE; // To check GNSS data parsing is success bool on_header = FALSE; // For header int header_repeat_counter = 0; int header_char_index = 0; // each char in header index // For counting comma int counted_token_index = 0; // To hold the result character index bool data_found = FALSE; char *result_start = result; char header_found[10]; while (*data_ptr) { // 1. Packet start if (*data_ptr == GNSS_PACKET_START) { on_header = TRUE; header_char_index = 0; // to index each character in header data_found = FALSE; // is data part found data_ptr++; } // 2. For header parsing if (on_header) { if (*data_ptr == GNSS_TOKEN_SEPARATOR || header_char_index >= GNSS_HEADER_LENGTH) { on_header = FALSE; } else { header_found[header_char_index] = *data_ptr; if (header_char_index == GNSS_HEADER_LENGTH - 1) { // Now Header found header_found[header_char_index + 1] = '\0'; on_header = FALSE; if (!strcmp(header, header_found)) { // Some headers may repeat - to identify it set the repeat index if (header_repeat_counter == repeat_index) { //printf("Header: %s\r\n", header_found ); data_found = TRUE; } header_repeat_counter++; } } header_char_index++; } } // 3. data found if (data_found) { // To get the index data separated by comma if (counted_token_index == token_index && *data_ptr != GNSS_TOKEN_SEPARATOR) { // the data to parse *result++ = *data_ptr; gnss_parsed_result = TRUE; } if (*data_ptr == GNSS_TOKEN_SEPARATOR) { // if , counted_token_index++; // The comma counter for index } // Break if the counted_token_index(token_counter) greater than token_index(search_token) if (counted_token_index > token_index) { break; } } // Appending \0 to the end *result = '\0'; // To trim the data if ends with \r or \n str_trim(result_start); // Input data data_ptr++; } return gnss_parsed_result; } int main() { char res[100]; char *nem = "\ $GNRMC,080817.000,A,0852.089246,N,07636.289920,E,0.00,139.61,270322,,,A,V*04\r\n\\r\n\ $GNGGA,080817.000,0852.089246,N,07636.289920,E,1,5,1.41,11.246,M,-93.835,M,,*5E\r\n\ $GNVTG,139.61,T,,M,0.00,N,0.00,K,A*2F\r\n\ $GNGSA,A,3,30,19,17,14,13,,,,,,,,1.72,1.41,0.98,1*0A\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,3*02\r\n\ $GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,6*07\r\n\ $GPGSV,3,1,12,06,64,177,,30,60,138,15,19,51,322,18,17,42,356,27,1*68\r\n\ $GPGSV,3,2,12,14,36,033,17,07,34,142,17,13,32,267,17,02,21,208,,1*6C\r\n\ $GPGSV,3,3,12,15,05,286,,01,05,037,,03,03,083,,20,02,208,,1*6B\r\n\ $GAGSV,1,1,00,7*73\r\n\ $GIGSV,1,1,00,1*7D\r\n\ $GNGLL,0852.089246,N,07636.289920,E,080817.000,A,A*43\r\n\ $PQTMANTENNASTATUS,1,0,1*4F\r\n"; printf("Parsing GNRMC\r\n"); printf("===============\r\n"); for(int i=1;i<=16;i++){ parse_gnss_token(nem, "GNRMC", 0, i, res); printf("Index: %d, Result: %s\r\n", i, res); } printf("Parsing GNVTG (First Parameter)\r\n"); printf("================================"); // GNVTG - Header, 0 - Repeat Index(if header is repeating), 1 - Value Index, parse_gnss_token(nem, "GNVTG", 0, 1, res); printf("\r\nGNVTG: %s\r\n", res); return 0; }
How do you use a string array in a conditional statement in C?
I want to use a string array in an if statement to test whether the input string matches any of the strings in the array. So far this is what I've tried: void checkForError(char input[50]) { const char *input2[]={"print","loadStarter","terminate()"}; if(input != input2) { printf("Error:Incorrect method '%s'.\n",input); } else { abort(); } } And if I were to enter something in the array like "print" it would end up showing me: Error:Incorrect method 'print'. but when I try something not listed in the array like "g" it repeats the error message nonstop. I was thinking perhaps something like this could work: void checkForError(char input) { if(strcmp(input,"print"))!=0 || strcmp(input,"loadStarter"))!=0 || strcmp(input,"terminate()") { printf("Error:Incorrect method '%s'.\n"); } else { abort(); } } But it turns out that actually doesn't work so what do I do?
You cannot compare strings (usefully) in C using == and !=; you must use library functions such as strcmp instead. See How to compare strings in an "if" statement? for details.
I think a good solution to your question would be to loop around your array, aborting on the first match. void checkForError(char* input) { const char *input2[]={"print","loadStarter","terminate()"}; const int len = sizeof(input2)/sizeof(input2[0]); for (int i = 0; i < len ;i ++) { if (strcmp(input,input2[i]) == 0) { //I have matched the string input2[i] abort(); } } // Nothing matched printf("Not found\n"); } This would also be easier to extend than any handcoded method. Also, if you plan on doing a lot with these strings, and you have a limited number of strings, you should probably turn them into some sort of enum. This way you do not have to have strcmp scattered everywhere, and you can use a standard switch statement.
a better method would be to have a return value then have your error message depend on return value. // return 1 when string is OK, 0 otherwise: int checkForError(const char* input) { if(!strcmp(input,"print")) || !strcmp(input,"loadStarter")0 || !strcmp(input,"terminate()") { return 1; } else { return 0; } }
Your second thought is correct, you should not compare the strings using == operator, anyway, I'm not sure whether the rest is a typo or not, but it should be like that: void checkForError(char * input) // ^ note the * (pointer) { if(strcmp(input,"print")!=0 || strcmp(input,"loadStarter")!=0 || strcmp(input,"terminate()") != 0) // you forgot brackets { printf("Error:Incorrect method '%s'.\n", input); // ^ you forgot the "input" parameter } else { abort(); } }
Function that searches for difference between members of an array
I need to write a function that will return true if it has found a difference between members of an array. My code is: int func1(int *str) { int i; for(i=0;i<*(str+i);i++) { if(*(str+i) == *(str+i+1)) { return 1; } } return 0; } I have to implement it with pointers. The code above does not work(logically). Can anybody help? UPDATE: I have changed my code to the following: int func1(int *str) { int i,temp=0; for(i=0;i<10-1;i++) { if(*(str+i) == *(str+i+1)) { temp++; if( temp == 10 ) { return 1; } } } return 0; } What is the problem with the new code?
This looks like homework to me, so I don't want to spoil the fun but one thing about C I'd like to mention: having a pointer to some array doesn't tell you anything about the size of the array. So your function will need to take a pointer and a second size_t argument (or maybe a pointer to the last element of the array).
Your function only takes in a single array pointer, that seems like one too few for a comparison. You must add an argument that specifies the lengths of the arrays, or implement some kind of "policy" that e.g. terminates the arrays using a specific value. You should also look into using the standard memcmp() function.
I don't understand the question (It's unclear what you're trying to achieve)... As others have already said, there's no boundary checking on your array, which is wrong... Here's some other feedback on your code: // func1 - consider giving functions a meaningful name, it helps people to // understand what the function is supposed to be doing.... // In this instance, it might have been helpful to identify what the expected // return values / inputs of the function are... int func1(int *str) { int i; // Start a counter at 0, loop (adding 1) while // the current value of the counter is less than, the value held in the // array so, {1,2,3,4,0,7} Would terminate on the 0 // This: {1,20,7,14,0,7} Would also terminate on the 0 // This seems wrong, but again, it's unclear what you're trying to do here. for(i=0;i<*(str+i);i++) { // If the current element of the array // is the same as the next element of the array if(*(str+i) == *(str+i+1)) { // return 1 - two numbers next to each other in the // array are the same? return 1; } } // Either: The array contained a digit less than the counter, // Or: It didn't contain two numbers that were the same next to each other. // This seems a bit wrong?!? return 0; } Your question could be improved (to get a more useful answer), if you showed what inputs you were expecting to return what return values. Based on this 'I will need to write a function that will return true if its found diffrence between members of array.' In pseudo code, it seems like you would want: // Loop, checking we don't overflow. No point checking the last element as // there's nothing after it to check... for (count = 0 to arraysize -1) { // If the current element != the next element, we've found a difference?!? if(arrayElement[count] != arrayElement[count+1) { return true } } return false UPDATE: In your new code... // You're still assuming the size of 'str' int func1(int *str) { int i,temp=0; // Loop while i < 9, i.e. 9 times. for(i=0;i<10-1;i++) { if(*(str+i) == *(str+i+1)) { temp++; // Temp can never == 10, you're only going round the loop 9 times... // Maybe it should be (temp == 10-1), but I don't know where the // 10 comes from... if( temp == 10 ) { return 1; } } } return 0; } This: if(*(str+i) == *(str+i+1)) { temp++; // Temp can never == 10, you're only going round the loop 9 times... if( temp == 10 ) { return 1; } } Could be: // return 0 (FALSE) on first difference if(*(str+i) != *(str+i+1)) { return 0; } If you changed the return 0 at the end of your function to return 1