I'm trying to write a program that receives strings using fgets, but for some reason I can't get it to go past the user input stage. The input should stop once the user enters a "blank line", ie. the Enter key (\n) but even when this key is pressed the loop continues.
Here's the problematic part of my code:
char array[100][256];
for (int i = 0; array[i] != '\n'; i++)
{
fgets(array[i], 256, stdin);
}
100 and 256 represent the maximum amount of lines and chars expected respectively.
Does anyone know where I went wrong?
Here is your code fixed with minimal changes, explanations in comments. Note that this is not a very good way to solve your problem, long lines for example may not behave as you want (they will get split at several array lines).
char array[100][256];
memset(array, 0, sizeof array); // initialize the memory
int i = 0;
while(i<100) // avoid overflow of lines, also while may be clearer than for loop
{
if(!fgets(array[i], 256, stdin)) break; // detect read failure
if(array[i][0] == '\n') break; // got empty line
// Note [0] above to test first char of line i
++i;
}
if (i==100) { /* too many lines */ }
else if (array[i][0] == 0) { /* read failure */ }
else { /* indexes 0...i-1 contain data, index i contains empty line */ }
Related
So far I have been using if statements to check the size of the user-inputted strings. However, they don't see to be very useful: no matter the size of the input, the while loop ends and it returns the input to the main function, which then just outputs it.
I don't want the user to enter anything greater than 10, but when they do, the additional characters just overflow and are outputted on a newline. The whole point of these if statements is to stop that from happening, but I haven't been having much luck.
#include <stdio.h>
#include <string.h>
#define SIZE 10
char *readLine(char *buf, size_t sz) {
int true = 1;
while(true == 1) {
printf("> ");
fgets(buf, sz, stdin);
buf[strcspn(buf, "\n")] = 0;
if(strlen(buf) < 2 || strlen(buf) > sz) {
printf("Invalid string size\n");
continue;
}
if(strlen(buf) > 2 && strlen(buf) < sz) {
true = 0;
}
}
return buf;
}
int main(int argc, char **argv) {
char buffer[SIZE];
while(1) {
char *input = readLine(buffer, SIZE);
printf("%s\n", input);
}
}
Any help towards preventing buffer overflow would be much appreciated.
When the user enters in a string longer than sz, your program processes the first sz characters, but then when it gets back to the fgets call again, stdin already has input (the rest of the characters from the user's first input). Your program then grabs another up to sz characters to process and so on.
The call to strcspn is also deceiving because if the "\n" is not in the sz chars you grab than it'll just return sz-1, even though there's no newline.
After you've taken input from stdin, you can do a check to see if the last character is a '\n' character. If it's not, it means that the input goes past your allowed size and the rest of stdin needs to be flushed. One way to do that is below. To be clear, you'd do this only when there's been more characters than allowed entered in, or it could cause an infinite loop.
while((c = getchar()) != '\n' && c != EOF)
{}
However, trying not to restructure your code too much how it is, we'll need to know if your buffer contains the newline before you set it to 0. It will be at the end if it exists, so you can use the following to check.
int containsNewline = buf[strlen(buf)-1] == '\n'
Also be careful with your size checks, you currently don't handle the case for a strlen of 2 or sz. I would also never use identifier names like "true", which would be a possible value for a bool variable. It makes things very confusing.
In case that string inside the file is longer that 10 chars, your fgets() reads only the first 10 chars into buf. And, because these chars doesn't contain the trailing \n, function strcspn(buf, "\n") returns 10 - it means, you are trying to set to 0 an buf[10], so it is over buf[] boundaries (max index is 9).
Additionally, never use true or false as the name of variable - it totally diminishes the code. Use something like 'ok' instead.
Finally: please clarify, what output is expected in case the file contains string longer than 10 characters. It should be truncated?
Basically, my code is supposed to encrypt a sentence(or word) by taking out all the even numbered index values(starting from 0) and the odd numbered index values and placing the even index values before the odd index values.
For example, a word like "test" (0)t(1)e(2)s(3)t should be printed as (0)t(2)s(1)e(3)t or "tset". There aren't supposed to be any numbers printed, I just used them to show the odd and even index values.
My code works sometimes, depending on where it is run. I seem to be getting differing results between code blocks and an online compiler I tried. I suspect there must be a larger issue to blame for this inconsistency. Can anyone help me to see what I'm doing wrong so I can finally understand and rectify my errors?
I have tried using one counter to input both even and odd index values into one array but I was having errors with that as well so I decided to put them into separate arrays and then use strcat to combine them. Is there a way to make it work with the method I've shown in my code or should I go back to the previous method?
#include <stdio.h>
#include <string.h>
#define SIZE 1000
int main()
{
char message[SIZE];
char even[SIZE];
char odd[SIZE];
int length,j=0;
printf("Enter a word or sentence.\n");
fgets(message,SIZE,stdin);
printf("Your message is: %s\n",message);
message[strcspn(message, "\n")] = 0;
length=strlen(message);
printf("The length of the message is: %d\n",length);
for(int i=0;i<length;i+=2){
even[i/2]=message[i];
// printf("%c\n",even[i/2]);
}
for(int i=1;i<length;i+=2){
odd[j]=message[i];
j++;
}
printf("The even letters are: %s\n",even);
printf("The odd letters are: %s\n",odd);
strcat(even,odd);
printf("%s",even);
/*printf("\nFInalyy.");
for(i=0;i<=count;i++)
for(j=i+1;j<=count;j++){
if(strcmp(allmessages[i],allmessages[j])>0){
strcpy(temp,allmessages[i]);
strcpy(allmessages[i],allmessages[j]);
strcpy(allmessages[j],temp);
}
}
printf("The original messages in alphabetical order are: ");
for(i=0;i<=count;i++)
puts(allmessages[i]);*/
return 0;
}
It works perfectly when I type in words like "test" or "sentence". Sometimes I type in sentences like "this is a test sentence" and it would work perfectly then one time it would print out some random garbage letters along with the encrypted sentence. I would like to know how to fix this and to understand why it works perfectly with the same entry a few times then just stops. I used https://www.onlinegdb.com/online_c_compiler to test it the last few times so my results are based on that.
Successful Result:
Unsuccessful Result using the same entry:
You can take a slightly shorter approach by simply using two indexes to build your encrypted string (like evenstart and oddstart) based on the total length of the message entered by the user. evenstart = 0; and oddstart = (msglen + 1) / 2;
Then just loop over the characters in the message entered by the user writing even characters at encrypt[evenstart++] and odd characters at encrypt[oddstart++]. (don't forget to nul-terminate encrypt if you will be using it as a string for output purposes.
Putting it together you could do:
#include <stdio.h>
#include <string.h>
#define MAXC 1024
int main (void) {
char message[MAXC],
encrypt[MAXC];
size_t len, evenstart = 0, oddstart;
fputs ("enter message: ", stdout);
if (!fgets (message, MAXC, stdin)) { /* validate message entered */
fputs ("(user canceled input)\n", stdout);
return 1;
}
message[(len = strcspn(message, "\r\n"))] = 0; /* trim '\n', get len */
oddstart = (len + 1) / 2; /* get oddstart (add 1 before divide) */
for (size_t i = 0; i < len; i++) /* loop over each char */
if (i & 1) /* if odd, write char at oddstart */
encrypt[oddstart++] = message[i];
else /* if even, write at evenstart */
encrypt[evenstart++] = message[i];
encrypt[len] = 0; /* nul-terminate */
printf ("message : '%s'\nencrypt : '%s'\n", message, encrypt);
}
(note: you can use i % 2 to check even/odd if you like, or simply i & 1 -- in binary, if the ones-bit is 1 it's odd, otherwise its even -- up to you)
Example Use/Output
$ ./bin/encrevenodd
enter message: tes
message : 'tes'
encrypt : 'tse'
$ ./bin/encrevenodd
enter message: test
message : 'test'
encrypt : 'tset'
$ ./bin/encrevenodd
enter message: tests
message : 'tests'
encrypt : 'tsset'
$ ./bin/encrevenodd
enter message: my dog has fleas
message : 'my dog has fleas'
encrypt : 'm o a laydghsfes'
Look things over and let me know if you have questions.
Strings in C are terminated by the null byte ('\0' or ascii value 0) just so it knows where the string ends. Since the even and odd character arrays were not terminated by the null byte, strcat does not know when to stop appending characters. So the garbage values you see is because strcat keeps appending characters until you luckily find a 0 in memory.
Strings in C do not know their length, they're just pointers. Strings must be terminated with a null character in order for functions like printf and strlen to know when to stop.
char even[SIZE];
char odd[SIZE];
At this point even and odd both contain whatever garbage was in memory at that time.
for(int i=0;i<length;i+=2){
even[i/2]=message[i];
}
for(int i=1;i<length;i+=2){
odd[j]=message[i];
j++;
}
Now the beginning of odd and even have been filled in, but they were not null terminated.
printf("The even letters are: %s\n",even);
printf("The odd letters are: %s\n",odd);
These will start at where even and odd point, print the characters you've put in there, and then keep on printing whatever garbage was in memory until they happen to hit a null character.
The fix is to either zero out the memory with memset.
memset(even, '\0', SIZE);
memset(odd, '\0', SIZE);
Or ensure that even and odd are null terminated once you're done with them.
for(i=0;i<length;i+=2){
even[i/2]=message[i];
}
even[i/2] = '\0';
for(i=1;i<length;i+=2){
odd[j]=message[i];
j++;
}
odd[j] = '\0';
Side note, even and odd loops can be done with the same technique.
for( i=0,j=0; i<length; i+=2,j++ ) {
even[j]=message[i];
}
even[j] = '\0';
for( i=1,j=0; i<length; i+=2,j++ ) {
odd[j]=message[i];
}
odd[j] = '\0';
Then we can observe that the only difference is where we start reading message. That means we can put this in a function and always remember to null terminate the result.
void copy_every_other_character(const char *src, char *dst) {
int i,j,length = 0;
length = strlen(src);
for( i=0,j=0; i<length; i+=2,j++ ) {
dst[j] = src[i];
}
dst[j] = '\0';
}
copy_every_other_character(message, even);
copy_every_other_character(message+1, odd);
Adding 1 to message means copy_every_other_character will get a pointer to the second character of message and go on from there. If message is 01234 it will see 1234.
I was creating a word guessing game that reads a text file line per line until it finds a random word and stores it in a string (word). Then the user enters letters until all the letters of the stored word are revealed.
So far it works perfectly but every time it's the very first word that is read some unknown characters get stored at the beginning of char word[20].
Note that I use C mobile app and it uses clang 6.0 compiler I think. So does the error come from my code or it come from their app? (which I love).
Here is the FULL, clearer code:
//guess the right word
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define NB_OF_WORDS 3 //number of words in text file
main() {
char begin, word[20] = { 0 }, guessedletter;
int num, rightletter, success;
int i = 0;
int show[20] = { 0 };//shown letters
FILE *ressource = NULL;
srand(time(NULL));
if ((ressource = fopen("ressource.txt", "r")) == NULL) {
fprintf(stderr, "Error ressource.txt");//open in read only mode
exit(EXIT_FAILURE);
}
printf("Welcome to Word Guess, a word guessing contest.\n"
"You have to find the letters of a word and guess what word it is.\n"
"Begin? (y/n)\n");
while ((begin = getchar()) == 'y') { //game loop
/*reinitializations*/
fseek(ressource, 3, SEEK_SET);//replaces rewind(ressource)
success = FALSE;
rightletter = 0;
num = 0;
num = rand() % NB_OF_WORDS; //random number between 0 and NB-1
for (i = 0; i < 20; i++) {
show[i] = FALSE;
word[i] = 0;
}
i = 0;
/*end of reinitializations*/
while (i <= num) {//reads line by line until random word is stored
if (fgets(word, 20, ressource) == NULL) {
fprintf(stderr, "fgets did not work");
exit(EXIT_FAILURE);
}
i++;
}
printf("%s", word);//here is just for testing if the read word is well read. Which isn't the case if num=0
for (i = 0; i < 20; i++)
if (word[i] == '\n')
word[i] = '\0';//adds zero character to show where the string ends
while (!success) { //guessing loop
printf("\nWrite a letter: ");
scanf("%c", &guessedletter);
printf("\n");
for (i = 0; word[i] != '\0'; i++) { //compares entered letter to letter from string. If they match...
if (word[i] == guessedletter) {
if (!show[i])
rightletter++;//avoids letters entered twice from increasing count
show[i] = TRUE;
}
if (show[i])
printf("%c", word[i]);//...a letter is revealed...
else
printf("*");//else it stays hidden
}
if (rightletter == strlen(word))
success=TRUE;//if all the right letters found (same number of letters found as number of letters in the words) you win
}
printf("\nCongratulations you have won!\nDo you want to replay? (y/n)");
getchar();//clears newline character
}
fclose(ressource);
return 0;
}
When num=0, I get a weird sign before the printed word as if the first characters of the text file were not supposed to be there...
Moreover during the guessing game if the word to guess (word[20]) is "annoying" let's say and that it's the first word from "ressource.txt" (num=0). The word will print like so on the screen once I guessed all the letters: ***annoying. Which does not happen with ANY other words from the list.
I'm new to this site and post from my phone... sorry for any mistakes.
EDIT: removed fgetc for fgets. Still get several unknown characters if fgets reads first line.
EDIT 2: added the whole code, translated mot[20] into word[20], added error testing
EDIT 3: Replacing rewind(ressource); by fseek(ressource, 3, SEEK_SET); solved the problem. Which means there are indeed three unknown characters at the beginning of the text file
There are multiple problems in the posted code:
You only posted a fragment of code, the rest of the function could cause problems that cannot be analysed from what you posted. Please post the complete code to a minimal program exhibiting the problem.
You do not test for end of file in the while loop. If you try to skip more lines than are present in the file, this loop will run indefinitely.
You do not test the return value of fgets(): if by chance you skipped the full contents of the file in the preceding while loop, fgets() will fail and return NULL, leaving mot in an undetermined state, causing unexpected behavior in the subsequent printf.
EDIT: the modified code still does not check the return value of fgets().
EDIT: thank you for posting the full code, but modifying the question this way makes this answer and the comments irrelevant.
Your dictionary file ressource.txt seems to start with a BOM (Byte Order Mark) encoded in UTF-8. I am guessing it contains French words including some with accented letters such as reculées and encodées... Your text editor saved it encoded in UTF-8 with an extra code-point at the beginning for other programs to determine this encoding easily. The problem with that is your program does not handle UTF-8. It assumes each byte read from stdin represents a single character. It might by chance match some words with accented letters, but will more likely fail to find them.
I have an array of 15 strings (which don't all have to necessarily be used), and despite reading everywhere that gets should never be used, for some reason I believe it is most convenient for me for this program.
After prompting the user to specify how many rows and columns he wants, to create a matrix, I ask him to enter the matrix values, one row per line at a time. I do this using gets. Simultaneously, I want to scan through the string for the amount of spaces entered to ensure that the user is entering the appropriate amount of numbers that correspond to the amount of columns specified.
At the end I want to print out the second row that I entered.
You can assume rowone and colone are already defined, I just didn't copy that part of the code to save space.
int i=0, rowone, colone, sbar=0, inputs=0;
char matrixone[15][10000];
......
printf("input your matrix\n");
for (i=0;i<rowone;i++){
gets(matrixone[i]);
while(matrixone[i][inputs]!='\n'){
if (mplier[i][inputs] == ' '){
sbar++;
inputs++;
}
else
inputs++;
}
if (sbar>=colone||sbar<colone-1){
printf("Too many/too few inputs per line\n");
main();
}
sbar = 0;
inputs = 0;
}
puts(matrixone[2])
I get warnings when compiling and ultimately not even the chance to input the matrix as "Too many/too few inputs" always pops up.
Any help would be greatly appreciated, thank you!
Infinite loop.
Or more properly, eventually matrixone[i][inputs] accesses way beyond what it was read and maybe beyond the array itself. This leads to undefined behavior (UB).
gets() consumes, but does not save '\n'.
gets(matrixone[i]);
while(matrixone[i][inputs]!='\n'){ // why should this quit?
...
inputs++;
}
Instead, drop gets() as it is dropped from the language for 5 years now, use fgets(), check return value and look for the end of the string as well
if (fgets(matrixone[i], sizeof matrixone[i], stdin) {
while(matrixone[i][inputs] != '\0'){
...
}
}
Suggestion for OP (guessing OP's goal)
char *matrixone[15] = { 0 };
printf("input your matrix\n");
for (i=0;i<min(rowone,15);i++){
buf[1000];
if (fgets( buf, sizeof buf, stdin) == NULL) break;
buf[strcspn(buf, "\n")] = 0;
// qualify buf concerning ' ' , sbar, etc
...
matrixone[i] = strdup(buf);
}
I am a beginner learning C; so, please go easy on me. :)
I am trying to write a very simple program that takes each word of a string into a "Hi (input)!" sentence (it assumes you type in names). Also, I am using arrays because I need to practice them.
My problem is that, some garbage gets putten into the arrays somewhere, and it messes up the program. I tried to figure out the problem but to no avail; so, it is time to ask for expert help. Where have I made mistakes?
p.s.: It also has an infinite loop somewhere, but it is probably the result of the garbage that is put into the array.
#include <stdio.h>
#define MAX 500 //Maximum Array size.
int main(int argc, const char * argv[])
{
int stringArray [MAX];
int wordArray [MAX];
int counter = 0;
int wordCounter = 0;
printf("Please type in a list of names then hit ENTER:\n");
// Fill up the stringArray with user input.
stringArray[counter] = getchar();
while (stringArray[counter] != '\n') {
stringArray[++counter] = getchar();
}
// Main function.
counter = 0;
while (stringArray[wordCounter] != '\n') {
// Puts first word into temporary wordArray.
while ((stringArray[wordCounter] != ' ') && (stringArray[wordCounter] != '\n')) {
wordArray[counter++] = stringArray[wordCounter++];
}
wordArray[counter] = '\0';
//Prints out the content of wordArray.
counter = 0;
printf("Hi ");
while (wordArray[counter] != '\0') {
putchar(wordArray[counter]);
counter++;
}
printf("!\n");
//Clears temporary wordArray for new use.
for (counter = 0; counter == MAX; counter++) {
wordArray[counter] = '\0';
}
wordCounter++;
counter = 0;
}
return 0;
}
Solved it! I needed to add to following if sentence to the end when I incremented the wordCounter. :)
if (stringArray[wordCounter] != '\n') {
wordCounter++;
}
You are using int arrays to represent strings, probably because getchar() returns in int. However, strings are better represented as char arrays, since that's what they are, in C. The fact that getchar() returns an int is certainly confusing, it's because it needs to be able to return the special value EOF, which doesn't fit in a char. Therefore it uses int, which is a "larger" type (able to represent more different values). So, it can fit all the char values, and EOF.
With char arrays, you can use C's string functions directly:
char stringArray[MAX];
if(fgets(stringArray, sizeof stringArray, stdin) != NULL)
printf("You entered %s", stringArray);
Note that fscanf() will leave the end of line character(s) in the string, so you might want to strip them out. I suggest implementing an in-place function that trims off leading and trailing whitespace, it's a good exercise as well.
for (counter = 0; counter == MAX; counter++) {
wordArray[counter] = '\0';
}
You never enter into this loop.
user1799795,
For what it's worth (now that you've solved your problem) I took the liberty of showing you how I'd do this given the restriction "use arrays", and explaining a bit about why I'd do it that way... Just beware that while I am experienced programmer I'm no C guru... I've worked with guys who absolutely blew me into the C-weeds (pun intended).
#include <stdio.h>
#include <string.h>
#define LINE_SIZE 500
#define MAX_WORDS 50
#define WORD_SIZE 20
// Main function.
int main(int argc, const char * argv[])
{
int counter = 0;
// ----------------------------------
// Read a line of input from the user (ie stdin)
// ----------------------------------
char line[LINE_SIZE];
printf("Please type in a list of names then hit ENTER:\n");
while ( fgets(line, LINE_SIZE, stdin) == NULL )
fprintf(stderr, "You must enter something. Pretty please!");
// A note on that LINE_SIZE parameter to the fgets function:
// wherever possible it's a good idea to use the version of the standard
// library function that allows you specificy the maximum length of the
// string (or indeed any array) because that dramatically reduces the
// incedence "string overruns", which are a major source of bugs in c
// programmes.
// Also note that fgets includes the end-of-line character/sequence in
// the returned string, so you have to ensure there's room for it in the
// destination string, and remember to handle it in your string processing.
// -------------------------
// split the line into words
// -------------------------
// the current word
char word[WORD_SIZE];
int wordLength = 0;
// the list of words
char words[MAX_WORDS][WORD_SIZE]; // an array of upto 50 words of
// upto 20 characters each
int wordCount = 0; // the number of words in the array.
// The below loop syntax is a bit cyptic.
// The "char *c=line;" initialises the char-pointer "c" to the start of "line".
// The " *c;" is ultra-shorthand for: "is the-char-at-c not equal to zero".
// All strings in c end with a "null terminator" character, which has the
// integer value of zero, and is commonly expressed as '\0', 0, or NULL
// (a #defined macro). In the C language any integer may be evaluated as a
// boolean (true|false) expression, where 0 is false, and (pretty obviously)
// everything-else is true. So: If the character at the address-c is not
// zero (the null terminator) then go-round the loop again. Capiche?
// The "++c" moves the char-pointer to the next character in the line. I use
// the pre-increment "++c" in preference to the more common post-increment
// "c++" because it's a smidge more efficient.
//
// Note that this syntax is commonly used by "low level programmers" to loop
// through strings. There is an alternative which is less cryptic and is
// therefore preferred by most programmers, even though it's not quite as
// efficient. In this case the loop would be:
// int lineLength = strlen(line);
// for ( int i=0; i<lineLength; ++i)
// and then to get the current character
// char ch = line[i];
// We get the length of the line once, because the strlen function has to
// loop through the characters in the array looking for the null-terminator
// character at its end (guess what it's implementation looks like ;-)...
// which is inherently an "expensive" operation (totally dependant on the
// length of the string) so we atleast avoid repeating this operation.
//
// I know I might sound like I'm banging on about not-very-much but once you
// start dealing with "real word" magnitude datasets then such habits,
// formed early on, pay huge dividends in the ability to write performant
// code the first time round. Premature optimisation is evil, but my code
// doesn't hardly ever NEED optimising, because it was "fairly efficient"
// to start with. Yeah?
for ( char *c=line; *c; ++c ) { // foreach char in line.
char ch = *c; // "ch" is the character value-at the-char-pointer "c".
if ( ch==' ' // if this char is a space,
|| ch=='\n' // or we've reached the EOL char
) {
// 1. add the word to the end of the words list.
// note that we copy only wordLength characters, instead of
// relying on a null-terminator (which doesn't exist), as we
// would do if we called the more usual strcpy function instead.
strncpy(words[wordCount++], word, wordLength);
// 2. and "clear" the word buffer.
wordLength=0;
} else if (wordLength==WORD_SIZE-1) { // this word is too long
// so split this word into two words.
strncpy(words[wordCount++], word, wordLength);
wordLength=0;
word[wordLength++] = ch;
} else {
// otherwise: append this character to the end of the word.
word[wordLength++] = ch;
}
}
// -------------------------
// print out the words
// -------------------------
for ( int w=0; w<wordCount; ++w ) {
printf("Hi %s!\n", words[w]);
}
return 0;
}
In the real world one can't make such restrictive assumptions about the maximum-length of words, or how many there will be, and if such restrictions are given they're almost allways arbitrary and therefore proven wrong all too soon... so straight-off-the-bat for this problem, I'd be inclined to use a linked-list instead of the "words" array... wait till you get to "dynamic data structures"... You'll love em ;-)
Cheers. Keith.
PS: You're going pretty well... My advise is "just keep on truckin"... this gets a LOT easier with practice.