I'm currently working on an assignment that requires me to take an input file, separate it and store the contents into two different arrays.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char DataMem[32][3];
int RegMem[32][10];
char line[100][21]; //Holds the value for each line in the input file
int i = 0;
int j = 0;
while(fgets(line[i], 20, stdin) != NULL)
{
line[i++];
if(line[i] == " ")
DataMem[j] = line[i];
//printf("%s", line[3]);
}
return 0;
}
Suppose the input file looks something like:
95864312
68957425
-136985475
36547566
24957986
1
45
98
where the first values before the 1, are stored into the array named line, and the lines following the blank line need to be put into the array DataMem.
Can anyone point me into the direction as to how to do this? I can fill in the array line correctly, however I am having a hard time stopping the fill at that point and subsequently filling the rest of the file into the array datamem.
Thank you
Several issues; one is that (as Some programmer dude says) is that you do i++ so your test of line[i] == " " references uninitialized memory.
You also have a problem in that you cannot compare strings using ==, you must use strcmp(). The issue is that strings in C are actually pointers, so
char *foo = "Hello";
char *bar = "Hello";
if (foo == bar)
printf("This will never print out\n")
is not going to do what you expect--you will never see the printout, as the way this will (likely) compile is that foo will be set to point to an address in memory (say 0x1000) that has 'H' in 0x1000, 'e' in 0x1001, etc, up to 'o' in 0x1004, and '\0' in 0x1005. The bar variable will point to some other address (say 0x2000) which will hold the string "Hello" also. So while both strings are the same, the if test is actually testing if (0x1000 == 0x2000), which will fail--you need to do if (!strcmp(foo, bar)) to actually test the contents.
[Note: this example is actually flawed in that most modern compilers will simply create one instance of the "Hello" string in read-only memory and point both variables at it, so the if test would actually work in this case. But you shouldn't rely on that and it definitely does not hold for the general case.]
Your test on line[i] looks wrong also, as I suspect you want to copy strings that start with a space, not strings that are exactly " ", so I suspect you actually want to test on line[i][0]. But I can't be sure without knowing your assignment.
Your declaration of DataMem is incorrect also--you have declared it as 32 3-char entries, but you are writing line[i] into it and line[i] is a pointer. You either need to declare all instances big enough to hold the entire string you want (presumably the same 21 bytes that line can hold) and copy into it, or you need to declare it as an array of pointers (char *DataMem[32]). There is a key difference you need to understand: if you copy the string, then if you modify DataMem's view of the string, line's view of the string is unchanged. If you simply copy the pointer then changing one string changes both (because they are both pointing at exactly the same memory). Obviously, copying the string is slower and takes more memory (well, except for very short strings).
The magic numbers are bad as well. Instead of 20 and 21, for example, I would do #define MAX_STRING_LEN 20 and use it in the code. (Good job remembering to declare the array big enough to hold the terminating NIL by the way. However fgets() already is aware of the need and will read in up to one fewer characters so there is room for the NIL. You should be passing in 21 not 20). Also, I would pass sizeof(line[i]) as the argument to fgets(), not MAX_STRING_LEN (and certainly not 20). That way if the size of line[i] ever changes the code will still be correct; if you pass in the same dimension that you used to declare the variable someone might change it without realizing they need to change it here too.
Finally, you need to bounds-check inside your loop. What happens if the input is longer than the 100 entries you declared for line[]? Without a test you run the risk of writing beyond your variable boundary (which tends to lead to really hard-to-find bugs). A very useful macro is
#define NELEM(x) (sizeof(x) / sizeof(*(x)))
which you could use to do the test:
if (i >= NELEM(line)) {
printf("Data overflow\n");
exit(1);
}
so you don't need to embed the 100 (or the #define you replace it with) inside your code. (The #define works by taking the size of the entire data structure and dividing it by the size of the first element in it. And all the parentheses are actually required).
#include <stdio.h>
int main()
{
char DataMem[32][3];
char line[100][21]; //Holds the value for each line in the input file
for(int i = 0; fgets(line[i], 20, stdin) != NULL ; ++i)
{
if('\r' == line[i][0] || '\n' == line[i][0]) {
break;
}
printf("line[%d] = %s",i, line[i]);
}
for(int i = 0; fgets(DataMem[i], 20, stdin) != NULL ; ++i)
{
printf("DataMem[%d] = %s",i, DataMem[i]);
}
return 0;
}
Related
I am trying to concatenate a random number of lines from the song twinkle twinkle. Into the buffer before sending it out because I need to count the size of the buffer.
My code:
char temp_buffer[10000];
char lyrics_buffer[10000];
char *twinkle[20];
int arr_num;
int i;
twinkle[0] = "Twinkle, twinkle, little star,";
twinkle[1] = "How I wonder what you are!";
twinkle[2] = "Up above the world so high,";
twinkle[3] = "Like a diamond in the sky.";
twinkle[4] = "When the blazing sun is gone,";
twinkle[5] = "When he nothing shines upon,";
srand(time(NULL));
arr_num = rand() % 5;
for (i=0; i<arr_num; i++);
{
sprintf(temp_buffer, "%s\n", twinkle[i]);
strcat(lyrics_buffer, temp_buffer);
}
printf("%s%d\n", lyrics_buffer, arr_num);
My current code only prints 1 line even when I get a number greater than 0.
There are two problems: The first was found by BLUEPIXY and it's that your loop never does what you think it does. You would have found this out very easily if you just used a debugger to step through the code (please do that first in the future).
The second problem is that contents of non-static local variables (like your lyrics_buffer is indeterminate. Using such variables without initialization leads to undefined behavior. The reason this happens is because the strcat function looks for the end of the destination string, and it does that by looking for the terminating '\0' character. _If the contents of the destination string is indeterminate it will seem random, and the terminator may not be anywhere in the array.
To initialize the array you simply do e.g.
char lyrics_buffer[10000] = { 0 };
That will make the compiler initialize it all to zero, which is what '\0' is.
This initialization is not needed for temp_buffer because sprintf unconditionally starts to write at the first location, it doesn't examine the content in any way. It does, in other words, initialize the buffer.
Update the buffer address after each print after initializing buffer with 0.
char temp_buffer[10000] = {0};
for (i=0; i<arr_num; i++) //removed semicolon from here
{
sprintf(temp_buffer + strlen(temp_buffer), "%s\n", twinkle[i]);
}
temp_buffer should contain final output. Make sure you have enough buffer size
You don't need strcat
The following code will print to the file correctly if char finalstr[2048]; is declared, however if I remove it (since it's not used anywhere) the program prints garbage ascii instead. This makes me believe it's something related to memory, however I have no clue.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
FILE *fp2;
char str[2048];
char finalstr[2048];
fp = fopen("f_in.txt", "r");
fp2 = fopen("f_out.txt", "w");
while(fgets(str,2047,fp))//read line by line until end of file
{
int i;
for(i=0;i<=strlen(str);i++)//go trough the string cell by cell
{
if(str[i]>47 && str[i]<58 && str[i+1]>47 && str[i+1]<58)//from 0 to 9
{
char temp[2];//to hold temporary two digit string number
temp[0]=str[i];
i++;
temp[1]=str[i];
if(atoi(temp)<27)//if it's an upper case letter
fprintf(fp2,"%c",atoi(temp)+64);
else//if it's lowercase, skip the special characters between Z and a
fprintf(fp2,"%c",atoi(temp)+70);
}
else fprintf(fp2,"%c",str[i]);
}
}
fclose(fp);
fclose(fp2);
}
Input
20343545 3545 27 494140303144324738 343150 404739283144: ffabcd. 094540' 46 3546?
01404146343144 283127474635324738 404739283144 09 453131 3545 abcdefYXWVUTSRQP
2044474546 3931. 09 37404149 27 384146!
Output if finalstr[] is declared
This is a wonderful hex number: ffabcd. Isn' t it?
Another beautiful number I see is abcdefYXWVUTSRQP
Trust me. I know a lot!
Output if finalstr[] is not declared
?99? 9? 9 ?9999?9?9 99? 9?999?: ffabcd. ??9' ? 9??
((((.(( (((((.((. ((.((( ( ((( .( abcdefYXWVUTSRQP
øòòøò øò. ø òòòò ø òòò!
I did notice that the first if() statement could cause an overflow, however replacing <= with < had no effect on the end result.
I really wonder what the explanation behind this is, and whether it's C specific or if it would have happened in C++ too.
The main problem is with the temporary string you're using. It's not long enough to store a null terminating character, so you have an unterminated string.
Make the array 3 bytes long and add the terminator:
char temp[3];//to hold temporary two digit string number
temp[0]=str[i];
i++;
temp[1]=str[i];
temp[2]=0;
Also, you're looking too far off of the end of the array in your for loop. Use < instead of <=:
for(i=0;i<strlen(str);i++)//go trough the string cell by cell
Finally, make sure you #include <string.h> so that you have a proper declaration for strlen.
atoi(temp) causes undefined behaviour. The atoi function expects a pointer to null-terminated string as argument, however you provided a pointer to two characters without a terminator.
The atoi function will read off the end of your array. Your dummy array influences this because it changes what junk is present after the temp array.
BTW you could use (str[i] - '0') * 10 + (str[i+1] - '0') instead of atoi.
To my understanding, the problem is that the program fills the array potentially up to its full capacity by
fgets(str,2047,fp)
which means that the condition
i <= strlen(str)
works as expected only if the location after str is terminated with a zero; this might be the case when declaring finalstr.
#include <stdio.h>
#include <stdlib.h>
main()
{
char *a,*b,*c={0};
int i=0,j=0;
a=(char *)malloc(20*sizeof(char));
b=(char *)malloc(20*sizeof(char));
c=(char *)malloc(20*sizeof(char));
printf("Enter two strings:");
gets(a);
gets(b);
while(a[i]!=NULL)
{
c[i]=a[i];
i++;
}
while(b[j]!=NULL)
{
c[i]=b[j];
i++;
j++;
}
printf("The concated string is %s",c);
}
this is crazy........i spend one whole night it didn't work and then next night it suddenly works perfectly....i'm confused
There are many things wrong with your code.
Not all of them matter if all you care about is getting the code to work.
However, I have tried here to show you different misconceptions that are clear from your code, and show you how to code it better.
You are misunderstanding what NULL means. a NULL pointer doesn't point at anything
Strings are terminated with '\0' which is an ASCII NUL, not the same thing, though both use the value 0.
char* s = "hello";
The above string is actually 6 characters long. 5 bytes for the hello, 1 for the '\0' that is stuck at the end. Incidentally, this means that you can only have strings up to 19 characters long because you need to reserve one byte for the terminal '\0'
char* r = NULL;
The pointer r is pointing at nothing. There is no '\0' there, and if you attempt to look at r[0], you will crash.
As Ooga pointed out, you missed terminating with '\0' which is going to create random errors because your printf will keep going to try to print until the first zero byte it finds. Whether you crash on any particular run is a matter of luck. Zeros are common, so usually you will stop before you crash, but you will probably print out some junk after the string.
Personally, I would rather crash than have the program randomly print out the wrong thing. At least when you crash, you know something is wrong and can fix it.
You also seem to have forgotten to free the memory you malloc.
If you are going to use malloc, you should use free at the end:
int* a = malloc(20);
...
free(a);
You also are only mallocing 20 characters. If you go over that, you will do horrible things in memory. 20 seems too short, you will have only 19 characters plus the null on the end to play with but if you do have 20 characters each in a and b, you would need 40 characters in c.
If this is an assignment to use malloc, then use it, but you should free when you are done. If you don't have to use malloc, this example does not show a reason for using it since you are allocating a small, constant amount of memory.
You are initializing c:
char* c = {0};
In a way that makes no sense.
The {0} is an array with a single zero value. c is pointing to it, but then you immediately point it at something else and never look at your little array again.
You probably mean that C is pointing to nothing at first.
That would be:
char* c = NULL;
but then you are immediately wiping out the null, so why initialize c, but not a and b?
As a general rule, you should not declare values and initialize them later. You can always do something stupid and use them before they are initialized. Instead, initialize as you declared the:
int* a = malloc(20);
int* b = malloc(20);
int* c = malloc(40);
Incidentally, the size of a char is by definition 1, so:
20* sizeof(char)
is the same as 20.
You probably saw an example like:
20 * sizeof(int)
Since sizeof(int) which is not 1 the above does something. Typically sizeof(int) is 4 bytes, so the above would allocate 80 bytes.
gets is unsafe, since it doesn't say how long the buffer is
ALWAYS use fgets instead of gets. (see below).
Many computers have been hacked using this bug (see http://en.wikipedia.org/wiki/Robert_Tappan_Morris)
Still, since malloc is not really needed, in your code, you really should write:
enum { SIZE = 128 };
char a[SIZE];
fgets(a, SIZE, STDIN);
char b[SIZE];
fgets(b, SIZE, STDIN);
char c[SIZE*2];
int i;
int j = 0;
for (i = 0; a[i] != '\0' && i < 127; i++)
c[j++] = a[i];
for (i; b[i] != '\0' && i < 127; i++)
c[j++] = a[i];
c[j] = '\0';
...
Last, I don't know if you are learning C or C++. I will simply point out that this kind of programming is a lot easier in C++ where a lot of the work is done for you. You can first get concatenation done the easy way, then learn all the pointer manipulation which is harder.
#include <string>
#include <iostream>
using namespace std;
int main() {
string a,b,c;
getline(cin, a); // read in a line
getline(cin, b);
c = a + b;
cout << c;
}
Of course, you still need to learn this low-level pointer stuff to be a sophisticated programmer in C++, but if the purpose is just to read in and concatenate lines, C++ makes it a lot easier.
You are not properly null-terminating c. Add this before the printf:
c[i] = '\0';
Leaving out null-termination will seem to work correctly if the char at i happens to be 0, but you need to set it to be sure.
The string c is not being terminated with a null char, this means printf does not know where to stop and will likely segfault your program when it overruns. The reason you may be getting sporadic success is that there is a random chance the malloced area has been pre zeroed when you allocate it, if this is the case it will succeed as a null char is represented as a literal 0 byte.
there are two solutions available to you here, first you could manually terminate the string with a null char as so:
c[i] = '\0';
Second you can use calloc instead of malloc, it guarantees the memory is always pre zeroed.
As a side note you should likely add some length checking to your code to ensure c will not overflow if A and B are both over 10. (or just make c 40 long)
I hope this helps.
This question already has answers here:
buffering a set of lines from a file and storing it in an array in C
(3 answers)
Closed 8 years ago.
I am trying to read all strings from a text file and save them in an array of strings.
However, when I try to print the contents of my string array only the last part is printed.
Why does my code only copy the last string?
My code
# include <stdio.h>
# define BUFFERSIZE 100
int main(int argc, char *argv[]){
char buffer[BUFFERSIZE];
int i = 0;
char *text[BUFFERSIZE];
while(fgets(buffer, BUFFERSIZE, stdin) != NULL){
text[i] = buffer;
i++;
}
int j = 0;
for (j=0; j<sizeof(text)/sizeof(char); i++){
printf("%s\n", text[j]);
}
return 0;
}
My text file
ESD can create spectacular electric sparks (thunder and lightning is a large-scale ESD event), but also less dramatic forms which may be neither seen nor heard, yet still be large enough to cause damage to sensitive electronic devices.
Electric sparks require a field strength above about 4 kV/cm in air, as notably occurs in lightning strikes. Other forms of ESD include corona discharge from sharp electrodes and brush discharge from blunt electrodes.
Output
>>> make 1_17; ./1_17 < example.txt
m blunt electrodes.
m blunt electrodes.
m blunt electrodes.
m blunt electrodes.
...
There are two issues. The first is that for all you is, text[i] contains the same buffer which you've used multiple times. The second is that in your printing code, you're only printing text[0].
Using the same buffer
There's only one buffer declared,
char buffer[BUFFERSIZE];
and while you modify its contents many times in the loop, it's always the same buffer (i.e., the same storage area in memory), so
text[i] = buffer;
makes every element of text contain the (address of) the same buffer. You need to copy the contents of buffer into a new string and store that in text[i] instead. You can duplicate the string using, e.g., strdup if you can use POSIX functions, as in
text[i] = strdup(buffer);
strdup uses malloc to allocate the space for the string, so if you're using this in a larger application, be sure to free those strings later. In your simple application, though, they'll be freed when the application exits, so you're not in too much trouble.
If you can only use standard C functions, you'll probably want strcpy which will make you do a little bit more work. (You'll need to allocate a string big enough to hold the current contents of buffer, and then copy buffer's contents into it. You'll still want to free them afterwards.
Printing only text[0]
However, you've also got an issue with your printing code. You're indexing into text with j, but never modifying j (you're incrementing i with i++), so you're always printing the same string (which is actually the first in the array, not the last, but its contents are the same as the lsat string that you read from the file):
int j = 0;
for (j=0; j<sizeof(text)/sizeof(char); i++){
printf("%s\n", text[j]);
}
After the first loop, i is the number of strings that you've got, so you probably just want:
int j;
for ( j=0; j < i; j++ ) {
printf("%s\n", text[j]);
}
Also, in addition to the answers already posted, you're incrementing i in your for loop, not j.
Look at this loop:
while(fgets(buffer, BUFFERSIZE, stdin) != NULL){
text[i] = buffer;
i++;
}
You're setting every element of your array to the exact same value: the value of buffer, which is a pointer to a piece of memory. On every iteration you're reading data over the top of where you read it last time.
To fix this, you'd need to allocate a new buffer on each iteration, and set the array value to a pointer to that new buffer. That way each element of the array will point to a different section of memory.
I have a tiny problem with my assignment. The whole program is about tree data structures but I do not have problems with that.
My problem is about some basic stuff: reading strings from user input and then storing them in an array list.
char str[1000];
fgets(str, 1000, stdin);
int x = 0;
int y = 0;
int z = 0;
char **list;
list = (char**)malloc((x+1)*sizeof(char));
list[x] = (char*)malloc((y+1)*sizeof(char));
while(str[z] != '\n')
{
list[x][y] = str[z];
z++;
if(str[z] == ',')
{
x++;
y = 0;
list = (char**)realloc(list, (x+1) * sizeof(char*));
list[x] = (char*)malloc((y + 1)*sizeof(char));
z++;
if(str[z] == ' ') // Skips space after the comma
{
z++;
}
}
else if(str[z] == '\n')
{
break;
}
else
{
y++;
list[x] = (char*)realloc(list[x], (y+1)*sizeof(char));
}
}
I pass this list array into another function.
As an example, inputs could be something like
Abcde, Fghijk, Lmnop, Qrstu
and I am trying to split each of these words into the array list.
Abcde
Fghijk
Lmnop
Qrstu
When I try to output the strings I sometimes get weird, excessive characters such as upside down question marks and numbers.
printf("%s ", list[some_number]);
gets me
Fghijk¿
or
Fghijk\200
All of my program works as expected except for this minor problem which I am having trouble solving. Even with the same exact inputs the bugs may or may not appear. I am guessing it has to do with memory allocation?
Thanks for your help!
You need to put '\0' at the end of your new string.
See most of the C library functions such as printf and strlen process strings assuming \0 as the end character of all. Otherwise, they keep on reading the memory out of bounds either making a memory violation or gets some where the value 0 and stops and all the bytes in between in the memory are interpreted to their extended ascii equivalent hence you are getting such a strange behaviour.
So, allocate an extra byte for \0 character and assign it to the last byte.
Either initialize your variables to null, or as tomato said, put a null character at the end of the new string.
C lacks many of the luxuries programmers now take for granted when it comes to memory management. You're on the right path with malloc but that function only allocates memory... it doesn't clear it out. As a result, your variables will have the correct amount of space (critical for reducing memory leaks and overflow errors), but will be filled with garbage. This garbage could be anything, and in your case, it's an upside down question mark. Appropriate, don't you think?
I could be mistaken since I can't run the code myself without more information, but after your
char **list;
list = (char**)malloc((x+1)*sizeof(char));
list[x] = (char*)malloc((y+1)*sizeof(char));
statements, you'll want to do something like this:
list = NULL;
and the like to clear out the garbage.
Furthermore, you may care to use the strlen() function (contained in string.h) to figure out just how many blocks of memory you need to allocate.
Clearing out the spaces you use for variables is a good practice to get into with C. Good to see you learning it as well.