I'm working on learning C and decided to port my Game of Life code over from Java. It doesn't seem too difficult except that the FileIO part. My input file looks something like:
Beehive
5 6
------
--XX--
-X--X-
--XX--
------
Here's the pseduo-code of what I did in Java;
Scanner to open the file,
String line = file.nextLine(),
print the line,
get the second line
Trim and split the firstLine,
String[tokens[0]][tokens[1]],
while(line != NULL) -> string[row][col] = input.charAt(i);
close input,
return string[][]
This is what I have so far in C,
void fileIO() {
FILE *file;
char buffer[BUFFER_SIZE];
file = fopen("INPUT_FILE", "r");
if(file == NULL) {
printf("Cannot open file!");
}
while(fgets(buffer, BUFFER_SIZE, file) != NULL ) {
}
}
I'm not sure how to proceed from here? Can anyone give me a pointer in which way to go from here?
To print a line: puts
puts(buffer);
Note that after fgets, buffer contains the newline character \n, which will be printed too. I guess you want this behavior (not sure how this works in Java).
To trim and split a line: sscanf
int height, width;
...
sscanf(buffer, "%d%d", &height, &width);
To extract a character from a string (instead of input.charAt(i)):
char c = buffer[i];
This is not file I/O; it's just the C syntax for getting a character from a string.
It seems that you have a function in Java that returns a 2-D array (of characters? of strings?), which is dynamically allocated. Java supports recording the width and height in the array object itself, while C doesn't support this. Instead of the 2-D array, you will have to use a struct:
struct MyDataFromFile
{
int height, width;
bool **data;
};
Such data structure is only one possible option; you could use different options:
bool[MAX_HEIGHT][MAX_WIDTH] - convenient if there is maximum height and width
uint64_t *data if you want to use 1 bit for storage, instead of 1 byte - this requires additional bit-fiddling
Related
I am kind of new when it comes to C. Took a class on it in college but I just don't practice it much. Well my issue that I'm having is that I'm trying to take an text file and convert it into an array. I have been able to get the text file and print it into the console but when I save run the while statement to read each line, my whole array gets overwritten. For instance if my last line on my text file is 19, my array[0] gets over written to what should be on array[18].
I know the indentations are off a off, coding is a mess, and forgive me on the printf commands, I'm only using them to troubleshoot my code. The text file will have IP address on each line.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#define MAX_LINE_LENGTH 16
int main()
{
int lineCntPOSlist = 0;
int lineCnt = 0;
int i = 0;
FILE *textfile;
char line[MAX_LINE_LENGTH];
char* posList[50];
textfile = fopen("pos-list.txt", "r");
if(textfile == NULL)
{
int posListExist = 0;
system("cls");
printf("File Exist %d\n", posListExist);
fprintf(stderr, "**File open failed\n Make sure there is a pos-list.txt file");
return 1;
}
system("cls");
while(fgets(line, MAX_LINE_LENGTH, textfile))
{
printf("Line %d: %s",lineCnt , line);
posList[lineCnt] = line;
printf("posList[%d] = %s\n", lineCnt, posList[lineCnt] );
printf("posList[0] = %s\n", posList[0] );
lineCnt = ++lineCnt;
lineCntPOSlist = ++lineCntPOSlist;
}
fclose(textfile);
return 0;
}
This:
posList[lineCnt] = line;
simply sets posList[lineCnt] to point at the line buffer in line, it copies zero characters of actual data. In higher-level languages where strings have a bit more presence, this would make sense, but in C it doesn't.
Since there is only one line buffer, it will always hold the characters making up the most recently loaded line, and thus it will act as if previous lines are being "overwritten".
There are several solutions, here are a few:
Make posList into a proper array of strings, but that requires you to decide max length in advance and will waste the space for shorter lines.
Use something like strdup() to allocate copies on the heap of each line, and store pointers to those.
Use a more "proper" reallocating array for the storage and store line pointers (or offsets, which might be better suited due to the reallocating) in the array for easier access.
Wondering if I could get some advice. Firstly, I am very new to programming, so I apologise for any silly mistakes. Please feel free to point them out and I will try to go research to improve.
I feel I am doing something fundamentally wrong with my array.
I am trying to read in from a file whose filename is specified by user input, store the information from the file in a 2D array, which I then plan to print into another file, again defined by user input.
I am currently printing out the array, to check that it has been stored, but I believe I am using the 2D array incorrectly, as when I try to fprintf into my file, it just does not work.
Any advice would be greatly appreciated.
Thank you. Code as follows:
#include <stdio.h>
#include <string.h>
int main()
{
char finame[100];
printf("Enter file you would like to open: ");
scanf("%s", finame);
FILE *in = fopen(finame, "r");
char foname[100];
printf("Enter the filename you would like the output included in: ");
scanf("%s", foname);
FILE *out = fopen(foname, "w");
/*Char array to store string */
char str[50][20];
int i =0;
/*Loop for reading the file till end*/
while((fgets(str[i],sizeof(str[i]), in)) != NULL) {
fputs(str[i++],stdout);
//getchar();
}
return 0;
}
Avoid mixing fgets(), scanf() calls. scanf() leaves a newline character in the input buffer which is later consumed by fgets() (doesn't
matter in this case since input comes from a file not from stdin but a good practice overall).
There is also no protection for overflow, if you want to stick to scanf() add a width specifier and check the result to see if it succeeded.
if (scanf("%99s", finame) != 1) /* save one byte for \0 */
{
/* handle error case */
}
Check that you don't exceed the size of your array while writing to it.
Added 2 define directives that could clean your code up.
#define MAX_LINES 50
#define MAX_CHAR 20
char str[MAX_LINES][MAX_CHAR];
int i = 0;
while (i < MAX_LINES && (fgets(str[i], MAX_CHAR, in)) != NULL) /* always check if running out bounds */
{
fputs(str[i++], stdout);
}
The problem with the above code is that, if the file gets too big, you will end up missing data, what you could do is have a dynamic array and use a malloc / realloc approach to expand the array or a linked list.
I found this piece of code at Reading a file character by character in C and it compiles and is what I wish to use. My problem that I cannot get the call to it working properly. The code is as follows:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL)
return NULL; //could not open file
code = malloc(1500);
while ((c = fgetc(file)) != EOF)
{
code[n++] = (char) c;
}
code[n] = '\0';
return code;
}
I am not sure of how to call it. Currently I am using the following code to call it:
.....
char * rly1f[1500];
char * RLY1F; // This is the Input File Name
rly1f[0] = readFile(RLY1F);
if (rly1f[0] == NULL) {
printf ("NULL array); exit;
}
int n = 0;
while (n++ < 1000) {
printf ("%c", rly1f[n]);
}
.....
How do I call the readFile function such that I have an array (rly1f) which is not NULL? The file RLY1F exists and has data in it. I have successfully opened it previously using 'in line code' not a function.
Thanks
The error you're experiencing is that you forgot to pass a valid filename. So either the program crashes, or fopen tries to open a trashed name and returns NULL
char * RLY1F; // This is not initialized!
RLY1F = "my_file.txt"; // initialize it!
The next problem you'll have will be in your loop to print the characters.
You have defined an array of pointers char * rly1f[1500];
You read 1 file and store it in the first pointer of the array rly1f[0]
But when you display it you display the pointer values as characters which is not what you want. You should just do:
while (n < 1000) {
printf ("%c", rly1f[0][n]);
n++;
}
note: that would not crash but would print trash if the file read is shorter than 1000.
(BLUEPIXY suggested the post-incrementation fix for n BTW or first character is skipped)
So do it more simply since your string is nul-terminated, pass the array to puts:
puts(rly1f[0]);
EDIT: you have a problem when reading your file too. You malloc 1500 bytes, but you read the file fully. If the file is bigger than 1500 bytes, you get buffer overflow.
You have to compute the length of the file before allocating the memory. For instance like this (using stat would be a better alternative maybe):
char *readFile(char *fileName, unsigned int *size) {
...
fseek(file,0,SEEK_END); // set pos to end of file
*size = ftell(file); // get pos, i.e. size
rewind(file); // set pos to 0
code = malloc(*size+1); // allocate the proper size plus one
notice the extra parameter which allows you to return the size as well as the file data.
Note: on windows systems, text files use \r\n (CRLF) to delimit lines, so the allocated size will be higher than the number of characters read if you use text mode (\r\n are converted to \n so there are less chars in your buffer: you could consider a realloc once you know the exact size to shave off the unused allocated space).
I am really struggling to understand how character arrays work in C. This seems like something that should be really simple, but I do not know what function to use, or how to use it.
I want the user to enter a string, and I want to iterate through a text file, comparing this string to the first word of each line in the file.
By "word" here, I mean substring that consists of characters that aren't blanks.
Help is greatly appreciated!
Edit:
To be more clear, I want to take a single input and search for it in a database of the form of a text file. I know that if it is in the database, it will be the first word of a line, since that is how to database is formatted. I suppose I COULD iterate through every single word of the database, but this seems less efficient.
After finding the input in the database, I need to access the two words that follow it (on the same line) to achieve the program's ultimate goal (which is computational in nature)
Here is some code that will do what you are asking. I think it will help you understand how string functions work a little better. Note - I did not make many assumptions about how well conditioned the input and text file are, so there is a fair bit of code for removing whitespace from the input, and for checking that the match is truly "the first word", and not "the first part of the first word". So this code will not match the input "hello" to the line "helloworld 123 234" but it will match to "hello world 123 234". Note also that it is currently case sensitive.
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[100]; // declare space for the input string
FILE *fp; // pointer to the text file
char fileBuf[256]; // space to keep a line from the file
int ii, ll;
printf("give a word to check:\n");
fgets(buf, 100, stdin); // fgets prevents you reading in a string longer than buffer
printf("you entered: %s\n", buf); // check we read correctly
// see (for debug) if there are any odd characters:
printf("In hex, that is ");
ll = strlen(buf);
for(ii = 0; ii < ll; ii++) printf("%2X ", buf[ii]);
printf("\n");
// probably see a carriage return - depends on OS. Get rid of it!
// note I could have used the result that ii is strlen(but) but
// that makes the code harder to understand
for(ii = strlen(buf) - 1; ii >=0; ii--) {
if (isspace(buf[ii])) buf[ii]='\0';
}
// open the file:
if((fp=fopen("myFile.txt", "r"))==NULL) {
printf("cannot open file!\n");
return 0;
}
while( fgets(fileBuf, 256, fp) ) { // read in one line at a time until eof
printf("line read: %s", fileBuf); // show we read it correctly
// find whitespace: we need to keep only the first word.
ii = 0;
while(!isspace(fileBuf[ii]) && ii < 255) ii++;
// now compare input string with first word from input file:
if (strlen(buf)==ii && strstr(fileBuf, buf) == fileBuf) {
printf("found a matching line: %s\n", fileBuf);
break;
}
}
// when you get here, fileBuf will contain the line you are interested in
// the second and third word of the line are what you are really after.
}
Your recent update states that the file is really a database, in which you are looking for a word. This is very important.
If you have enough memory to hold the whole database, you should do just that (read the whole database and arrange it for efficient searching), so you should probably not ask about searching in a file.
Good database designs involve data structures like trie and hash table. But for a start, you could use the most basic improvement of the database - holding the words in alphabetical order (use the somewhat tricky qsort function to achieve that).
struct Database
{
size_t count;
struct Entry // not sure about C syntax here; I usually code in C++; sorry
{
char *word;
char *explanation;
} *entries;
};
char *find_explanation_of_word(struct Database* db, char *word)
{
for (size_t i = 0; i < db->count; i++)
{
int result = strcmp(db->entries[i].word, word);
if (result == 0)
return db->entries[i].explanation;
else if (result > 0)
break; // if the database is sorted, this means word is not found
}
return NULL; // not found
}
If your database is too big to hold in memory, you should use a trie that holds just the beginnings of the words in the database; for each beginning of a word, have a file offset at which to start scanning the file.
char* find_explanation_in_file(FILE *f, long offset, char *word)
{
fseek(f, offset, SEEK_SET);
char line[100]; // 100 should be greater than max line in file
while (line, sizeof(line), f)
{
char *word_in_file = strtok(line, " ");
char *explanation = strtok(NULL, "");
int result = strcmp(word_in_file, word);
if (result == 0)
return explanation;
else if (result > 0)
break;
}
return NULL; // not found
}
I think what you need is fseek().
1) Pre-process the database file as follows. Find out the positions of all the '\n' (carriage returns), and store them in array, say a, so that you know that ith line starts at a[i]th character from the beginning of the file.
2) fseek() is a library function in stdio.h, and works as given here. So, when you need to process an input string, just start from the start of the file, and check the first word, only at the stored positions in the array a. To do that:
fseek(inFile , a[i] , SEEK_SET);
and then
fscanf(inFile, "%s %s %s", yourFirstWordHere, secondWord, thirdWord);
for checking the ith line.
Or, more efficiently, you could use:
fseek ( inFile , a[i]-a[i-1] , SEEK_CURR )
Explanation: What fseek() does is, it sets the read/write position indicator associated with the file at the desired position. So, if you know at which point you need to read or write, you can just go there and read directly or write directly. This way, you won't need to read whole lines just to get first three words.
I have a text file with 25 lines, each with 34 characters on.
In C how is it possible to load these characters and store them into a 2D array?
If the first three lines of the file are such:
bamaaaaaaaacxxxxxxxxxxbaaaaaaaamac
jzjzzzzzzzzdaaaaaaaaaaezzzzzzzzjzj
jzjzbaaczgzzzzzzzzzzzzzzgzbaaczjzj
...and so on
I require the array to be stored as if it was defined like this:
char* data[] = {
"baaaaaaaaaaaaaacxxbaaaaaaaaaaaaaac",
"jzzzzzzzzzzzzzzjxxjzzzzzzzzzzzzzzj",
"jzbaaaaaaaaaaaaexxdaaaaaaaaaaaaczj",
...and so on
Hopefully this makes some sense! It is important that the type of data is char data[][] as it is used in that format in the rest of my project and cannot be changed.
I have done the basic begining of the File IO
FILE *infp;
printf("Opening file\n");
if((infp = fopen("file.txt", "r"))== NULL) {
printf("\nERROR : Unable to open input file\n");
SetExitWithCode( 999 );
}else{
//code here
}
Can anyone help?
So, you want your array to look like this:
char data[25][35] //There is an extra 1 character per line to hold the null terminator character
Then simply read the whole file into that array
fread(data, 1, sizeof(data), infp);
And finally, replace the new line character in on each line with a terminator
for (int i = 0; i < 25; ++i) {
data[i][34] = 0;
}
This is the easiest solution to the problem, but it is also a bad way of doing it. It does not validate that the data is in the correct format, and everything is hard coded.