I'm trying to read a file like this:
nInp=20
nOut=1
NLaye=3
hid=30
er=0.001
epo=100
epoRep=100
pscpa=0
tip=Aggr
net=emi
numPrec=0.25
prec=NH3;NOX;PM10;SO2;VOC
rag=4
and I must read only the values after the =, and with the prec's values, I must separate every single value (delimited with ;) with a new line and then I write those into a new file like:
NH3
NOX
PM10
SO2
VOC
To read after equals symbolt there is no problems, but I can't to separate price.
This is my function:
void settaggiRete(char values[20][50]){
char line[50];
int i = 0, j = 0;
char str[10][20];
FILE *conf = fopen("conf.txt", "r");
if(conf == NULL){
printf("Impossibile apripre il file conf\n");
exit(EXIT_FAILURE);
}
//Ciclo sulle linee del file di configurazione
while(fgets(line, sizeof(line), configRete) != NULL){
// Leggo i valori contenuti dopo =
if(i==10){
char * str = values[10];
sscanf(line, "%*[^=]=%s", values[i]);
while ((token = strsep(line, ";")) != NULL){
str[j] = token;
j++;
}
}else{
sscanf(line, "%*[^=]=%s", values[i]);
}
i++;
}
fclose(configRete);
}
So How can I separate that values??
You can't assign to the array like this
str[j] = token;
try
strcpy(str[j], token);
althought that is dangerous to do, so you could
size_t length = strlen(token);
if (length >= sizeof(str[j]))
length = sizeof(str[j]) - 1;
memcpy(str[j], token, length);
str[j][length] = '\0';
notice that you are writing safe code at the expense of trimming the token, so a better approach is to use dynamic allocation.
You also, redeclared str inside the loop, so delete this line
char * str = values[10];
which is presumably wrong depending on how you declared values.
do separate at main.
like this :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void settaggiRete(char values[20][50]){
char line[50];
int i = 0;
FILE *conf = fopen("conf.txt", "r");
if(conf == NULL){
printf("Impossibile apripre il file conf\n");
exit(EXIT_FAILURE);
}
while(fgets(line, sizeof(line), conf) != NULL){
sscanf(line, "%*[^=]=%49s", values[i++]);
}
fclose(conf);
}
int main(void){
char values[20][50] = {{0}};
char *value11[25];
int i, v11 = 0;
settaggiRete(values);
for(i=0;i<20;i++){
if(!*values[i])
break;
if(i==11){
char *token, *p = values[11];
int j = 0;
while(token = strsep(&p, ";")){
value11[v11++] = token;
}
for(j = 0; j < v11; ++j){
printf("values[11][%d]=%s\n", j, value11[j]);
}
} else {
printf("values[%d]=%s\n", i, values[i]);
}
}
return 0;
}
Related
I'm trying to read the following file line by line into an array of strings where each line is an element of the array:
AATGC
ATGCC
GCCGT
CGTAC
GTACG
TACGT
ACGTA
CGTAC
GTACG
TACGA
ACGAA
My code is as follows:
void **get_genome(char *filename) {
FILE *file = fopen(filename, "r");
int c;
int line_count = 0;
int line_length = 0;
for (c = getc(file); c != EOF; c = getc(file)) {
if (c == '\n') line_count++;
else line_length++;
}
line_length /= line_count;
rewind(file);
char **genome = calloc(line_length * line_count, sizeof(char));
for (int i = 0; i < line_count; i++) {
genome[i] = calloc(line_length, sizeof(char));
fscanf(file, "%s\n", genome[i]);
}
printf("%d lines of %d length\n", line_count, line_length);
for (int i = 0; i < line_count; i++)
printf("%s\n", genome[i]);
}
However, for some reason I get garbage output for the first 2 elements of the array. The following is my output:
`NP��
�NP��
GCCGT
CGTAC
GTACG
TACGT
ACGTA
CGTAC
GTACG
TACGA
ACGAA
You seem to assume that all lines have the same line length. If such is the case, you still have some problems:
the memory for the row pointers is allocated incorrectly, it should be
char **genome = calloc(line_count, sizeof(char *));
or better and less error prone:
char **genome = calloc(line_count, sizeof(*genome));
the memory for each row should be one byte longer the the null terminator.
\n is the fscanf() format string matches any sequence of whitespace characters. It is redundant as %s skips those anyway.
it is safer to count items separated by white space to avoid miscounting the items if the file contains any blank characters.
you do not close file.
you do not return the genome at the end of the function
you do not check for errors.
Here is a modified version:
void **get_genome(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL)
return NULL;
int line_count = 1;
int item_count = 0;
int item_length = -1;
int length = 0;
int c;
while ((c = getc(file)) != EOF) {
if (isspace(c)) {
if (length == 0)
continue; // ignore subsequent whitespace
item_count++;
if (item_length < 0) {
item_length = length;
} else
if (item_length != length) {
printf("inconsistent item length on line %d\", line_count);
fclose(file);
return NULL;
}
length = 0;
} else {
length++;
}
}
if (length) {
printf("line %d truncated\n", line_count);
fclose(file);
return NULL;
}
rewind(file);
char **genome = calloc(item_count, sizeof(*genome));
if (genome == NULL) {
printf("out of memory\n");
fclose(file);
return NULL;
}
for (int i = 0; i < item_count; i++) {
genome[i] = calloc(item_length + 1, sizeof(*genome[i]));
if (genome[i] == NULL) {
while (i > 0) {
free(genome[i]);
}
free(genome);
printf("out of memory\n");
fclose(file);
return NULL;
}
fscanf(file, "%s", genome[i]);
}
fclose(file);
printf("%d items of %d length on %d lines\n",
item_count, item_length, line_count);
for (int i = 0; i < item_count; i++)
printf("%s\n", genome[i]);
return genome;
}
char **genome = calloc(line_length * line_count, sizeof(char));
must be
char **genome = calloc(line_count, sizeof(char*));
or more 'secure'
char **genome = calloc(line_count, sizeof(*genome));
in case you change the type of genome
else the allocated block if not enough long if you are in 64b because line_count is 5 rather than 8, so you write out of it with an undefined behavior
You also need to return genome at the end of the function
It was also possible to not count the number of lines and to use realloc to increment your array when reading the file
As I see the lines have the same length. Your function should inform the caller how many lines have been read. There is no need of reading the file twice. There is no need of calloc (which is more expensive function). Always check the result of the memory allocation functions.
Here is a bit different version of the function:
char **get_genome(char *filename, size_t *line_count) {
FILE *file = fopen(filename, "r");
int c;
size_t line_length = 0;
char **genome = NULL, **tmp;
*line_count = 0;
if(file)
{
while(1)
{
c = getc(file);
if( c == EOF || c == '\n') break;
line_length++;
}
rewind(file);
while(1)
{
char *line = malloc(line_length + 1);
if(line)
{
if(!fgets(line, line_length + 1, file))
{
free(line);
break;
}
line[line_length] = 0;
tmp = realloc(genome, (*line_count + 1) * sizeof(*genome));
if(tmp)
{
genome = tmp;
genome[*line_count] = line;
*line_count += 1;
}
else
{
// do some memory free magic
}
}
}
fclose(file);
}
return genome;
}
I'm trying to find all the words with capital letters in a string, but am unable to process my data structure. i seem to be able to print out fileContent, indicating that it is loading in successfully, but my second function is not working on the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* loadFile(char* fileName)
{
FILE *inputFile;
inputFile = fopen(fileName, "r");
//finds the end of the file
fseek(inputFile, 0, SEEK_END);
//stores the size of the file
int size = ftell(inputFile);
//Sets the scan to the start of the file
fseek(inputFile, 0, SEEK_SET);
char *documentStore = (char*)malloc(size);
int i = 0, c;
while((c = fgetc(inputFile)) != EOF)
{
documentStore[i] = c;
i++;
}
return documentStore;
}
void countImportantWords(char* fileContent, char** importantWords, int* frequencyWords)
{
int uniqueWordCount = 0;
int lengthWordStore = 10;
int i = 0;
int recording = 0;
char wordBuffer[50];
int wordBufferCount = 0;
int isWordPresent = 0;
while(fileContent[i] != EOF)
{
//To allocate more memory incase the structure is full
if(uniqueWordCount == lengthWordStore)
{
lengthWordStore += 10;
char** newWordStore = realloc(importantWords, lengthWordStore * sizeof(char*));
int* newFrequencyStore = realloc(frequencyWords, sizeof(int));
importantWords = newWordStore;
frequencyWords = newFrequencyStore;
}
printf("%s", wordBuffer);
//Conditions to fill if its a word
if(fileContent[i] >= 'A' && fileContent[i] <= 'Z' && recording == 0)
{
wordBuffer[0] = fileContent[i];
recording = 1;
}else if(fileContent[i] >= 'a' && fileContent[i] <= 'z' && recording == 1)
{
//each if is to check if the end of word is reached. Any character that is non alphabetical is considered end of word
wordBufferCount += 1;
wordBuffer[wordBufferCount] = fileContent[i];
} else if (fileContent[i] >= 'A' && fileContent[i] <= 'Z' && recording == 1)
{
wordBufferCount += 1;
wordBuffer[wordBufferCount] = fileContent[i];
} else {
//Adding a terminating character so that it strcpy only copies until that point
wordBuffer[wordBufferCount + 1] = '\0';
recording = 0;
//check to see if that word is in the array already, and if it is, it will just increment the frequency
for(int j = 0; j < uniqueWordCount; j++){
if(strcmp(wordBuffer, importantWords[j]) == 0)
{
frequencyWords[j] += 1;
isWordPresent = 1;
}
}
//if its not present, it should assign it to the structure
if(isWordPresent == 0)
{
char* wordStore = (char*)malloc(wordBufferCount * sizeof(char));
strcpy(wordStore, wordBuffer);
uniqueWordCount += 1;
importantWords[uniqueWordCount] = wordStore;
frequencyWords[uniqueWordCount] = 1;
}
}
i++;
}
}
int main() {
char fileName[50];
char *fileContent;
char **importantWords = (char**)malloc(10*sizeof(char**));
int *frequencyWords = (int*)malloc(10*sizeof(int));
printf("Please input the full file path: ");
scanf("%s", fileName);
fileContent = loadFile(fileName);
countImportantWords(fileContent, importantWords, frequencyWords);
int i = 0;
while(importantWords[i] != '\0')
{
printf("%s %d", importantWords[i], frequencyWords[i]);
i++;
}
return 0;
}
I've put in the full file so you can see how the structure was created incase that it is the issue, but ideally what would happen is that the final loop would print out all the words that are important and they're frequency. Currently i'm getting exit code 11, which i'm not sure what it means, but may be worth mentioning. I'd really appreciate any help :)
You can simplify the process dramatically but utilising functions and learning to manage your memory. I wrote a short example which does not take punctuation into account. It just assumes every word is separated by a space, which you can customise to your discretion.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char* readfile(char* filename){
char* data = NULL;
FILE* file = fopen(filename, "r");
if(file == NULL){
return NULL;
}
fseek(file, 0, SEEK_END);
long size = ftell(file)+1;
fseek(file, 0, SEEK_SET);
data = (char*)malloc(size);
if(data == NULL){
return NULL;
}
fgets(data, (int)size, file);
return data;
}
typedef struct uppercase_t{
char** word;
int count;
}uppercase;
void copy(uppercase* u,char* token){
size_t length = strlen(token);
u->word[u->count] = (char*)malloc(length+1);
if(u->word[u->count] == NULL){
return;
}
strcpy(u->word[u->count], token);
++u->count;
}
void createuppercasedata(uppercase* u, char* data){
const char delimeter[] = " ";
char* token = strtok(data, delimeter);
if(token == NULL){
return;
}
u->word = (char**)malloc(u->count+1);
if(u->word == NULL){
return;
}
if(isupper(token[0])){
copy(u,token);
}
while(token != NULL){
token = strtok(0, delimeter);
if(token != NULL)
if(isupper(token[0])) {
char** reallocated = (char**)realloc(u->word, u->count+1);
if(reallocated == NULL){
return;
}
u->word = reallocated;
copy(u, token);
}
}
}
void destroyuppercasedata(uppercase* u){
for(int index = 0; index < u->count; ++index){
free(u->word[index]);
}
free(u->word);
}
int main(){
char filename[] = "textfile";
char* data = readfile(filename);
if(data == NULL){
return -1;
}
uppercase u = {0};
createuppercasedata(&u, data);
printf("found %i uppercase words\n",u.count);
for(int index = 0; index < u.count; ++index){
printf("%s\n", u.word[index]);
}
destroyuppercasedata(&u);
free(data);
}
The code will allocate a new pointer for each uppercase and memory for the word to be copied too. It will free all the memory it allocated in the structure with destroyuppercasedata and it will free the initial data that was read from file. Error checking and memory management in C is really important. So utilise those properly.
This was the test file I used.
textfile
How many Uppercase words can Be Found In this text File the answer should be Seven
And this was the output to the terminal:
How
Uppercase
Be
Found
In
File
Seven
I want to change my input.txt file to an integer array.
But sadly I keep missing one integer whenever new-line-character is met.
Following is my main()
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
val = convert(STRING);
return 0;
}
Following is my file input function
char *readFile() {
int count;
FILE *fp;
fp = fopen("input.txt", "r");
if(fp==NULL) printf("File is NULL!n");
char* STRING;
char oneLine[255];
STRING = (char*)malloc(255);
assert(STRING!=NULL);
while(1){
fgets(oneLine, 255, fp);
count += strlen(oneLine);
STRING = (char*)realloc(STRING, count+1);
strcat(STRING, oneLine);
if(feof(fp)) break;
}
fclose(fp);
return STRING;
}
Following is my integer array function
int *convert(char *STRING){
int *intarr;
intarr = (int*)malloc(sizeof(int)*16);
int a=0;
char *ptr = strtok(STRING, " ");
while (ptr != NULL){
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " ");
}
return intarr;
}
There are many issues.
This is a corrected version of your program, all comments are mine. Minimal error checking is done for brevity. intarr = malloc(sizeof(int) * 16); will be a problem if there are more than 16 numbers in the file, this should be handled somehow, for example by growing intarr with realloc, similar to what you're doing in readFile.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
char *readFile() {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL)
{
printf("File is NULL!n");
return NULL; // abort if file could not be opened
}
#define MAXLINELENGTH 255 // define a constant rather than hardcoding "255" at several places
char* STRING;
char oneLine[MAXLINELENGTH];
STRING = malloc(MAXLINELENGTH);
int count = MAXLINELENGTH; // count mus be initialized and better declare it here
assert(STRING != NULL);
STRING[0] = 0; // memory pointed by STRING must be initialized
while (fgets(oneLine, MAXLINELENGTH, fp) != NULL) // correct usage of fgets
{
count += strlen(oneLine);
STRING = realloc(STRING, count + 1);
strcat(STRING, oneLine);
}
fclose(fp);
return STRING;
}
int *convert(char *STRING, int *nbofvalues) { // nbofvalues for returning the number of values
int *intarr;
intarr = malloc(sizeof(int) * 16);
int a = 0;
char *ptr = strtok(STRING, " \n"); // strings may be separated by '\n', or ' '
*nbofvalues = 0;
while (ptr != NULL) {
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " \n"); // strings are separated by '\n' or ' '
} // read the fgets documentation which
// terminates read strings by \n
*nbofvalues = a; // return number of values
return intarr;
}
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
if (STRING == NULL)
{
printf("readFile() problem\n"); // abort if file could not be read
return 1;
}
int nbvalues;
val = convert(STRING, &nbvalues); // nbvalues contains the number of values
// print numbers
for (int i = 0; i < nbvalues; i++)
{
printf("%d: %d\n", i, val[i]);
}
free(val); // free memory
free(STRING); // free memory
return 0;
}
I'm not sure what your requirement is, but this can be simplified a lot because there is no need to read the file into memory and then convert the strings into number. You could convert the numbers on the fly as you read them. And as already mentioned in a comment, calling realloc for each line is inefficient. There is room for more improvements.
I have a csv file having values
1,A,X
2,B,Y
3,C,Z
I have to read the CSV file line by line and keep it in a Structure array.
The values are going fine each time in the for loop. But at the end when I am printing the Array, only the last value is being printed.
Somebody please tell me where am I doing the logical error?
struct proc
{
char *x;
char *y;
};
void main()
{
fflush(stdin);
fflush(stdout);
const char s[2] = ",";
char *token;
int rows=0,i,tokenVal=0,rowCount=0;
FILE *fpCount = fopen("data.csv","r");
if(fpCount != NULL)
{
char lineCount[20];
while(fgets(lineCount, sizeof lineCount, fpCount))
rows++;
}
struct proc *pi[rows];
for(i=0;i<rows;i++)
pi[i] = (struct proc*) malloc(sizeof(struct proc));
FILE *fp = fopen("data.csv", "r");
if(fp != NULL)
{
char line[20];
while(fgets(line, sizeof line, fp) != NULL)
{
printf("Start rowCount = %d\t",rowCount);
token = strtok(line, s);
while(token!=NULL)
{
if(tokenVal==0)
{
pi[rowCount]->Id =token;
}
if(tokenVal==1)
{
pi[rowCount]->act = token;
}
printf("\n");
tokenVal++;
token = strtok(NULL,s);
}
tokenVal = 0;
printf("end rowCount = %d\t",rowCount);
rowCount++;
}
fclose(fp);
} else {
perror("data.csv");
}
printf("total %d",rowCount);
int k=0;
for(k=0;k<rowCount;k++)
{
printf(" %d = %s----%s",k,pi[k]->Id,pi[k]->act);
}
}
Diagnosis
The fundamental problem you face is that you are saving pointers to the variable line in your structures, but each new line overwrites what was previously in line, so at the end, only data from the last line is present. It is fortuitous that your lines of data are all the same 'shape'; if the fields were of different lengths, you'd have more interesting, but equally erroneous, results.
Consequently, you need to save a copy of each field, not simply a pointer to the field. The simple way to do that is with POSIX function strdup(). If you don't have the function, you can create it:
char *strdup(const char *str)
{
size_t len = strlen(str) + 1;
char *rv = malloc(len);
if (rv != 0)
memmove(rv, str, len); // or memcpy
return rv;
}
Your code doesn't compile; your data structure has elements x and y but your code uses elements Id and act. You use a VLA of pointers to your struct proc, but it would be sensible to allocate an array of the structure, either as a VLA or via malloc() et al. You should check memory allocations — there isn't a way to check VLAs, though (one reason to use dynamic allocation instead). You could rewind the file instead of reopening it. (It's a good idea to use a variable to hold the file name, even if you only open it once; it makes error reporting better. Also, errors should stop the program, in general, though you did use perror() if the reopen operation failed — but not if the open failed.) You don't need two arrays into which to read the lines. It's a good idea to use far longer buffers for input lines. You should free dynamically allocated memory. Also, see What should main() return in C and C++?; the answer is int and not void (unless perhaps you are on Windows).
Here are three variants of your code, with various aspects of the issues outlined above more or less fixed.
VLA of pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int rows = 0, rowCount = 0;
FILE *fpCount = fopen(datafile, "r");
if (fpCount == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char lineCount[2000];
while (fgets(lineCount, sizeof(lineCount), fpCount))
rows++;
fclose(fpCount);
printf("Read %d rows from '%s'\n", rows, datafile);
struct proc *pi[rows];
for (int i = 0; i < rows; i++)
pi[i] = (struct proc *)malloc(sizeof(struct proc));
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to reopen '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount]->x = strdup(token);
}
else if (tokenVal == 1)
{
pi[rowCount]->y = strdup(token);
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k]->x, pi[k]->y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k]->x);
free(pi[k]->y);
free(pi[k]);
}
return 0;
}
VLA of structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int rows = 0, rowCount = 0;
FILE *fpCount = fopen(datafile, "r");
if (fpCount == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char lineCount[2000];
while (fgets(lineCount, sizeof(lineCount), fpCount))
rows++;
fclose(fpCount);
printf("Read %d rows from '%s'\n", rows, datafile);
struct proc pi[rows];
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to reopen '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount].x = strdup(token);
}
else if (tokenVal == 1)
{
pi[rowCount].y = strdup(token);
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k].x, pi[k].y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k].x);
free(pi[k].y);
}
return 0;
}
Dynamic array of structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int num_rows = 0, rowCount = 0;
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp))
num_rows++;
rewind(fp);
printf("Read %d rows from '%s'\n", num_rows, datafile);
struct proc *pi = calloc(num_rows, sizeof(*pi));
if (pi == 0)
{
fprintf(stderr, "Failed to allocate %zu bytes of memory\n", num_rows * sizeof(*pi));
exit(EXIT_FAILURE);
}
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount].x = strdup(token);
// null check
}
else if (tokenVal == 1)
{
pi[rowCount].y = strdup(token);
// null check
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k].x, pi[k].y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k].x);
free(pi[k].y);
}
free(pi);
return 0;
}
Given a data file:
1,A,X
2,B,Y
3,C,Z
3192-2146-9913,Abelone,Zoophyte
all three programs produce the same output:
Read 4 rows from 'data.csv'
Start rowCount = 0 [1][A][X] end rowCount = 0
Start rowCount = 1 [2][B][Y] end rowCount = 1
Start rowCount = 2 [3][C][Z] end rowCount = 2
Start rowCount = 3 [3192-2146-9913][Abelone][Zoophyte] end rowCount = 3
total 4
0 = [1]----[A]
1 = [2]----[B]
2 = [3]----[C]
3 = [3192-2146-9913]----[Abelone]
In the printf(" %d = %s----%s----%s",k,pi[k]->Id,pi[k]->act);
There are four data
%d
%s
%s
%s
but you set only three
k
pi[k]->Id
pi[k]->act
Struggling to move tokens to a 2D array .
The idea is that I am reading a file with multiple lines , get the number of lines and then based on that create a 2D array to use memory wisely(I dont want to create a 100 x 3 array for no reason).
I think I got the 2D array initialized in a separate funtion but when I try to enter data read from strtok() , I am getting error :
error: 'arr' undeclared (first use in this function)
strcpy(&arr[s2][c2],token);
Here is my code :
#include <stdio.h>
#include <string.h>
int ch, lines;
int no_of_lines(char* fp)
{
while(!feof(fp)) {
ch = fgetc(fp);
if(ch == '\n') {
lines++;
}
}
lines++;
return lines;
}
void declare_space_array(int size)
{
char* arr = (char*)malloc(size * 3 * sizeof(char));
return;
}
int main(void)
{
int c2 = 0;
int s2 = 0;
int len;
// char data[10][4];
static const char filename[] = "C:\\Users\\PC\\Documents\\Assignments\\stringops\\test.txt";
FILE* file = fopen(filename, "r");
no_of_lines(file);
printf("No of lines in file = %d", lines);
printf("\n");
// Closing file because it was read once till the end of file
fclose(file);
// Opening file again to read for parsing
file = fopen(filename, "r");
declare_space_array(lines);
char* token;
if(file != NULL) {
char line[128];
while(fgets(line, sizeof line, file) != NULL)
{
len = strlen(line);
printf("%d %s", len - 1, line);
const char s = ",";
token = strtok(line, ",");
while(token != NULL) {
strcpy(arr[s2][c2], token);
// printf( "%s\n", token );
token = strtok(NULL, ",");
c2++;
}
s2++;
}
fclose(file);
} else {
perror(filename); /* why didn't the file open? */
}
for(r1 = 0; r1 < lines; r1++) {
for(c1 = 0; c1 < 3; c1++) {
printf("%s", &arr[r1][c1]);
}
}
return 0;
}
file is something like this:
A1,B1,C1
A2,B2,C2
A3,B3,C3
EXPECTED OUTPUT TO SOMETHIGN LIKE THIS:
A1
B1
C1
A2
B2
C2
A3
B3
C3
After discussion in chat, etc, you could end up with code like this. This uses a global variable arr that's a pointer to an array of arrays of 3 char * values.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int lines = 0;
static char *(*arr)[3] = 0; // global definition.
static int no_of_lines(FILE *fp)
{
lines = 0;
int ch;
while ((ch = fgetc(fp)) != EOF)
{
if (ch == '\n')
lines++;
}
return ++lines; // Allow for last line possibly not having a newline
}
static void declare_space_array(int size)
{
arr = calloc(size, 3 * sizeof(char *)); // zeroed memory allocation
if (arr == 0)
{
fprintf(stderr, "Failed to allocate memory\n");
exit(1);
}
}
int main(void)
{
int c2 = 0;
int s2 = 0;
int len;
// char data[10][4];
// static const char filename[] = "C:\\Users\\PC\\Documents\\Assignments\\stringops\\test.txt";
const char *filename = "data";
FILE *file = fopen(filename, "r");
if (file == 0)
{
fprintf(stderr, "Failed to open file '%s' for reading\n", filename);
exit(1);
}
no_of_lines(file);
printf("No of lines in file = %d\n", lines);
rewind(file);
declare_space_array(lines);
const char delims[] = ",\n";
char line[128];
while (fgets(line, sizeof line, file) != NULL)
{
char *token;
c2 = 0;
len = strlen(line);
printf("%d [%.*s]\n", len - 1, len - 1, line);
token = strtok(line, delims);
while (token != NULL)
{
arr[s2][c2] = strdup(token); // copy token (from strtok) into newly allocated string.
token = strtok(NULL, delims);
c2++;
}
s2++;
}
fclose(file);
for (int r1 = 0; r1 < lines; r1++)
{
if (arr[r1][0] != 0)
{
for (int c1 = 0; c1 < 3; c1++)
printf(" %-10s", arr[r1][c1]);
putchar('\n');
}
}
return 0;
}
It doesn't release the memory that's allocated — I got lazy.
Sample data (note that the names are longer than 2 characters and are of variable length):
server1,Phoenix,Windows
server2,Dallas,Linux
server-99,London,z/OS
Sample output:
No of lines in file = 4
23 [server1,Phoenix,Windows]
20 [server2,Dallas,Linux]
21 [server-99,London,z/OS]
server1 Phoenix Windows
server2 Dallas Linux
server-99 London z/OS
The 'number of lines in file = 4' allows for the possibility that there isn't a newline at the end of the last line. The code in the printing loop allows for the possibility that there was a newline at the end and therefore the count is an over-estimate. It would spot a memory allocation from strdup() as long as the failure was on the first field of a line. It might crash if it was the second or third field that was not successfully copied.