String array searching in C - c

I'm trying to figure out how to search a list of names that are inputted into a string array. If the name entered is part of the original array, then the search function should return the position of the string in the array; if the string is not found, it should return -1. If -1 is returned then I want to be able to print out "not found", which doesn't seem like it would be too hard to figure out, but if the name is found, I want to be able to print out the position at which the name is found.
Here is my code, obviously I'm new to this, so I might have butchered how this is supposed to be done. The rest of my code seems to work fine, but it's this function that has me at a loss.
#include<stdio.h>
#include<conio.h>
#include<string.h>
#define MAX_NAMELENGTH 10
#define MAX_NAMES 5
void initialize(char names[MAX_NAMES][MAX_NAMELENGTH]);
int search(char names[MAX_NAMES][MAX_NAMELENGTH],int i,Number_entrys);
int main()
{
char names[MAX_NAMES][MAX_NAMELENGTH];
int i;
initialize(names);
search(names,i,Number_entrys);
search_result= search(names,i,Number_entrys);
if (search_result==-1){
printf("Found no names.\n");
}
if(search_result==0){
printf("Name found");
} getch();
return 0;
}
void initialize(char names[MAX_NAMES][MAX_NAMELENGTH])
{
int i, Number_entrys;
printf("How many names would you like to enter to the list?\n");
scanf("%d",&Number_entrys);
if(Number_entrys>MAX_NAMES){
printf("Please choose a smaller entry\n");
}else{
for (i=0; i<Number_entrys;i++){
scanf("%s",names[i]);
}
}
for(i=0;i<Number_entrys;i++){
printf("%s\n",names[i]);
}
}
int search(char names[MAX_NAMES][MAX_NAMELENGTH],int i)
{
int j, idx;
char name[MAX_NAMELENGTH];
printf("Now enter a name in which you would like to search the list for:");
scanf("%s", name);
for(x = 0; x < Number_entrys; x++) {
if ( strcmp( new_name, names[x] ) == 0 ){
/* match, x is the index */
return x;
}else{
return -1;
}
}
}

There are several problems here.
The purpose of search is to ask the user to enter a single name to be searched for. So why is
char new_name[MAX_NAMES][MAX_NAMELENGTH];
You need only a single array
char new_name[MAX_NAMELENGTH];
Then you have a loop you just go round once, so you don't need a loop
scanf("%s",new_name);
would be enough. This feels like you have copied the code you used to fill your array of names, but haven't really understood its essence.
Another problem is that you have no control on how long a name the user might enter. What would happen if the user typed a very long name? You'd over-fill the array and your program will probably crash and burn. Read this article to learn about how to control this.
To be really pedantic you should also check the return code from scanf, you are expecting to read one item so the return value should be 1, anything else would be an error.
Then you are trying to use strstr(), to look through an array of char arrays. The strstr documentation says that its purpose is to search for a substring within a string rather than search through an array of strings.
So instead just search the array by hand
/* what is i? the number of items used in the array? */
for(x = 0; x < i; x++) {
if ( strcmp( new_name, names[x] ) == 0 ){
/* match, x is the index */
return x;
}
}
/* here with no match */
return -1;
In your main
int search_result;
search_result = search( /* etc */ );
if ( search_result == -1 ) {
/* print "not found" here */
} else {
/* print something else */
}

You means like this:
int search(char names[MAX_NAMES][MAX_NAMELENGTH], int i)
{
int j, idx;
char name[MAX_NAMELENGTH];
printf("Now enter a name in which you would like to search the list for:");
scanf("%s", name);
idx = -1;
for(j = 0; j < i; j++){
if(strstr(names[i], name) != NULL){
idx = j;break;
}
}
return idx;
}

(8) ...
Maybe we'll turn it all around
'Cause it's not too late
It's never too late!! (8)
:) !
Sorry about the song ^_^... oh Well, I was having fun with this question for a couples of minutes, I hope this code help you out a little more, with regards:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* return array of indexs */
int*
search(const char *list, size_t slen, const char **arr_names, size_t arrlen)
{
int *arr_idx;
int j,i,idx;
i=idx=0;
arr_idx = malloc(sizeof(int)*arrlen+1);
if(!arr_idx)
exit(EXIT_FAILURE);
for(i=0; i<slen; i++) {
for(j=0; j<arrlen ; j++) {
if(!strncmp(&list[i], &arr_names[j][0], strlen(arr_names[j]))) {
arr_idx[idx] = j;
idx++;
}
}
}
arr_idx[idx] = -1; /* -1 terminated array */
if(!idx) {
free(arr_idx);
return NULL; /* found no names */
}
return arr_idx;
}
/* I'm a sick [something], nul terminated strings :P */
const char *string = "My favorite artists: ATB, Nujabes and Bonobo also Aphex Twins is nice, along with Trapt and Breaking Benjamin.\0";
const char *names[] = {"ATB\0", "Scifer\0", "Aphex\0", "Bonobo\0", "Nujabes\0", "Tipper\0"};
#define N_NAMES 6
int
main(void)
{
int i;
int *array;
array = search(string, strlen(&string[0]), names, N_NAMES);
if(!array) {
puts("Found no names.\n");
return EXIT_SUCCESS;
}
printf("Names found: ");
for(i=0; array[i]!=-1; i++)
printf("%s,", names[array[i]]);
printf("\b \n");
free(array); /* important */
/* system("pause"); */ /* NOTE: decomment this if you're on windows */
return EXIT_SUCCESS;
}
ran some tests:
~$ ./program
output
Names found: ATB,Nujabes,Bonobo,Aphex

Related

find words that match user input with strstr

In my register, I want to find all the words that match user input and display them.
For example if I have words like redapple, rottenapple, apple, banana in my register.
And user inputs apple I want to be able to dispaly redapple, rottenapple, apple and their itemnumber and inventory balance. I cannot display in the right way and cannot figure it why, have tried different way and I will post my last try. Thank you!
void search(Car a[],int nr){
char ItmName[50];
int i;
while(1){
printf("Type item name (q for menu): ");
scanf("%s%*c", &ItmName);
if(strcmp(ItmName,"q")==0){
return;
}else{
for(i=0;i<nr;i++){
char *word = strstr(a[i].name,ItmName);
for(i=0;i<nr;i++)
if(word==itemN){
printf("%d\t\t%s\t\t%d\n", a[i].itemNmr, a[i].name, a[i].inventory);
}
return;
}
}
}
}
Your nested loop use the same control variable, i, and continuation condition, which ensures only one iteration of the outer loop occurs.
The contents of the loop make little sense. You repeatedly compare a pointer to the first element of the input buffer (itemN; pressumably itemName) against the pointer value returned by strstr, after it looks through the name field of only the first element of the a array for the substring provided in itemName.
Rewritten verbosely, this reads as
for (i = 0; i < nr; i++) {
for (i = 0; i < nr; i++) {
if (strstr(a[0].name, itemName) == &itemName[0]) {
printf(/* some information */);
}
}
}
which hopefully you can see makes no sense. A pointer value that points to an element of a[0].name will never be equal to the pointer value that points to the first element of itemName - as that would require their memory to overlap somehow.
In any case, this should not require any nested loops, as this can be done with a linear search of your array of structures.
First suggestion: move the user input to outside the function. Make search accept a third argument, a string to search for in each structures name. Separately (and repeatedly) take user input and then call this function.
Second suggestion: forget scanf exists entirely. Use fgets (and sscanf if you need to extract formatted data from the string).
Here's a cursory example of a linear search function:
#include <stdio.h>
#include <string.h>
typedef struct {
char name[128];
unsigned inventory;
} Car;
void search(Car *cars, size_t length, const char *query)
{
for (size_t i = 0; i < length; i++)
if (strstr(cars[i].name, query))
printf("NAME: [%s]\tQUANT: [%u]\n", cars[i].name, cars[i].inventory);
}
int main(void)
{
Car cars[] = {
{ "redapple", 10 },
{ "rottenapple", 7 },
{ "foo", 4 },
{ "bar", 15 },
{ "candyapple", 11 }
};
size_t len = sizeof cars / sizeof *cars;
while (1) {
char buf[512];
printf("Enter a search string (. = exit): ");
fflush(stdout);
if (!fgets(buf, sizeof buf, stdin))
return 1;
if ('.' == *buf)
return 0;
/* remove any trailing CRLF */
buf[strcspn(buf, "\r\n")] = '\0';
search(cars, len, buf);
}
}

Sorting structures from files

I recently got an assignment to sort members in a struct by last name and if they are the same to sort by first name. What i have so far only reads their name and age from the file but I am not properly grapsing how I would be able to sort it. So far I gathered the data from the file but im at a loss from there. I followed a code I saw but i didnt get a proper grasping of the process so i reverted back to step one.
struct Members{
int id;
char fname[50];
char lname[50];
int age;
}bio;
int main(){
int i=0;
FILE *fptr;
file = fopen("Members Bio.txt", "r");
while ( fscanf(file, "%d%s%s%d", &bio[i].id,bio[i].fname,bio[i].lname,&bio[i].age) != EOF)
{
printf("%d %s %s %d %d\n", bio[i].id,bio[i].fname, bio[i].lname, bio[i].age);
i++;
}
fclose(fptr);
}
Can anyone help me out on this one?
Code goes something like this for your case.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Members{
int id;
char fname[50];
char lname[50];
int age;
};
typedef int (*compare_func)(void*, void*);
int struct_cmp(void* s1, void* s2)
{
int l_result = strcmp(((struct Members*) s1)->lname, \
((struct Members*) s2)->lname);
if (l_result < 0)
return 1;
else if (l_result > 0)
return 0;
else
return (strcmp(((struct Members*) s1)->fname, \
((struct Members*) s2)->fname) < 0 ? 1 : 0);
}
void sort(void* arr,long ele_size,long start,long end,compare_func compare)
{
// Generic Recursive Quick Sort Algorithm
if (start < end)
{
/* Partitioning index */
void* x = arr+end*ele_size;
long i = (start - 1);
void* tmp=malloc(ele_size);
for (long j = start; j <= end - 1; j++)
{
if ((*compare)(arr+j*ele_size,x))
{
i++;
// Swap is done by copying memory areas
memcpy(tmp,arr+i*ele_size,ele_size);
memcpy(arr+i*ele_size,arr+j*ele_size,ele_size);
memcpy(arr+j*ele_size,tmp,ele_size);
}
}
memcpy(tmp,arr+(i+1)*ele_size,ele_size);
memcpy(arr+(i+1)*ele_size,arr+end*ele_size,ele_size);
memcpy(arr+end*ele_size,tmp,ele_size);
i= (i + 1);
sort(arr,ele_size,start, i - 1,compare);
sort(arr,ele_size,i + 1, end,compare);
}
}
int main()
{
FILE* fp;
int bio_max = 3;
struct Members bio[bio_max]; // Define bio to be large enough.
/* Open FILE and setup bio matrix */
/* For testing */
bio[0].id = 0;
strcpy(bio[0].fname, "");
strcpy(bio[0].lname, "Apple");
bio[0].age = 0;
bio[1].id = 1;
strcpy(bio[1].fname, "");
strcpy(bio[1].lname, "Cat");
bio[1].age = 1;
bio[2].id = 2;
strcpy(bio[2].fname, "");
strcpy(bio[2].lname, "Bat");
bio[2].age = 2;
/* Sort the structure */
sort(bio, sizeof(struct Members), 0, bio_max - 1, struct_cmp);
/* Print the sorted structure */
for (int i = 0; i < bio_max; i++) {
printf("%d %s %s %d\n", bio[i].id, bio[i].fname, \
bio[i].lname, bio[i].age);
}
}
Output
0 Apple 0
2 Bat 2
1 Cat 1
If the strings are not sorting in the way you want, you can redefine the struct_cmp function. Code is self explanatory, the base logic in the code is pass an array and swap elements using memcpy functions. You cant use simple assignment operator if you want to be generic, so that is why the element size is explicitly passed.
Edit
The code was not handling the condition, if lname are same. I missed it thanks for #4386427 for pointing this out.
I think you should define bio to be an array. And google sort algorithms please. Also recommend you google how to use libc function qsort.

How to get struct from a file in C

I am new to programming and would like some help in for File in C.
This is my code
#include <stdio.h>
#include <string.h>
void read_file();
void write_fil();
void add();
void display();
void search();
#define NAME_CHRS 30
struct employee_rec
{ char name[NAME_CHRS];
int dependents;
float pay_rate;
};
struct employee_rec employee;
struct employee_rec emp_array[];
FILE *employ;
int count=-1;
void read_file(void)
{
int idx;
employ = fopen("EMPLOYEE.DAT", "a+");//i add
//for (idx=0; idx <= count; idx++)
fread(&emp_array[idx], sizeof emp_array[idx], 1, employ);
fclose(employ);
}
void write_file()
{
int x;
employ = fopen("EMPLOYEE.DAT", "a+");
for (x=0; x <= count; x++)
fwrite(&emp_array[x], sizeof emp_array[x], 1, employ);
fclose(employ);
}
void add()
{
count+=1;
printf("Enter name: ");
scanf("%s", emp_array[count].name);
printf("Pay rate, other dependents: ");
scanf("%f %i", &emp_array[count].pay_rate, &emp_array[count].dependents);
}
void display()
{
int idx;
read_file();
printf("Name\t\tPay Rate\tDependents\n");
printf("----\t\t--------\t----------\n");
for (idx=0; idx <= count; idx++)
{
printf("%-10s\t%-8g\t%-8d\n", emp_array[idx].name, emp_array[idx].pay_rate, emp_array[idx].dependents);
}
}
void search()
{
char target[20];
int idx, found=0;
printf("Enter a name to search: ");
scanf("%s", target);
for (idx=0; idx <= count; idx++){
if(strcmp(emp_array[idx].name, target) == 0)
{
found = 1;
break;
}
}
if(found == 1)
{
printf("Name: %s\n", emp_array[idx].name);
printf("Pay rate: %g\n", emp_array[idx].pay_rate);
printf("Dependents: %d\n", emp_array[idx].dependents);
}
else
printf("Not found!\n");
}
int main(void)
{
int i;
//for (i=0; i < 3; i++)
// add();
// write_file();
display();
search();
return 0;
}
The original program would ask the user to key in data and then display it for the user.
So when another user runs the program and has the file it doesnt need to add more data but just read from the file
I have commented the for loop, display and search in my main because i just want to retrieve the data from my file.
Im not sure what to do with
fread(&emp_array[idx], sizeof emp_array[idx], 1, employ);
fclose(employ);
Since right now im using the counter from the insert to get the size, it will say size is 0 as the current run did not enter the data into the file (file already has data).
Is there any other way I can get all my data from the file without inserting it in the same run?
EDIT: So as some of the comments have mentioned the size of emp_arry is empty. I would like to get the size from a file. Since the file as a array inside which i want to transfer the data to emp_array.
One simple solution is to design a file header which includes the number of data in this file. Then, you can first read file header to get the number and then dynamically allocate memory to read data from file.

Overlapping and too long integer values in dynamic c structs

I have the following problem.
I need to create a list of savestates with dynamical length. That's why I decided to create a list by defining some structs and connecting dynamically created structs together to build a list of structs which can dynamically be extended and so on.
However, some things seem to not work at all. Here's the relevant code first:
saves.h:
#ifndef SAVES_H
#include<time.h>
#define SAVES_H
#define SVS_STRLEN 500
#define SVS_FILE "savefile.dat"
#define True 1
#define False 0
typedef struct SVS_STATE SVS_STATE;
typedef struct SVS_STATES SVS_STATES;
struct SVS_STATE {
int i_playfield[6][7];
int i_turn;
time_t i_time;
void *next;
};
struct SVS_STATES {
SVS_STATE *states;
int count;
int loaded;
};
void SVS_Add_State(int i_playfield[][7], int i_turn, time_t i_time);
void SVS_Debug_State(SVS_STATE *state);
void SVS_Format_State(SVS_STATE *state, char text[]);
SVS_STATE *SVS_Get_State(int number);
#endif
saves.c:
#include "saves.h"
#include<string.h>
#include<time.h>
SVS_STATE *SVS_Get_State(int number)
{
int i = 1;
SVS_STATE *state;
if (svs_current_state.loaded == False) return NULL;
if (number > svs_current_state.count) return NULL;
state = svs_current_state.states;
printf("printing state 1:");
SVS_Debug_State(state);
while( i < number)
{
i++;
state = (SVS_STATE*)(state->next);
printf("printing state %i:", i);
SVS_Debug_State(state);
}
return state;
}
void SVS_Format_State(SVS_STATE *state, char text[])
{
int i, j;
if (svs_current_state.loaded == False) return;
text[0] = '\0';
strcat(text, "{\0");
for (i = 0; i < X_SIZE; i++)
{
strcat(text, "{\0");
for(j = 0; j < Y_SIZE; j++)
{
strcat(text, "%i,\0");
sprintf(text, text, state->i_playfield[i][j]);
}
strcat(text, "}\0");
}
strcat(text, "};%i;%i\n\0");
sprintf(text, text, state->i_turn, state->i_time);
printf("\nFormatted state:%s\n", text);
}
void SVS_Debug_State(SVS_STATE *state)
{
char text[SVS_STRLEN];
SVS_Format_State(state, text);
printf("%s\n", text);
}
void SVS_Add_State(int i_playfield[][7], int i_turn, time_t i_time)
{
int i, j;
SVS_STATE *laststate, *newstate;
newstate = (SVS_STATE*)malloc(sizeof(SVS_STATE));
printf("adding state with time:%i\n", i_time);
if (svs_current_state.loaded == False) return;
for (i = 0; i < 6; i++)
for (j = 0; j < 7; j++)
newstate->i_playfield[i][j] = i_playfield[i][j];
newstate->i_turn = i_turn;
newstate->i_time = i_time;
newstate->next = NULL;
printf("initialized state:");
SVS_Debug_State(newstate);
if (svs_current_state.coun > 0)
{
laststate = SVS_Get_State(svs_current_state.count);
laststate->next = (void*)newstate;
} else
svs_current_state.states=newstate;
svs_current_state.count++;
}
int main()
{
int i_playfield[6][7] = {0};
// mark saves library as loaded here, but removed function, since it
// just sets svs_current_state.loaded (which is the global struct of
// type SVS_STATES) to 1
SVS_Add_State(i_playfield, 1, time(NULL));
i_playfield[0][0] = 2;
SVS_Add_State(i_playfield, 2, time(NULL));
return 0;
}
The actual problems I encountered while using the printf's and Debug_State calls in these functions:
- the i_time I give is printed out once in Add_State(), correctly. Means it is a legal time and stuff, but when printed out after creating the full state by using Format_State() the string is 50 percent to long and the last part is displayed twice, for example:
if the time is 12345678, it is displayed correctly while debugging in Add_State, but Format_State() displays 123456785678.
- second problem: the first state added works, more or less, fine. But after adding a second one, printing the first state (retrieved by using Get_State and formatted with Format_State) prints a mixture of two states, for example something like this:
state 1: {{0,0,0,0,0,0,0}{0,0,0,0,0,0,0}{0,0,0,0,0,0,0}...
{0,0,0,0,0,0}};1;123456785678
state 2: {{0,0,0,0,0,0}{0,0,0,0,0,0}...
{0,0,0,0,0,0}};2;1234567856785678,0}{0,0,0,0,0,0}...
Thanks for reading.
These calls
sprintf(text, text, ...
invoke undefined behaviour, as the target buffer and one of the other arguments overlap.
From the POSIX specs to sprintf():
If copying takes place between objects that overlap as a result of a call to sprintf() [...], the results are undefined.

How to loop through data file to fill in structs?

Program specifications:
Read questions from a data file in the following format:
Question
Number of choices
N-amount of choices
Correct answer
Example:
What is the capital of France?
3
Madrid
Sydney
Paris
Paris
Present the user a question at a time and keep track of their score, etc, etc.
What I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINE_SIZE 60
#define MAX_LIST_SIZE 15
#define MAX_QUIZ_SIZE 10
typedef struct question {
char *question;
char **choices;
int n_choices;
char *correct_answer;
} QUESTION;
typedef struct quiz {
struct question *questions;
int n_questions;
} QUIZ;
char *dupString(const char *s) {
// copies a string
char *dup = malloc(strlen(s) + 1);
strcpy(dup, s);
return dup;
}
void free_choices(QUESTION *q) {
// free memory
for(int i = 0; i < q->n_choices; i++) {
free(q->choices[i]);
}
free(q->choices);
}
int ask(QUESTION *q) {
// Return 1 for correct guess, 0 for incorrect guess.
int choice;
// Print the question
printf("\n%s\n", q->question);
// Print the choices
for (int i = 0; i <= q->n_choices-1; i++) {
printf("%d : %s", i+1, q->choices[i]);
}
// Get user guess
do {
printf("Select an answer [1-%d]: ", q->n_choices);
scanf("%d", &choice);
/* Not sure how to structure here*/
if (strcmp(q->choices[choice-1], q->correct_answer) == 0) {
// if correct return 1
return 1;
}
} while (choice < 1 || choice > q->n_choices);
// Incorrect
return 0;
}
struct question parseQuestion(FILE *pData) {
int qIndex, numChoices;
char question[MAX_LINE_SIZE], temp[MAX_LINE_SIZE], choices[MAX_LINE_SIZE], correctAns[MAX_LINE_SIZE];
QUESTION q = {NULL, NULL, 0, NULL};
// Eat first line = QUESTION
fgets(question, MAX_LINE_SIZE, pData);
q.question = question;
// Eat second line = NUMBER OF CHOICES
fgets(temp, MAX_LINE_SIZE, pData);
numChoices = atoi(temp);
q.n_choices = numChoices;
// Allocate memory
q.choices = calloc(q.n_choices, sizeof(char*));
// Eat nth lines = CHOICES
for (qIndex=0; qIndex<=numChoices-1; qIndex++) {
fgets(choices, MAX_LINE_SIZE, pData);
q.choices[qIndex] = dupString(choices);
}
// Eat nth + 1 line = CORRECT ANSWER
fgets(correctAns, MAX_LINE_SIZE, pData);
q.correct_answer = correctAns;
return q;
}
int main() {
int num = 0; // question being asked
int strikes = 0; // incorrect guesses
FILE* pData;
char *filename = "tickle.txt";
char c;
if ((pData = fopen(filename, "r"))) {
printf("Welcome to the 2014 Quiz-festival!\n\n");
printf("Are you ready to begin? [Y/y]\n");
c = getchar();
if (c == 'Y' || c == 'y') {
QUESTION question = parseQuestion(pData);
ask(&question);
free_choices(&question);
} else {
printf("Come back again.\n");
return 0;
}
} else {
printf("File failed to open.");
}
fclose(pData);
return 0;
}
Thank you to #alk how picked up my error, that is resolved.
What I still can't get is how to loop through the data file and populate the quiz structure with question structures.
So this is where I'm struggling at the moment. From what I can tell I'm pretty close to finishing this little program as long as I can get this to work.
parseQuestion() duplicates the choices but misses to duplicate the question as well as the answer.
Instead it simply copies the two arrays' addresses to the locally defined variable QUESTION q which is copied on return.
The memory for the question and answer strings is free'd on returning from the function, accessing it afterwards invokes undefined behaviuor.

Resources