I have to read an undefined matrix from a text file in C language, and i want to read it line by line so that each line will be an integer array.But how do i know where is the end of a line, since i can't use "\n" as in for characters?
Here is the code:
#include "stdafx.h"
#include "stdlib.h"
#include "stdio.h"
using namespace System;
typedef struct
{
int *v;
int n;
}vector;
int main(array<System::String ^> ^args)
{
vector *a;
FILE* f;
int n = 15;
int i = 0;
int j,k;
if ((f = fopen("C:\\Users\\Mirelaa\\Documents\\visual studio 2013\\Projects\\MatriceNedefinita\\MatriceNedefinita\\Debug\\fisier2.in", "rt")) == NULL)
{
printf("Fisierul nu poate fi deschis!");
exit(1);
};
a = (vector *)malloc(n * sizeof(vector));
while (!feof(f))
{
a[i].v = (int*)malloc(n * sizeof(int));
a[i].n = 0;
//citeste rand
//citesti fiecare element din rand
j = 0;
while (a[i].v[j] != '\0')// wrong!!
{
fscanf(f, "%d", &a[i].v[j]);
j++;
a[i].n = a[i].n + 1;
}
for (k = 0 ; k < a[i].n ; k++)
{
printf("%d", a[i].v[j]);
printf("\n");
}
i++;
if (i == n)
{
n = 2 * n;
a = (vector *)realloc(a, n * sizeof(vector));
a[i].v = (int *)realloc(a[i].v, n * sizeof(int));
}
}
return 0;
}
Reading a line of integers and saving in a variable sized array is one approach.
The trouble with fscanf(f, "%d",... is that it first reads white-space and code loses the occurrence of '\n'. Code needs to look for it by some other means.
But rather than pack all the code in main(), consider helper functions. Following C function reads one line of numbers and return NULL on 1) out-of-memory, 2) no data or conversion failure with no numbers read. Otherwise return vector. It is not limited to any line length.
typedef struct {
int *v;
size_t n;
} vector;
vector *Read_vector(vector *v1, FILE *inf) {
v1->v = NULL;
v1->n = 0;
size_t size = 0;
for (;;) {
int number;
int ch;
// Check leading white-space for \n
while (isspace(ch = fgetc(inf))) {
if (ch == '\n') break;
}
if (ch == '\n' || ch == EOF) break;
ungetc(ch, inf);
if (1 != fscanf(inf, "%d", &number)) {
break;
}
// Is `v1` large enough?
if (v1.n >= size) {
size = size*2 + 1;
vector nu;
nu.v = realloc(v1->v, size * sizeof *nu.v);
if (nu.v == NULL) {
free(v1->v);
v1->v = NULL;
v1->n = 0;
return NULL;
}
v1->v = nu.v;
}
v1.v[v1.n++] = number;
}
if (v1->n == 0) {
return NULL;
}
return v1;
}
With repeated calls, an array of vectors could be had. Leave that to OP as it is very similar to the above.
Note: avoid use while (!feof(f)).
I would read in a lines with fgets...(see e.g. here)
char *fgets(char *str, int n, FILE *stream)
you can read a line up to $n$ characters line into a string.
This method gives you a way of loading each line individually to a string.
Edit after good comment
It is easier to split up the string with strtok and then use atoi to convert each bit of the string to an integer (rather than use sscanf as in my original answer).
Related
I have a file.csv . it contains two numbers separated by a comma.I put every line , such as string, in a pointer of char char *arr. My aim is to sort in ascending order by left number (number before comma i.e. //this is the example of what I have to sort, the whole example is below:
9514902
1134289
7070279
ecc..)
I tried strtok() but it delete the number after comma. I need both of the numbers for each couple.
To order the numbers I used Insertion Sort, trasforming my strings (couple of numbers with comma for me is a string) in long integers in order to compare them. swap function doesn't work because it returns me numbers that I've never passed him.
How can I resolve it?
main.c
#define SIZE 10
#define LEN 20
void swap(char *xp, char *yp){
char *temp=xp;
*xp = *yp;
*yp = *temp;
}
int main(){
FILE *fd = NULL;
fd = fopen("file.csv", "r");
int pos=0;
char (*arr)[LEN] = NULL;
arr = calloc ( SIZE, sizeof *arr);
while ( pos < SIZE && fgets ( arr[pos], sizeof arr[pos], fd)) {
++pos;
}
int i, j;
char *ptr;
for (i = 1; i < SIZE; i++){
char *p = strtok(arr[i], ",");
long pivot= strtol(p,&ptr,10);
char * c = strtok(arr[i-1], ",");
long value= strtol(c,&ptr,10);
for (j = i - 1; (j >= 0) && (value>pivot); j--){
swap(arr[j],arr[j+1]);
j--;
c = strtok(arr[j], ",");
value= strtol(c,&ptr,10);
}
}
}
file.csv
9514902,846
1134289,572
7070279,994
30886,48552
750704,1169
1385812,729
471548,3595
8908491,196
4915590,362
375309,212
You can do it without strtok too, Maybe this is not you looking for but you can look as another way of doing what you want, Have Fun
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
FILE* pFile = fopen("file.csv", "r");
if(pFile == NULL)
return 1;
char aBuf[256];
char aResult[1024];
aResult[0] = '\0';
// get each line
for(int i=0; fgets(aBuf, sizeof(aBuf), pFile) != NULL; i++){
// find comma in line
for(int j=0; j < strlen(aBuf); j++){
if(aBuf[j] != ',')
continue;
// copy everything before comma
char aAnotherBuffer[50];
strncpy(aAnotherBuffer, aBuf, j);
// convert it to integer
int FirstNum = atoi(aAnotherBuffer);
// get after of comma and convert it too
int SecondNum = atoi(aBuf + j + 1);
// make line that with sorted values
char aRes[256];
sprintf(aRes,
"%d,%d\n",
FirstNum < SecondNum? FirstNum: SecondNum,
FirstNum > SecondNum? FirstNum: SecondNum
);
// concatenate to result buffer
strcat(aResult, aRes);
// go for next line
break;
}
}
fclose(pFile);
// save results
{
FILE* pResFile = fopen("result.csv", "w");
if(pResFile){
fputs(aResult, pResFile);
fclose(pResFile);
}
}
return 0;
}
Im tryin to read a ascii-file consisting of 2 columns of and a variable nr of rows. Reading is done by processor 0 and then the data is distributed using MPI_Bcast(...). I had to create a 1-dim buffer array which contains the data and is sent to all other procs because I haven't found a way to directly broadcast the 2-dim. data.
There's something wrong with the allocation of the array arr. When I try to print the data, the first 3 rows are printed and then I get a segmentation fault. Most likely there are several mistakes.
I really tried to make the example as simple as possible.
my code:
//global variables
...
double **testarray;
int dim;
...
int main(int argc, char **argv){
...
...
read_file("test.txt",&testarray,&dim);
}
int read_file(char* infilename,double ***arr,int *rowsout)
{
FILE* infile;
int i,j,ch,number_of_lines=0;
int rows=0;
//first count lines
if(myid==0) //myid is processor id
{
infile = fopen(infilename, "r");
do
{
ch = fgetc(infile);
if(ch == '\n')
number_of_lines++;
} while (ch != EOF);
if(ch != '\n' && number_of_lines != 0)
number_of_lines++;
//close file
fclose(infile);
rows=number_of_lines-1;
*rowsout=rows;
}
// every proc should know about length of file in order
//to be able to allocate memory
MPI_Bcast(rowsout,1,MPI_INT,0,MPI_COMM_WORLD);
//allocate memory
double *buf; //1D-buffer for 2D-array
MPI_Alloc_mem((*rowsout)*2*sizeof(double), MPI_INFO_NULL, &buf);
MPI_Alloc_mem((*rowsout)*sizeof(double*), MPI_INFO_NULL,arr);
for (i = 0; i < (*rowsout); i++) {
MPI_Alloc_mem(2*sizeof(double),MPI_INFO_NULL,&arr[i]);
}
// Now read file on proc 0
if(myid==0)
{
infile=fopen(infilename,"r");
for(i=0;i<rows;i++)
{
for(j=0;j<2;j++)
{
fscanf(infile,"%lf",arr[i][j]);
printf("arr[%d][%d]:%e\n",i,j,(*arr)[i][j]);
}
}
fclose(infile);
}
return 0;
//dont go further, error occurs before loop finishs
MPI_Bcast(buf,(rows)*2,MPI_DOUBLE,0,MPI_COMM_WORLD);
//now reconstruct array from buffer
for(i=0;i<(*rowsout);i++)
{
for(j=0;j<2;j++)
{
*arr[i][j]=buf[i*2+j];
}
}
MPI_Free_mem(buf);
return 0;
}
You have some issues with your arr as you guessed. Here is a fixed version of your code (minus the MPI stuff):
#include <stdio.h>
#include <stdlib.h>
void read_file(char *filename, double ***arr, int *rowsout);
int main(void)
{
double **testarray;
int dim;
read_file("test.txt", &testarray, &dim);
return 0;
}
void read_file(char *filename, double ***arr, int *rowsout)
{
FILE *infile;
int i, j, ch;
infile = fopen(filename, "r");
do
{
ch = fgetc(infile);
if (ch == '\n')
++*rowsout;
} while (ch != EOF);
rewind(infile);
*arr = malloc(sizeof **arr * *rowsout);
for (i = 0; i < *rowsout; ++i)
(*arr)[i] = malloc(sizeof ***arr * 2);
for (i = 0; i < *rowsout; ++i)
{
for (j = 0; j < 2; ++j)
{
fscanf(infile, "%lf", &(*arr)[i][j]);
printf("(*arr)[%d][%d]: %e\n", i, j, (*arr)[i][j]);
}
}
fclose(infile);
for (i = 0; i < *rowsout; ++i)
free((*arr)[i]);
free(*arr);
}
Example input: test.txt
0.01 0.02
0.3 0.4
5.0 6.0
Example output:
(*arr)[0][0]: 1.000000e-02
(*arr)[0][1]: 2.000000e-02
(*arr)[1][0]: 3.000000e-01
(*arr)[1][1]: 4.000000e-01
(*arr)[2][0]: 5.000000e+00
(*arr)[2][1]: 6.000000e+00
I believe the mistake comes with your fscanf() line. fscanf() wants a double * when you are using "%lf", but you are instead passing arr[i][j] and there's two things wrong with that: since arr is actually a pointer to your 2-D array, you will need to deference it first ((*arr)), and second, since it needs the address of the double, you will need to use &: &(*arr)[i][j].
So my program takes a string, and then outputs it as a marquee sign. I have a for loop so that it may take multiple strings and then outputs each of the strings as a sign.
My problem is: after each iteration, it outputs the sign, and then continues to prompt me for the next string when I want it to just take in all my inputs at once, and then output every sign at the very end. Here is what I'm talking about:
Current Input:
3
Hello World!
5
Sign #1: (This is the output)
[Hello]
[ello ]
[llo W]
[lo Wo]
[o Wor]
[ Worl]
[World]
[orld!]
[rld! ]
[ld! H]
[d! He]
[! Hel]
[ Hell]
Activist
10
Sign #2: (This is the output)
[Activist ]
LOL
2
Sign #3: (This is the output)
[LO]
[OL]
[L ]
[ L]
This is what I want it to do:
Input:
3
Hello World!
5
Activist
10
LOL
2
Output:
Sign #1:
[Hello]
[ello ]
[llo W]
[lo Wo]
[o Wor]
[ Worl]
[World]
[orld!]
[rld! ]
[ld! H]
[d! He]
[! Hel]
[ Hell]
Sign #2:
[Activist ]
Sign #3:
[LO]
[OL]
[L ]
[ L]
Here is my ORIGINAL code:
#include <stdio.h>
#include <string.h>
void ignoreRestOfLine(FILE *fp) {
int c;
while ((c = fgetc(fp)) != EOF && c != '\n');
}
int main() {
int num_times, count = 0;
int marq_length, sign = 0;
scanf("%d ", &num_times);
char s[100];
for (count = 0; count < num_times; count++) {
if (fgets(s, sizeof(s), stdin) == NULL) {
// Deal with error.
}
if (scanf("%d", &marq_length) != 1) {
// Deal with error.
}
ignoreRestOfLine(stdin);
size_t n = strlen(s) - 1;
int i, j;
if (s[strlen(s)-1] == '\n')
s[strlen(s)-1] = '\0';
printf("Sign #%d:\n", ++sign);
if (n <= marq_length) {
printf("[%-*s]\n", marq_length, s);
} else {
for (i = 0; i < n + 1; i++) {
putchar('[');
for (j = 0; j < marq_length; j++) {
char c = s[(i + j) % (n + 1)];
if (!c)
c = ' ';
putchar(c);
}
printf("]\n");
}
}
}
return 0;
}
Here is my UPDATED code, where I added the part of my code that actually outputs the string in a marquee sign into a function. I just don't know how to properly call it back to the main function so it can output all the signs at the very end:
#include <stdio.h>
#include <string.h>
void ignoreRestOfLine(FILE* fp){
int c;
while ( (c = fgetc(fp)) != EOF && c != '\n');
}
char getSign(char s[100], int marq_length);
char getSign(char s[100], int marq_length){
int count =0;
int sign =0;
//char s[100];
if ( fgets(s, sizeof(s), stdin) == NULL )
{
// Deal with error.
}
if ( scanf("%d", &marq_length) != 1 )
{
// Deal with error.
}
ignoreRestOfLine(stdin);
size_t n = strlen(s)-1;
int i,j;
if(s[strlen(s)-1] == '\n')
s[strlen(s)-1] = '\0';
printf("Sign #%d:\n", ++sign);
if (n <= marq_length) {
printf("[%-*s]\n", marq_length, s);
} else {
for (i = 0; i < n + 1; i++) {
putchar('[');
for (j = 0; j < marq_length; j++) {
char c = s[(i + j) % (n + 1)];
if (!c)
c = ' ';
putchar(c);
}
printf("]\n");
}
}
}
int main(){
int i, num_times, sign_length;
char string[100];
scanf("%d", &num_times);
//char *results=malloc(num_times * sizeof(char));
for(i=0 ;i<num_times;i++){
scanf("%s", string);
scanf("%d", &sign_length);
printf((getSign(string, sign_length)));
}
return 0;
}
I think it is good to read all input and then print results because input is short and these size are easy to predict.
Your main() function should be like this
int main(void){
int i, num_times, *sign_length;
char (*string)[100];
/* read number of test cases */
scanf("%d", &num_times);
/* allocate buffer to store input */
sign_length = malloc(sizeof(*sign_length) * num_times);
string = malloc(sizeof(*string) * num_times);
if (sign_length == NULL || string == NULL) {
free(sign_length);
free(string);
return 1;
}
/* read input */
for(i=0 ;i<num_times;i++){
scanf("%99s", string[i]);
scanf("%d", &sign_length[i]);
}
/* process and print */
for(i=0 ;i<num_times;i++){
getSign(string[i], sign_length[i]);
}
/* cleanup */
free(string);
free(sign_length);
return 0;
}
and the part that are trying to destroy the input in getSign()
if ( fgets(s, sizeof(s), stdin) == NULL )
{
// Deal with error.
}
if ( scanf("%d", &marq_length) != 1 )
{
// Deal with error.
}
ignoreRestOfLine(stdin);
have to be deleted.
#include <stdlib.h> have to be added to the head of your code to use malloc() and free().
UPDATE: Here is a program with problems fixed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getSign(char s[100], int marq_length, int case_number);
void getSign(char s[100], int marq_length, int case_number){
size_t n = strlen(s)-1;
size_t i,j;
if (strlen(s) < n) n = 0;
if(s[n] == '\n')
s[n] = '\0';
else
n++;
printf("Sign #%d:\n", case_number);
if (n <= (size_t)marq_length) {
printf("[%-*s]\n", marq_length, s);
} else {
for (i = 0; i < n + 1; i++) {
putchar('[');
for (j = 0; j < (size_t)marq_length; j++) {
char c = s[(i + j) % (n + 1)];
if (!c)
c = ' ';
putchar(c);
}
printf("]\n");
}
}
}
int main(void){
int i, num_times, *sign_length;
char (*string)[100];
/* read number of test cases */
if(scanf("%d", &num_times) != 1) return 1;
/* allocate buffer to store input */
sign_length = malloc(sizeof(*sign_length) * num_times);
string = malloc(sizeof(*string) * num_times);
if (sign_length == NULL || string == NULL) {
free(sign_length);
free(string);
return 1;
}
/* read input */
for(i=0 ;i<num_times;i++){
int dummy;
while((dummy = getchar()) != '\n' && dummy != EOF); /* skip rest of previous line */
if(scanf("%99[^\n]%d", string[i], &sign_length[i]) != 2) {
free(sign_length);
free(string);
return 1;
}
}
/* process and print */
for(i=0 ;i<num_times;i++){
getSign(string[i], sign_length[i], i + 1);
}
/* cleanup */
free(string);
free(sign_length);
return 0;
}
The approach I'd take here is to either store the input until all input is read and only then to generate the output, or to store the output and defer it's printing until the end.
The first is IMHO opinion the better approach, because the amount of data to store is less. OTOH is storing the output a good first step to parallelize taking input and generating output. I go with the later as this is closer to the wording in your question.
OK, your output is basically a C string. So you need to store one of these for each input that you get. Luckily you know even before the first iteration how many inputs you'll get, so you can just allocate the correct amount of space beforehand:
char const ** outputs; // A pointer to (an array of) constant C strings
outputs = malloc(sizeof(*outputs) * numInputs);
Then, you read input as you do it and generate your output into a C string. You've been using a stack allocated (constant size) buffer for that. To be able to reuse that buffer in each iteration you need to copy your result out of it:
char * result = malloc(strlen(sign) + 1);
strcpy(result, sign);
And the store that into your array of outputs:
outputs[currentIteration] = result;
Finally, at the end you print each output and free the allocated memory when done with it:
for (size_t o = 0; o < numOutputs; ++o) {
printf("%s\n", outputs[o]);
free(outputs[o]);
}
At the end, don't forget to also free the array that has been allocated for the outputs:
free(outputs);
Notes: You should check each and every allocation whether it succeeded. Moreover, using a constant sized buffer (on the stack) is OK only if you make absolutely sure that this buffer will never overflow! You should try to pack as much of the above and your sign generation in (small) functions.
Sketch of the overall code:
int main() {
// read how many inputs you'll get
// allocate outputs
// iterate, so that for each input:
// you read the input
// generate the sign
// copy the result into the outputs array
// iterate over all outputs
// print output
// free allocated memory of that output
// free memory for outputs
}
Reading the input and generating the sign should be done in two distinct functions (so its neither the original nor the updated code). Basically I think having these functions should give you a reasonable code structure:
// Read both the string and length, allocating memory for the string
bool read_input(char **string, unsigned * marq_length);
// Could alternatively be done with a fixed buffer
bool read_input(char * string, size_t max_string_size, unsigned * marq_length);
// Note: for bool include stdbool, or use a char instead
And one to generate the sign:
// Store the result in the fixed size buffer at dest, check for overflow!
void generate_sign(char * dest, size_t max_dest_size, char const * input, unsigned marq_length);
Things like printing the outputs could be done directly in main, or in a dedicated function.
I want to know what is the best option to read a txt file that contain two line of numbers using gets function in c and save them in an array within 1 second.
Assume the following example as an txt file called ooo.txt and it has the number 2.000.000 in the first line (which will be the size of the array) and 2.000.000 number in the second line that will be stored in the array.
Eg
2000000
59 595 45 492 89289 5 8959 (+1.999.993 numbers)
code i try (only the fcanf function)
int t_size;
fscanf(fp, "%d",&t_size); //bypass the first character!
int* my_array = NULL;
my_array = malloc(t_size*sizeof(*my_array));
if (my_array==NULL) {
printf("Error allocating memory!\n"); //print an error message
return 1; //return with failure
getchar();
}
int i =0;
for ( i = 0; i < t_size; i++ )
{
fscanf(fp, "%d",&my_array[i]); /*p[i] is the content of element at index i and &p[i] is the address of element at index i */
}
best, so far, code to make the procedure in 1 second
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>
int is_end(char* input) {
return *input == 0;
}
int is_linebreak(char* input) {
return *input == '\r' || *input == '\n' || *input == ' ';
}
char* eat_linebreaks(char* input) {
while (is_linebreak(input))
++input;
return input;
}
size_t count_lines(char* input) {
char* p = input;
size_t rows = 1;
if (is_end(p))
return 0;
while (!is_end(p)) {
if (is_linebreak(p)) {
++rows;
p = eat_linebreaks(p);
}
else {
++p;
}
}
return rows;
}
/* split string by lines */
char** get_lines(char* input, size_t line_count) {
char* p = input;
char* from = input;
size_t length = 0;
size_t line = 0;
int i;
char** lines = (char**)malloc(line_count * sizeof(char*));
do {
if (is_end(p) || is_linebreak(p)) {
lines[line] = (char*)malloc(length + 1);
for (i = 0; i < length; ++i)
lines[line][i] = *(from + i);
lines[line][length] = 0;
length = 0;
++line;
p = eat_linebreaks(p);
from = p;
}
else {
++length;
++p;
}
} while (!is_end(p));
// Copy the last line as well in case the input doesn't end in line-break
lines[line] = (char*)malloc(length + 1);
for (i = 0; i < length; ++i)
lines[line][i] = *(from + i);
lines[line][length] = 0;
++line;
return lines;
}
int main(int argc, char* argv[]) {
clock_t start;
unsigned long microseconds;
float seconds;
char** lines;
size_t size;
size_t number_of_rows;
int count;
int* my_array;
start = clock();
FILE *stream;
char *contents;
int fileSize = 0;
int i;
// Open file, find the size of it
stream = fopen(argv[1], "rb");
fseek(stream, 0L, SEEK_END);
fileSize = ftell(stream);
fseek(stream, 0L, SEEK_SET);
// Allocate space for the entire file content
contents = (char*)malloc(fileSize + 1);
// Stream file into memory
size = fread(contents, 1, fileSize, stream);
contents[size] = 0;
fclose(stream);
// Count rows in content
number_of_rows = count_lines(contents);
// Get array of char*, one for each line
lines = get_lines(contents, number_of_rows);
// Get the numbers out of the lines
count = atoi(lines[0]); // First row has count
my_array = (int*)malloc(count * sizeof(int));
for (i = 0; i < count; ++i) {
my_array[i] = atoi(lines[i + 1]);
}
microseconds = clock() - start;
seconds = microseconds / 1000000.0f;
printf("Took %fs", seconds);
return 0;
}
First of all, you will want to use fgets instead to avoid dangerous buffer overflows. Second, you want to remove all punctuation from your numbers. Thus 2.000.000 becomes 2000000. Then you can use pointers and the strtol function to convert characters to integers; there are also other functions to convert to floats and other types.
Since code wants speed and IO is a typically bottle-neck, reading the entire file at once after using fstat() to find its length (#Charlon) makes some sense. Following is a quick parsing of that buffer.
// Stream file into memory
size = fread(contents, 1, fileSize, stream);
contents[size] = 0;
fclose(stream);
#if 1
// new code
size_t array_n;
int n;
if (sscanf(contents, "%zu%n", &array_n, &n) != 1) Handle_BadInput();
my_array = malloc(array_n * sizeof *my_array);
if (my_array == NULL) Handle_OOM();
char *p = &contents[n];
errno = 0;
char *endptr;
for (size_t count = 0; count < array_n; count++) {
my_array[count] = strtol(p, &endptr, 10);
if (p == endptr || errno)
Handle_BadInput();
p = endptr;
}
char ch;
if (sscanf(p, " %c", &ch) == 1) Handle_ExtraInput();
#else
//old code
// Count rows in content
number_of_rows = count_lines(contents);
// Get array of char*, one for each line
lines = get_lines(contents, number_of_rows);
// Get the numbers out of the lines
count = atoi(lines[0]); // First row has count
my_array = (int*)malloc(count * sizeof(int));
for (i = 0; i < count; ++i) {
my_array[i] = atoi(lines[i + 1]);
}
#endif
Still prefer the scale-able approach of reading one number at a time.
The fastest way needs a lot of RAM :
1) open the file (man open)
2) use the fstat function to get the size of you file (man fstat)
3) read the file with a buffer malloc-ed with the size you just get at 2) (man malloc)
4) close the file (man close)
5) parse your buffer and transform each block of digits (each time until ' ' or '\0') to int
EDIT : if your RAM is not enough large, you need to create a get_next_int function that only stores in your buffer the next number in the file
EDIT 2 : You can read until you know the number of int you will need to store and compares this number with a security coef to the size of your ram, and use the good way so that your program won't set errno to ENOMEM if you know what I'm talking about ;)
I have a program that uses word search. I have a data file which contains the puzzle and the words. What can i implement into my program so that it reads the file and stores the letters present in it as an array?
Example of the data file (it is called testdata):
h e l l o a c d
f g b w o r l d
h a c c v b n a
e q b x n t q q
y e h n c a q r
hello
world
hey
I want to store all the letters in a 2-d array.
Also, I need to store all the words in a 1-dimensional array.
The maximum number of rows of columns or rows that AxA square of letters that is possible in a data file is 25. So, I believe that I should declare an array of that size for the letter and then write them into that array.
I just can't figure out how to read them into that array. There is a space after each letter in the array and no spaces in the words so I think that might be helpful when putting the letters in one array and words in another.
Given your question, and your input, there are a few questions, but in the interest of time, for now, I have made some assumptions about the dimensions of the array, i.e. that it is not necessarily square (as implied by columns or rows that AxA square). The actual data sample disagrees, so I wrote a routine that counts everything as it goes. The letter array is simply an array of arrays, but since it is stored in sequential memory, it just looks like one long array. The strings are each in there own location as well. In any case, this code should illustrate enough to get you on the right track...
#include <ansi_c.h>
#include <stdio.h>
void GetFileContents(char *file, int *nWords, int *lw, int *r, int *c);
void allocMemoryStr(int numStrings, int max);
void allocMemoryLtr(int numStrings, int max);
void freeMemoryStr(int numStrings);
void freeMemoryLtr(int numletters);
#define FILENAME "c:\\dev\\play\\_puzzle.txt"
char **letters;
char **strings;
int main()
{
int longest, cnt, wCount, rows, cols, i;
char line[260];
FILE *fp;
char *buf=0;
GetFileContents(FILENAME, &wCount, &longest, &rows, &cols);
allocMemoryStr(wCount, longest); //for strings
allocMemoryLtr(rows*cols, 1); //for strings
//read file into string arrays
fp = fopen(FILENAME, "r");
cnt=0;
for(i=0;i<rows;i++)
{
fgets(line, 260, fp);
buf = strtok(line, " \n");
while(buf)
{
strcpy(letters[cnt], buf);
buf = strtok(NULL, " \n");
cnt++; //use as accurate count of words.
}
}
cnt=0;
while(fgets(line, 260, fp)) //get remainder of lines into strings
{
//[EDIT]removed fgets()
buf = strtok(line, " \n");
while(buf)
{
strcpy(strings[cnt], buf);
buf = strtok(NULL, " \n");
cnt++; //use as accurate count of words.
}
}
fclose(fp);
freeMemoryStr(wCount);
freeMemoryLtr(rows*cols);
return 0;
}
void GetFileContents(char *file, int *nWords, int *lw, int *r, int *c)
{
char line[260];
FILE *fp;
char *buf=0;
char temp[80];
int wc=0, rc=0, cc=0, ck=0;
fp = fopen(FILENAME, "r");
while(fgets(line, 260, fp))
{
rc++;
buf = strtok(line, " \n");
while(buf)
{
strcpy(temp, buf); // word handler
if(strlen(temp) > 1)
{
wc++;
rc--; //
}
else if(strlen(temp) == 1) //leter handler
{
cc++;
(cc>ck)?(ck=cc):(cc=cc);
}
buf = strtok(NULL, " \n");
}
cc = 0;
}
fclose(fp);
*nWords = wc;
*r = rc;
*c = ck;
}
void allocMemoryStr(int numStrings, int max)
{
int i;
strings = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
strings[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void allocMemoryLtr(int numletters, int max)
{
int i;
letters = calloc(sizeof(char*)*(numletters+1), sizeof(char*));
for(i=0;i<numletters; i++)
{
letters[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void freeMemoryStr(int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(strings[i]) free(strings[i]);
free(strings);
}
void freeMemoryLtr(int numletters)
{
int i;
for(i=0;i<numletters; i++)
if(letters[i]) free(letters[i]);
free(letters);
}
I would parse the file line by line and char by char looking for what i need. In the example (which is untested), i hold three counters to help filling the arrays correctly.
char letters[25][25];
char words[10][25]
int letters_x_pos = 0; // Row counter
int letters_y_pos = 0; // Column counter
int words_pos = 0;
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
letters[i][j] = '\0';
}
}
const char *line;
while (line = some_read_function()) {
if (!(strlen(line) > 1)) {
continue;
}
if (line[1] == ' ') {
// Line contains letters
const char *letter = line;
while (*letter != '\0') {
if (*letter == ' ' || *letter == '\n' || *letter == '\r') {
continue;
}
else {
letters[letters_x_pos][letters_y_pos++] = *letter;
}
if (letters_y_pos == 25) {
// Maximum reached
break;
}
letter++;
}
// Increment row counter and reset column counter
letters_x_pos++;
letters_y_pos = 0;
if (letters_x_pos == 25) {
// Maximum reached
break;
}
}
else {
// Line contains word
strncpy(words[words_pos++], line, 25);
if (words_pos == 25) {
// Maximum reached
break;
}
}
}