I have a text file with up to 100 IP addresses, 1 per line. I need to read each address, as a string, into an array called "list". First, I'm assuming that "list" will need to be a two-dimensional char array. Each IP address is 11 characters in length, 12 if you include '\0', so I declared list as follows:
char list[100][12];
Next, I attempt to use fgets to read the stream:
for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
for (j = 0; j < 12; j++)
fgets(&list[i][j], 12, stream);
count++;
}
To check to see if the strings were read properly, I attempt to output them:
for (i = 0; i < 5; i++)
{
for (j = 0; j < 11; j++)
printf("%c", list[i][j]);
printf("\n");
}
After running the program, it's clear something is wrong. Being a beginner, I'm not sure what, but I'm guessing I'm reading the file wrong. There are no errors. It compiles, but prints a strange address on two lines.
Edit:
I replaced the fgets code with this:
for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
fgets(list[i], 12, stream);
count++;
}
It now prints five strings, but they are "random" characters from memory.
First, reading:
for (j = 0; j < 12; j++)
fgets(&list[i][j], 12, stream);
You have a big problem right here. This is attempting to read a string into each successive character in your array.
All in all, I think you're making this a lot more complex than it needs to be. Think of your array as 100 strings, and fgets will work with a string at a time. That means reading can look something like this:
for (i=0; i<100 && fgets(list[i], 11, string); i++)
;
There is one other minor detail to deal with: fgets() normally retains the new-line at the end of each line. As such, you may need to leave room for 13 characters (11 for address, 1 for new-line, 1 for NUL terminator), or else you may want to read the data into a temporary buffer, and copy it to your list only after you've stripped off the new-line.
In your current code for printing the strings, you're working one character at a time, which can work, but is unnecessarily difficult. Several people have suggested using the %s printf conversion, which is fine in itself. To go with it, however, you have to simplify your indexing a bit. Printing the first six addresses would look something like this:
for (i=0; i<6; i++)
printf("%s", list[i]);
Your call to fgets reads up to 11 characters from the stream into the array. So you don't want to be calling that once for each character of each string.
Just think about those loops: with i=0 and j=0, it reads up to 11 characters to &list[0][0]. Then with i=0 and j=1, it reads another 11 characters to &list[0][1]. That's wrong for two reasons - it overwrites the result of the last call, and potentially it writes more bytes than list[0] can hold.
A newline character makes fgets stop reading, but it is considered a valid character and therefore it is included in the string copied to str.
You may be reading the first 12 characters in the first call the fgets, then the second call will catch the newline, then the third call gets the next line.
Try using fgets with a 15 character limit, and expanding your buffer.
The second loop is not necessary and it corrupts your memory. You should do something like this,
for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
fgets(&list[i][j], 12, stream);
count++;
}
To check to see if the strings were read properly, I attempt to output them:
for (i = 0; i < 5; i++)
{
printf("%s\n", list[i]);
}
for (i = 0; i < 100; i++)
{
if (feof(fp))
break;
fscanf(fp,"%s\n",list[i]);
}
Do not use feof() as your loop condition; it will not return true until after you've tried to read past the end of the file, meaning your loop will execute one time too many. Check the result of your input call (whether you use fgets() or fscanf()) to see if it succeeded, then check feof() if you got an error condition.
if (fgets(buffer, sizeof buffer, stream) != NULL)
{
// process the input buffer
}
else if (feof(stream)
{
// handle end of file
}
else
{
// handle read error other than EOF
}
fgets() reads entire strings, not individual characters, so you don't want to pass the address of each individual character in your string. Call it like so instead:
if (fgets(list[i], sizeof list[i], stream) != NULL)
{
// process input address
}
And now, for Bode's usual spiel about arrays and pointers...
When an array expression appears in most contexts, the type of the expression is implicitly converted from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element of the array. The exceptions to this rule are when the array expression is the operand of the sizeof or & operators, or it is a string literal that is being used as an initializer in a declaration. When you hear people say "arrays and pointers are the same thing", they're garbling that rule. Arrays and pointers are completely different animals, but they can be used interchangeably in some contexts.
Note that in the code above I passed list[i] as the first argument to fgets() without any decoration (such as the & operator). Even though the type of list[i] is "12-element array of char", in this context it is implicitly converted to type "pointer to char", and the value will be the address of list[i][0]. Note that I also passed that same expression to the sizeof operator. In that case, the type of the array expression is not converted to a pointer type, and the sizeof operator returns the number of bytes in the array type (12).
Just to nail it down:
Expression Type Implicitly converted to
---------- ---- ----
list char [100][12] char (*)[12] (pointer to 12-element array of char)
list[i] char [12] char *
list[i][j] char N/A
What all this means is that fgets() will read up to the next 12 characters (provided it doesn't hit a newline or EOF first) and store it starting at list[i][0]. Note that fgets() will write a terminating nul character (0) to the end of your string. Note also that if fgets() encounters a newline and there's room in the target array for it and the terminating nul, fgets() will store the terminating newline before the nul character. So if your input file has a line like
1.1.1.1\n
then the contents of your input buffer after the read will be "1.1.1.1\n\0xxx" where x is some random value. If you don't want the newline there, you can use the strchr() function to find it and then overwrite it with a 0:
char *newline;
...
if ((newline = strchr(input[i], '\n')) != NULL)
{
*newline = 0;
}
Since fgets() stops at the next newline, and since your input buffer is sized for 12 characters, it's possible for you to run into a situation where you have a newline as the next input character in the file; in that case, fgets() will write only that newline to the input buffer, so you'll have some empty entries, which is probably not what you want. You might want to add an extra byte to your input buffer in order to avoid that situation.
Putting it all together:
char list[100][13];
...
for (i = 0; i < 100; ++)
{
if (fgets(list[i], sizeof list[i], stream) != NULL)
{
char *newline = strchr(list[i], '\n');
if (newline != NULL)
*newline = 0;
printf("Read address \"%s\"\n", list[i]);
count++;
}
else if (feof(stream))
{
printf("Reached end of file\n");
break;
}
else
{
printf("Read error on input; aborting read loop\n");
break;
}
}
I wrote a function for reading lines. I think it should be safe.
Check : io_readline
https://github.com/arhuaco/junkcode/blob/master/junk/misc/atail.c
Related
It is a simple program to check whether string is palindrome or not. I made following code in the program.
When i compile it, there is no error but when i try to run the .exe file, i always get following message.
The fundamental problem here is that the loop exit condition can never be met, and this leads to an infinite loop:
(i != j || i != j - 1)
This condition is logically equivalent to:
!(i == j && i == j - 1)
which is obviously always true, so the loop continues indefinitely. The loop only needs to continue so long as j > i.
There is another problem here; namely that the dangerous function gets() should never be used. This function was deprecated in C99 and completely removed from the language in C11. One alternative is to use fgets() instead. Note that this function keeps the newline (if there is room in the buffer), so you will need to remove this after getting the input. Also, characters may be left in the input stream if the buffer is too small. For this reason, it would be better to declare a generously sized input buffer to reduce the risk of problems here. There is no reason not to use an input buffer holding 1000 characters, and I usually just use 4096 for something like this. Memory is cheap.
Further, there is a risk of undefined behavior in the posted code, since the input string may be empty. In this case, strlen(str) would be 0, so in the first execution of the loop body j would be decremented to -1. But the array access str[-1] is out of bounds, and leads to undefined behavior.
This problem can be fixed by checking that j is positive before the first decrement. Note that size_t is the correct type for array indices, as it is an unsigned integer type that is guaranteed to be able to hold any array index. Also note that the strlen() function returns a value of type size_t, not int.
Here is a modified version of the posted code. The size_t type is used for array indices. The length of the input string is stored in j, which is then decremented only if it is a positive value; this sets j to the index of the character preceding the null terminator so long as the input string is not an empty string. The loop continues while j is greater than i and the characters indexed by these values match. After the loop terminates, str[i] and str[j] should agree; if they do not, then the input was not a palindrome.
#include <stdio.h>
#include <string.h>
#define BUF_SZ 4096
int main(void)
{
char str[BUF_SZ];
printf("Enter string:\n");
fgets(str, sizeof str, stdin); // Never use gets()
str[strcspn(str, "\r\n")] = '\0'; // remove '\n'
size_t i = 0;
size_t j = strlen(str);
if (j > 0) {
--j;
}
while (i < j && str[i] == str[j]) {
++i;
--j;
}
if (str[i] == str[j]) {
puts("string is palindrome!!");
} else {
puts("string is not palindrome!!");
}
return 0;
}
In C I'm using this method from a serial library:
int serialport_read_until(int fd, char* buf, char until, int buf_max, int timeout)
{
char b[1]; // read expects an array, so we give it a 1-byte array
int i=0;
do {
int n = read(fd, b, 1); // read a char at a time
if( n==-1) return -1; // couldn't read
if( n==0 ) {
usleep( 1 * 1000 ); // wait 1 msec try again
timeout--;
if( timeout==0 ) return -2;
continue;
}
#ifdef SERIALPORTDEBUG
printf("serialport_read_until: i=%d, n=%d b='%c'\n",i,n,b[0]); // debug
#endif
buf[i] = b[0];
i++;
} while( b[0] != until && i < buf_max && timeout>0 );
buf[i] = 0; // null terminate the string
return 0;
}
The string that it is going to read is like this:
"111\r\n" (with a carriage + new line behind)
It is being printed out in Arduino using
serial.print("1");
serial.print("1");
serial.println("1");
Using the serialport_read_until method (char until is '\r\n'), I want to ensure that I am reading the entire buffer correctly.
Which of the following below does the char* buf look like in the end?
1) 111\r\n
2) 111\r\n\0
3) 111\0
4) 111
I need to figure out this part before I use sscanf method to convert the string into an integer correctly, but I'm not sure which to use:
sscanf(buf, "%d\r\n", &num); OR sscanf(buf, "%d", &num);
In addition, should I change the 2nd last line: buf[i] = 0; to buf[i-1] = 0; ?
It looks to me like you should expect 111\r\n\0. Note that the condition b[0] != until is checked after incrementing i, so when the newline character is received and the loop exits, i points to the next byte after \n. Then b[i]=0 stores a null byte there.
Note that this code appears to have a bug: if the until character is never received, the loop will run until i == buf_max and then store one byte more with the null terminator. So a total of buf_max+1 bytes are stored, meaning the following code would have a buffer overflow:
char mybuf[123];
serialport_read_until(fd, buf, 'x', 123, 42);
Unless the documentation says that buf_max should be one less than the size of the buffer, which would be counterintuitive and error-prone, the loop termination condition should probably say i+1 < buf_max or something similar.
Also, since i is checked at the end, even with this fix, the code will still store one byte if you pass in buf_max == 0 (but without the fix it will store two bytes). So that's another bug.
The char b[1]; declaration and accompanying comment is a little weird too. It would be more idiomatic to simply declare char b; and then just pass &b to read().
So if this is your code, there's more work to do on it. If it's someone else's code, I'd be very careful using this library, if this function is any indication of the quality.
Doing buf[i-1]=0 at the end would avoid the overflow, but would also mean that if the until character is not received, the last byte received would be lost. It would also break if you ever call the function with buf_max == 0. So that's not what you want.
If you're using sscanf, the question of whether or not there is trailing whitespace is irrelevant; sscanf("%d") will just ignore it. You should have a careful read through your library's documentation of sscanf. In particular, the way it handles whitespace is not always intuitive.
I'm reading in from a file that has a hex value on each line. It will look like this:
F0BA3240C
083FA52
45D3687AF
etc.
The hex values won't have the same length.
I have fgets reading from this file into a buffer and then a piece of code to get rid of the newline character. From there I put the string from the buffer into my data array. But before putting the string from the buffer into my data array, I'm attempting to compare the string from the buffer to the strings already stored in the data array and see if there is or isn't a match so I can update some counters. However, I'm having issues using strcmp and strncmp. Any help will be appreciated, thanks.
Relevant code:
char **data = NULL;
char data_buffer[100];
//program first goes through the file and determines amount of lines there are hence this variable
int count_line = 0;
...
data = malloc(count_line * sizeof(char *));
int f;
int i;
for(i=0; i<count_line; i++)
{
fgets(data_buffer, sizeof(data_buffer), fp);
...
//allocate space to store copy of line and add one for null terminator
data[i] = malloc(line_length + 1);
...
if(asdf != NULL)
{
//problem here. don't know how to compare stream from buffer and compare to all elements of data buffer
for(f=0; f<sizeof(data); f++)
{
if(strcmp(data[f], data_buffer) == 0)
there_was_a_match++;
}
}
...
//copy string from buffer into data array
strcpy(data[i], data_buffer);
}
Consider these lines:
for(f=0; f<sizeof(data); f++)
{
if(strcmp(data[f], data_buffer) == 0)
there_was_a_match++;
}
What is the value of sizeof(data)? Since data is of type char**, presumably
sizeof(data) is the size of a pointer in bytes, so some fixed integer value such as 4 or 8.
Now observe that the first time you encounter this loop within the "for i" loop,
i is 0 and data[0] is the only pointer in the array of pointers that has been allocated--
every other pointer in data is invalid.
So now what happens is, we do the first iteration of the inner loop: f is 0, so we
end up comparing the string we just read to itself.
On the next iteration, f is 1, we try comparing our latest string to data[1],
but data[1] has not yet been initialized,
ergo we have undefined behavior (such as a crash).
You might be better off if the f loop were like this:
for(f=0; f<i; ++f)
{
if(strcmp(data[f], data_buffer) == 0)
there_was_a_match++;
}
This way, you will compare the newest string (which you have just saved in data[i])
with only the strings that were already loaded.
There is one other thing that may be troublesome. Suppose your input consists of four
copies of the same string.
Then after you read the second copy and execute this loop, there_was_a_match will be 1;
after reading the third copy and executing that loop, there_was_a_match will be 3
(because it matches twice);
after reading the fourth copy and executing that loop, there_was_a_match will be 6.
I suspect these are not the results you want.
Perhaps you want to break out of the loop after finding the first match.
I can´t see problems with strcmp/strcpy, but:
for(f=0; f<sizeof(data); f++)
You can´t use sizeof, ie. it won´t result in the value count_line.
From the shown code, this could be enough to make it work.
Just compare to the previous allocated lines, which is i lines.
// for(f=0; f<sizeof(data); f++)
for(f=0; f<i; f++)
This seems like it should be a simple thing but after hours of searching I've found nothing...
I've got a function that reads an input string from stdin and sanitizes it. The problem is that when I hit enter without typing anything in, it apparently just reads in some junk from the input buffer.
In the following examples, the prompt is "input?" and everything that occurs after it on the same line is what I type. The line following the prompt echoes what the function has read.
First, here is what happens when I type something in both times. In this case, the function works exactly as intended.
input? abcd
abcd
input? efgh
efgh
Second, here is what happens when I type something in the first time, but just hit enter the second time:
input? abcd
abcd
input?
cd
And here is what happens when I just hit enter both times:
input?
y
input?
y
It happens to return either 'y' or '#' every time when I run it anew. 'y' is particularly dangerous for obvious reasons.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#define STRLEN 128
int main() {
char str[STRLEN];
promptString("input?", str);
printf("%s\n", str);
promptString("input?", str);
printf("%s\n", str);
return EXIT_SUCCESS;
}
void promptString(const char* _prompt, char* _writeTo) {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
cleanString(_writeTo);
return;
}
void cleanString(char* _str) {
char temp[STRLEN];
int i = 0;
int j = 0;
while (_str[i] < 32 || _str[i] > 126)
i++;
while (_str[i] > 31 && _str[i] < 127) {
temp[j] = _str[i];
i++;
j++;
}
i = 0;
while (i < j) {
_str[i] = temp[i];
i++;
}
_str[i] = '\0';
return;
}
I've tried various methods (even the unsafe ones) of flushing the input buffer (fseek, rewind, fflush). None of it has fixed this.
How can I detect an empty input so that I can re-prompt, instead of this annoying and potentially dangerous behavior?
This part of cleanString
while (_str[i] < 32 || _str[i] > 126)
i++;
jumps over \0 when the string is empty.
You should add _str[i] != '\0' into the loop's condition.
To detect an empty string, simply check it's length just after the input:
do {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
} while (strlen(_writeTo) < 2);
(comparing with two because of '\n' which fgets puts into the end of buffer)
Why do you have a bunch of variable names with leading underscores? That's nasty.
Anyway, the first thing you must do is check the return value of fgets. If it returns NULL, you didn't get any input. (You can then test feof or ferror to find out why you didn't get input.)
Moving on to cleanString, you have a while loop that consumes a sequence of non-printable characters (and you could use isprint for that instead of magic numbers), followed by a while loop that consumes a sequence of printable characters. If the input string doesn't consist of a sequence of non-printables followed by a sequence of printables, you will either consume too much or not enough. Why not use a single loop?
while(str[i]) {
if(isprint(str[i]))
temp[j++] = str[i];
++i;
}
This is guaranteed to consume the whole string until the \0 terminator, and it can't keep going past the terminator, and it copies the "good" characters to temp. I assume that's what you wanted.
You don't even really need to use a temp buffer, you could just copy from str[i] to str[j], since j can never get ahead of i you'll never be overwriting anything that you haven't already processed.
I have a problem with reading empty string in C. I want to read string from the following -
ass
ball
(empty)
cat
but when I use gets() it does not treat (empty) as string[2]. It reads 'cat' as string[2]. So how can I solve this problem?
char str1[15002][12];
char str2[15002][12];
char s[25];
map<string,int> Map;
int main()
{
int ncase, i, j, n1, n2, count, Case;
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&ncase);
Case = 1;
while(ncase > 0)
{
Map.clear();
//this is the necessery part
scanf("%d %d\n",&n1,&n2);
count = 0;
printf("n1=%d n2=%d\n",n1,n2);
for(i = 0; i < n1; i++)
{
gets(str1[i]);
}
for(i = 0; i < n2; i++)
{
gets(str2[i]);
}
//end of reading input
for(i = 0; i < n1; i++)
{
for(j = 0; j < n2; j++)
{
strcpy(s,str1[i]);
strcat(s,str2[j]);
if(Map[s] == 0){
count += 1;
Map[s] = 1;
}
}
}
printf("Case %d: %d\n", Case, count);
Case++;
ncase--;
}
return 0;
}
and input can look like
I have given the code here. The input may be like
line1>1
line2>3 3
line3>(empty line)
line4>a
line5>b
line6>c
line7>(empty)
line8>b
And I expect
str1[0]=(empty).
str1[1]=a;
str1[2]=b;
and
str2[0]=c;
str2[1]=(empty);
str2[2]=b;
OK, at last I found the problem. It is the line
printf("n1=%d n2=%d\n",n1,n2);
which creates problem in taking input by gets(). Instead of taking newline with the integer n1, n2, then I take newline as a ("%c",&ch) and then everything is okay.
Thanks to everyone who answered me.
Chances are, the string contains \r\n\0 (or \n\r\0 - never remember which comes first). \r\n is newline on Windows and \0 is the terminating character of the string.
In general, if the first character of the string is \r or\n, you read an empty string. FWIW this should work on all platforms:
char* string;
// initialize string and read something into it
if (strlen(string) == 0 || string[0] == `\r` || string[0] == `\n`)
// string is empty
Update: you mention that you use gets, and read from a file. However, for the latter you need fgets, so there is some confusion here. Note that fgets includes the trailing newline character in the string returned, while gets does not.
Update3: The way you read from the file is indeed fishy. You reopen the standard input to read from the file - why??? The standard practice is to fopen the file, then read from it with fscanf and fgets.
Update2: stupid us (and clever #Salil :-). You say
it read 'cat' as string[3]
Since C arrays are indexed from 0, string[3] contains the 4th line read! The third line is stored in string[2] - I bet that will contain the empty string you are looking for.
Output of this code:
#include <cstdio>
int main ()
{
int i = 0;
char string [256];
while (gets(string)) {
++i;
}
printf("%d\n", i);
return 0;
}
For this input
a
b
d
Is
4
Which means, gets() reads all lines correctly, which in turn means your code must be screwed up. Post it here.
First and foremost, do not use gets!!!!! It is a buffer overflow vulnerability, since you cannot specify the size of the destination buffer, and so gets() can easily overrun your buffer. Instead, use fgets() or getchar().
Since you are using map<string,int>, it is clear that you are actually using C++ code. In that case, an even better approach is to use the C++ iostreams libraries for your input and output.
Now that I've done with my rant, the problem is this... gets -- which, again, you should never ever use -- according to the spec, will read up until a newline, and "any <newline> shall be discarded". The function fgets() will copy the newline into the destination buffer, giving you the desired behavior.
If there is no string, how do you expect to read it?
Please give us a piece of code :)
==Later edit ==
OK:
"gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with '\0'. "
So basically, if you have:
char x[3];
gets(x);
Then this function will fill in x[0] with '\0'
If you read the manpage you'll see that gets is not recommended. Use fgets instead