Storing Pieces of a String using Strtok - c

#include <stdio.h>
#include <math.h>
#include <string.h>
int main(void)
{
int menuswitch=1;
int amountofstudents;
int fname[50];
int lname[50];
int grade[50];
int i;
char studentinfo[100];
printf("Enter Amount of Students: ");
scanf("%d", &amountofstudents);
for (i=0;i<amountofstudents;i++)
{
gets(studentinfo);
strcpy(fname[i], strtok(studentinfo, " "));
strcpy(lname[i], strtok(NULL, " "));
strcpy(grade[i], strtok(NULL, " "));
}
Alright need a little using strtok. I am trying to store pieces of an input string to sort later. I was thinking of using strtok to break the string then placing each piece in the corresponding array. Yet, every time I try I get an error in Visual Studios saying Access Violation. Thanks for the help ahead of time
The error is
First-chance exception at 0x5120F7B3 (msvcr110d.dll) in Lab 2.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x5120F7B3 (msvcr110d.dll) in Lab 2.exe: 0xC0000005: Access violation reading location 0x00000000.
The input would be
FirstName Lastname 80(Grade)

One major problem is that you try to copy into integer values and not strings. Change the
integer arrays to arrays of strings:
...
char fname[50][100];
char lname[50][100];
char grade[50][100];
...
You also have a problem with the gets function (besides it being obseleted and should not be used), namely that the previous scanf doesn't remove the newline from the input buffer so the first gets call will see this empty newline and give you an empty line (which you do not check for).
This is simply solved by telling scanf to discard trailing whitespace by adding a space in the format string after the "%d":
scanf("%d ", &amountofstudents);
/* ^ */
/* | */
/* Note space */
Instead of gets, you should be using fgets:
fgets(studentinfo, sizeof(studentinfo), stdin);
And finally, always check for errors!

a potential issue is the scanf/gets combo. use instead fgets() and convert when appropriate to integer using atoi() it is also good to do a sanity check on what is returned from strtok (it is never good to assume anything about input)
char* token = strtok(studentinfo, " ");
if ( strlen(token) < sizeof(fname[i]) )
{
strcpy(fname[i], token);
...
you have also declared your strings as integer arrays, they should be char
e.g. char fname[50];

The problem you have is that you have declared three variables (fname, lname, and grade) as char[] (arrays) (well, that is the type you meant to use), but you want to prompt for and keep around a bunch of students information. And you later try to copy from strtok() into what you want to be a char[], but since you dereferenced fname[i] (lname[i], grade[i]), they are of type char, rather than char[].
You will need stdlib.h for exit,
#include <stdio.h>
#include <stdlib.h> //for exit
#include <string.h>
//#include <math.h> //you don't need this, yet
#define STUDLIMIT (100)
You can either create an array of fname[], lname[], grade[], (see here: http://www.cs.swarthmore.edu/~newhall/unixhelp/C_arrays.html),
int main(void)
{
//int menuswitch=1; //you aren't using this
int amountofstudents;
char studentinfo[100];
char fname[STUDLIMIT][50];
char lname[STUDLIMIT][50];
char grade[STUDLIMIT][50];
int ndx;
printf("Enter Amount of Students: ");
if( (fscanf(stdin,"%d ", &amountofstudents)<=0)
|| (amountofstudents<1) || (amountofstudents>STUDLIMIT) )
{
printf("need %d to %d studends\n",1,STUDLIMIT); exit(0);
}
for (ndx=0;ndx<amountofstudents;ndx++)
{
printf("Student: "); fflush(stdout);
fgets(studentinfo,sizeof(studentinfo),stdin);
strcpy(fname[ndx], strtok(studentinfo, " "));
strcpy(lname[ndx], strtok(NULL, " "));
strcpy(grade[ndx], strtok(NULL, " "));
}
}
Or you can create a struct(ure) to hold the entered student information, and instantiate an array of these student records, one for each student you enter and store,
typedef struct student
{
char fname[50];
char lname[50];
char grade[50];
} StudentObj;
int StudentCopy(StudentObj*sp,char*fname,char*lname,char*grade)
{
if(!sp || !fname || !lname || !grade ) return -1;
strcpy(sp->fname, fname);
strcpy(sp->fname, lname);
strcpy(sp->fname, grade);
}
StudentObj students[STUDLIMIT];
int main(void)
{
//int menuswitch=1; //you aren't using this
int amountofstudents;
char studentinfo[100];
char fname[50];
char lname[50];
char grade[50];
int ndx;
printf("Enter Amount of Students: ");
if( (fscanf(stdin,"%d ",&amountofstudents)<=0)
|| (amountofstudents<1) || (amountofstudents>STUDLIMIT) )
{
printf("need %d to %d studends\n",1,STUDLIMIT); exit(0);
}
for (ndx=0;ndx<amountofstudents;ndx++)
{
printf("Student: "); fflush(stdout);
fgets(studentinfo,sizeof(studentinfo),stdin);
strcpy(fname, strtok(studentinfo, " "));
strcpy(lname, strtok(NULL, " "));
strcpy(grade, strtok(NULL, " \n\r"));
StudentCopy(&(students[ndx]),fname,lname,grade);
}
}

Related

How to parse through a string of STDIN word by word in C

I would like to read standard input of a command and its argument in a C program, for instance:
ATTACK 50 30
I would like my program to parse through the input using whitespace and assign each word to a variable but right now I would just like to print each word. However, when I tried the program only returned ATTACK and not ATTACK 50 30.
I tried:
int main(){
// Grid size declaration //
int *x, *y;
char command[20];
char user_input[100];
scanf("%s", user_input);
printf("%s", user_input);
return 0;
}
As I said I used ATTACK 50 30 as my STDIN but my printf function only returned ATTACK. I thought of maybe using a while loop to keep scanning until the character interpreted is the return key (which I believe in this case would just be the null character?). I tried it using the code below:
int main(){
// Grid size declaration //
int *x, *y;
char command[20];
char user_input[100];
while(scanf("%s", user_input)!="\0"){
scanf("%s", user_input);
printf("%s", user_input);
}
return 0;
}
This did not work, the error produced declared I was comparing a pointer to an integer.
Since you are dealing with stdin it would probably be a better idea to utilize the fgets function in lieu of the scanf function, and then parse the inputted line of data utilizing the strtok string function.
Utilizing that strategy, following is a snippet of code allowing for the parsing of entered text where each word or data group is identified.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 64
int main()
{
char line[MAX + 1];
const char delim[2] = " ";
char * wd;
printf("Enter some text or type \"quit\" to end: ");
while (fgets(line, MAX, stdin))
{
line[strlen(line) - 1] = ' '; /* Replace newline character at the end with a space */
wd = strtok(line, delim);
while (wd != NULL)
{
if (strcmp(wd, "quit") == 0)
{
return 0;
}
printf("%s\n", wd);
wd = strtok(NULL, delim);
}
printf("Enter some text or type \"quit\" to end: ");
}
return 0;
}
Testing out this code utilizing your text example yielded the following terminal output.
#Dev:~/C_Programs/Console/ParseWord/bin/Release$ ./ParseWord
Enter some text or type "quit" to end: ATTACK 50 30
ATTACK
50
30
Enter some text or type "quit" to end: quit
#Dev:~/C_Programs/Console/ParseWord/bin/Release$
This is just a springboard from where you might go, but test that out and see if it meets the spirit of your project.

using malloc for char variable can not take input data character

I am trying to implement DMA for char variable. But I am unable to take input. I tried with all the possible cases I know:
//gets(ptr_name);
//scanf("%[^\n]", &ptr_name);
//fgets(ptr_name, name, stdin);
But I can't even enter input data for the character variable ptr_name. I want to take input as "string with space" as input value. How to solve this problem?
And then how to print the entered name in the screen?
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int main()
{
char* ptr_name;
int name, i;
printf("Enter number of characters for Name: ");
scanf("%d",&name);
ptr_name = (char*)malloc(name);
printf("Enter name: ");
//gets(ptr_name);
//scanf("%[^\n]", &ptr_name);
//fgets(ptr_name, name, stdin);
printf("\n Your name is: ");
puts(ptr_name);
free(ptr_name);
return 0;
}
scanf("%d", ...) does not consume the enter so the next scanf() gets an empty string.
you can use getchar() to consume the enter.
Also, you need to allocate additional byte for the zero at the end of the string / string terminator. See the + 1 in malloc().
As for your questions, your commented scanf() had & before argument 2 which isn't expected (char ** vs. char *) but other than that it will allow spaces in strings. puts() will print the entered name, alternatively you can modify the above printf() to print the name, e.g: printf("\n Your name is: %s", ptr_name);
Lastly, please consult Specifying the maximum string length to scanf dynamically in C (like "%*s" in printf) for dynamically limiting the input size, avoiding buffer overflow.
DISCLAIMER: The following is only "make it work" version of the program above and is not intended for real life use without appropriately checking return codes and limiting the input size:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* ptr_name;
int name, i;
printf("Enter number of characters for Name: ");
scanf("%d",&name);
getchar();
ptr_name = (char*)malloc(name + 1);
printf("Enter name: ");
scanf("%[^\n]", ptr_name);
printf("\n Your name is: ");
puts(ptr_name);
free(ptr_name);
return 0;
}
if you want to get input with spaces you need to use getline():
getline(&buffer,&size,stdin);
here an example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* ptr_name;
int len;
printf("Enter number of characters for Name: ");
scanf("%d",&len);
ptr_name = (char*)malloc(len);
printf("Enter name: ");
getline(&ptr_name, &len, stdin);
printf("\n Your name is: %s", ptr_name);
free(ptr_name);
return 0;
}

Passing String to Main from a function

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void get_name();
void display_name(char *fullname);
int main(void)
{
char first[80];
char second[80];
char *fullname[80];
get_name();
display_name(*fullname);
system("pause");
return 0;
}
void get_name()
{
char first[80];
char second[80];
char *fullname[80];
printf("Please enter first name: ");
scanf("%s", &first);
printf("\nPlease enter last name: ");
scanf("%s", &second);
strcpy(*fullname, first);
strcat(*fullname, " ");
strcat(*fullname, second);
printf("\n\nFull name is : %s ", *fullname);
}
void display_name(char *fullname)
{
int index;
char check;
int count=0;
printf("\n\nFull name is : %s ", fullname); //check to see if string is passes correctly
for(index=0; fullname[index] != '\0'; index++)
{
check=fullname[index];
if(check != ' ')
{
count++;
}
}
printf("\n\nNumber of characters in string is: %i\n", count);
}
im trying to send the string from get_name() to display name to count the number of characters. Everytime i pass the string, its comes out as gibberish. Am i passing wrong? I need to use one function to get the first and last name and concatenate the full name, then use another function to count the number of characters.
You're using pointers and scanf quite wrongly.
First of all scanf argument to for %s is supposed to be an array of characters. Remember that the array is in fact the pointer to the array.
Second you declare fullname to be an array of 80 pointers which is probably not what you want to do. Especially when you don't allocate the space for the string.
Instead it should be something like:
void get_name()
{
char first[80];
char second[80];
char fullname[80]; // an array of chars instead of pointers
printf("Please enter first name: ");
scanf("%s", first); // not taking the address of first - is already an address
printf("\nPlease enter last name: ");
scanf("%s", second); // not taking the address of second - is already an address
strcpy(fullname, first); // don't dereference fullname
strcat(fullname, " "); // don't dereference fullname
strcat(fullname, second); // don't dereference fullname
printf("\n\nFull name is : %s ", fullname); // don't dereference fullname
}
The declarations of variables are local to the scope where they are declared.
IOW when you declare first, second and fullname in your function get_name, they are local to that function. In order to pass the value outside of the function you have two, no three ways to do this starting with the worst way:
(1) declare the variable global, i.e. outside of main then share that variable in your function(s).
(2) declare the variable in main but pass it to the function who then fills in the string
int main()
{
char fullname[80];
get_name(fullname,sizeof(fullname)); // good to tell function avail size
...
void get_name(char* fullname, size_t length)
{
...
(3) Allocate memory on the heap, heap memory can be passed around between functions via a pointer
int main()
{
char* fullname = NULL;
get_name(&fullname);
...
void get_name(char** fullname)
{
*fullname = malloc(80);
...
EDIT
In order to read strings from the keyboard it is better to use fgets()
char buffer[128];
if (fgets(buffer,sizeof(buffer),stdin) != NULL) {
// remove the \n
char* p = strchr(buffer,'\n');
if ( p != NULL ) {
*p = '\0';
}
}
Using scanf reading from the keyboard is to be avoided, if you need to extract information use instead sscanf on the string read with fgets

Scanf input for char works once then is blank

I want to reference the input of name throughout the code I am writing, but for some reason after I use it successfully in the first printf(), the second printf() does not print the name.
int main()
{
char name[50];
char q1[1];
printf( " What is your name?\n");
scanf("%s", name);
printf( " Hi %s, Do you want to have some fun? [Y/N]\n",name);
scanf("%s",q1);
if(strcmp(q1,"Y") == 0||strcmp(q1,"y")==0)
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine"); goto endgame;
}
printf( "So %s, it's time to get started\n", name);
endgame:
getchar();
return 0;
}
The output for the entry 'Nick' is:
Hi Nick, do you want to have some fun? Awesome let's play So ,
let's get started
I would expect it to say:
So Nick, let's get started.
but for some reason char name is blank after it is used correctly the first time.
The problem, (as I assumed correctly) is with char q1[1]; and then using it like
scanf("%s",q1);
It is causing memory boundary overrun, because, a one-char array is not sufficient to hold a string of having only one element, as it lacks the space for the null-terminator required for the string. This invokes undefined behaviour.
Instead,
change char q1[1]; to char q1;
change scanf("%s",q1); to scanf(" %c", &q1);
change if(strcmp(q1,"Y") == 0||strcmp(q1,"y")==0) to if((q1 =='Y') || q1 == 'y')
That said, as a note,
The recommended signature of main() is int main(void).
To avoid the possibility of buffer overflow by longer input(s), it's better to limit the length of the input with scanf() by writing like
scanf("%49s", name);
Expand the size of your q1 buffer. scanf("%s", q1) doesn't have enough room to store the input. Remember that C uses a null character '\0' to terminate strings. If you don't account for that, the buffer could overrun into other memory causing undefined behavior. In this instance, it's probably overwriting memory allocated to name, so name ends up pointing to "\0ick". This causes printf(%s), which looks for '\0' to know when to stop printing, to think that the string is shorter than it really is.
The code works perfectly if you expand the buffer:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char name[50];
char q1[50];
printf( " What is your name?\n");
scanf("%49s", name);
printf( " Hi %s, Do you want to have some fun? [Y/N]\n",name);
scanf("%49s",q1);
if(strcmp(q1,"Y") == 0||strcmp(q1,"y")==0)
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine");
}
printf( "So %s, it's time to get started\n", name);
getchar();
return 0;
}
Output:
What is your name?
Nick
Hi Nick, Do you want to have some fun? [Y/N]
y
Awesome, let's play!
So Nick, it's time to get started
Note that I've added the qualifier %49s to avoid buffer overruns like this.
You could also circumvent the need for another string entirely by changing char q1[50] and scanf("%49s") to simply char q1 and scanf("%c%*c", &q1) (note the "address of" operator because q1 is no longer a pointer).
You'll probably even get a performance gain from this (albeit small), because strings are notorious memory hoggers. Comparing a single character is usually preferred over comparing strings.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char name[50];
char q1;
printf( " What is your name?\n");
scanf("%49s%*c", name);
printf( " Hi %s, Do you want to have some fun? [Y/N]\n",name);
scanf("%c%*c",&q1);
if(q1 == 'Y' || q1 == 'y')
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine");
}
printf( "So %s, it's time to get started\n", name);
getchar();
return 0;
}
if(q1 == 'Y' || q1 == 'y')
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine");
}
printf( "So %s, it's time to get started\n", name);
getchar();
return 0;
}
If you go this route, you have to ignore the enter key using the format specifier %*c because pressing enter sends a key to the stream as well.

Why is this string empty?

I have this little problem, and can't solve it.
The thing is that I'm trying to load all four strings in the function LOAD by asking the user to insert it. Everything seems fine and I don't get any compiler error. But eaf stays empty. I tried many ways, even replacing scanf with gets, gets_s, fgets, but nothing changes.
#include <conio.h>
#include <stdio.h>
#include <string.h>
void LOAD(char eaf[], char initials[], char finals[], char symbols[]);
int COMPARE(char s[], char eaf[]);
int main()
{
char eaf[10],initials[1],finals[10],symbols[5];
LOAD(eaf, initials, finals, symbols);
return 0;
}
void LOAD(char eaf[], char initials[], char finals[], char symbols[])
{
printf("Insert states of the optimized AFD\n");
scanf( " %s", eaf);
printf("Insert AFD initial state\n");
do
{
scanf( " %s", initials);
} while (COMPARE(initials, eaf));
printf("Insert final state(s)\n");
do
{
scanf( " %s",finals);
} while (COMPARE(finals, eaf));
printf("Insert the language symbols\n");
scanf( " %s",symbols);
}
int COMPARE(char s[], char eaf[])
{
int i;
char *ptr;
for(i; i < strlen(s); i++){
printf("%d\n", i);
while(ptr==NULL){
ptr = strchr(eaf, *s);
}
}
if (ptr == NULL) return 1;
else return 0;
}
What am I doing wrong? This is just a small part of a bigger program, but the rest of it is useless because eaf is empty. I thought that the problem was using scanf, but as I said other functions didn't work as well. I hope anyone can help me. Thanks
edit: I checked by strlen(eaf)
Using "scanf" for input is dangerous, and you've walked right into that danger. You're allowing it to overwrite the contents of "eaf" when you ask it to read initials as a string and it adds the terminating 0.
Ultimately, the string is empty because you got your array dimensions wrong. You gave "initials" an array size of 1 which does not provide space for the trailing '\0' C-string terminator.
See the live demo of this code on ideone:
#include <stdio.h>
void report(char* eaf, char* initials, char* foo)
{
printf("eaf = %p, initials = %p, foo = %p\n", eaf, initials, foo);;
printf("*eaf = %d, *initials = %d, *foo = %d\n", eaf[0], initials[0], foo[0]);
}
void load(char eaf[], char initials[], char foo[])
{
printf("load\n");
report(eaf, initials, foo);
printf("Enter EAF\n");
scanf(" %s", eaf);
report(eaf, initials, foo);
printf("Enter initial state\n");
scanf(" %s", initials);
report(eaf, initials, foo);
}
int main(int argc, const char* argv[])
{
char eaf[10], initials[1], foo[10];
report(eaf, initials, foo);
load(eaf, initials, foo);
report(eaf, initials, foo);
return 0;
}
You should have walked thru this in the debugger and watched the values of "eaf" and "initials" to see what happened as you progressed.
Do you have to write this program in C? It seems that using a scripting language such as perl or python might be easier for you.
Here is a working C method to start solving the problem, note that I didn't actually fix the problem, but it will make it easier to see it.
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
void report(char* eaf, char* initials, char* foo)
{
printf("eaf = %p, initials = %p, foo = %p\n", eaf, initials, foo);;
printf("*eaf = %d, *initials = %d, *foo = %d\n", eaf[0], initials[0], foo[0]);
}
void load(const char* label, const char* into, size_t intoSize)
{
assert(intoSize > 1); // can't store a string in 1 character.
printf("%s\n", label);
char input[1024] = "";
fgets(input, sizeof(input), stdin);
size_t len = strlen(input);
// strip trailing \n off.
if (len > 0 && input[len - 1] == '\n') {
input[--len] = 0;
}
// abort on empty input
if (len <= 0) {
fprintf(stderr, "Invalid input - terminated.\n");
exit(1);
}
if (len >= intoSize) {
fprintf(stderr, "Invalid input - length was %u, limit is %u\n", len, intoSize - 1);
exit(2);
}
strncpy(into, input, intoSize);
}
int main(int argc, const char* argv[])
{
char eaf[10], initials[1], foo[10];
report(eaf, initials, foo);
load("Insert states of the optimized AFD", eaf, sizeof(eaf));
report(eaf, initials, foo);
load("Insert initial AFD state", initials, sizeof(initials));
report(eaf, initials, foo);
printf("eaf = %s\ninitials = %s\n", eaf, initials);
return 0;
}
See live demo on ideone here.
You're format string in scanf is most likely the culprit; you have an extra space before the %s.
Change:
scanf( " %s", eaf);
to
scanf( "%s", eaf);
for all your scanf's.
Its not putting your input into eaf because it is looking for a string of the format " blahblahblah" (note the space in the beginning) instead of "blahblahblah"
Edit
Disregard the above,
whitespace: Any whitespace characters trigger a scan for zero or more whitespace characters. The number and type of whitespace characters do not need to match in either direction.
Also, you should initialize i in your COMPARE function (I don't understand how you're not getting an angry warning from your compiler), I ran your code with no modifications and strlen(eaf) returned the correct count (just after the scanf).

Resources