Tokenizer not working - c

I am trying to tokenize a string to give an array of strings but it seems like my code is wrong.
Here is my code:
asmInstruction *tokenizeLine(char *charLine) {
int words = countTokens(charLine);
char *tokens = (char*) malloc(MAX_LINE_LENGTH);
asmInstruction *instr = (asmInstruction*) malloc(sizeof(asmInstruction*));
instr->args = (char**) malloc(MAX_LINE_LENGTH);
int count = 1;
tokens = strtok(charLine, " ,");
while (count <= words) {
tokens = strtok(NULL, " ,");
instr->args[count - 1] = (char*)malloc(MAX_LINE_LENGTH);
instr->args[count - 1] = tokens;
++count;
}
free(tokens);
return instr;
}
/* Reads a file and returns the number of lines in this file. */
uint32_t countLines(FILE *file) {
uint32_t lines = 0;
int32_t c;
while (EOF != (c = fgetc(file))) {
if (c == '\n') {
++lines;
}
}
/* Reset the file pointer to the start of the file */
rewind(file);
return lines;
}
And the structure:
typedef struct {
char **args; /* An array of strings*/
} asmInstruction;
My main is here:
int main() {
char s[] = "ldr r2,r1";
asmInstruction *instr = tokenizeLine(s);
printf("%s", instr->args[0]);
}
/* Counts the number of tokens in a line */
uint32_t countTokens(char line[]) {
/* The correct way to do this! */
uint32_t numberOfTokens = 0;
/* Split at spaces and commas */
char *tokens = strtok(line, " ,");
while (tokens != NULL) {
tokens = strtok(NULL, " ,");
numberOfTokens++;
}
return numberOfTokens;
}
So, this should print ldr.
However, it prints null.
If I loop over the tokens it doesn't print them out but null.
I'm expecting to print out the tokens
ldr
r2
r1
But only the first one gets printed out.
It seems like instr->args[count-1] never gets assigned something because apparently tokens hasn't been assigned something either.
Why is that?
Thanks.

the following code:
handles errors
has many/ most of the logic errors corrected
properly defines the struct asmInstruction
performs the functionality indicated in the question.
suggest elimination of the struct asmInstruction as it is not needed, just use a char** args = NULL; in the tokenizeLine() function and return args.
it is not necessary, nor desirable to malloc memory for 'tokens'. Because that memory pointer will be overlayed each time 'tokens' is set from the returned value from strtok()
If a malloc is done, then there will be a memory leak.
In the following code, there still needs to be some
additional logic for freeing malloc'd memory and for closing the file before calling 'exit( EXIT_FAILURE );'
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
struct asmInstruction
{
char **args; /* An array of strings*/
};
#define MAX_LINE_LENGTH (100)
// prototypes
uint32_t countTokens(char line[]);
uint32_t countLines(FILE *file);
struct asmInstruction *tokenizeLine(char *charLine);
int main( void )
{
char s[] = "ldr r2,r1";
struct asmInstruction *instr = tokenizeLine(s);
printf("%s", instr->args[0]);
return( 0 );
} // end function: main
/* Counts the number of tokens in a line */
uint32_t countTokens(char line[])
{
/* The correct way to do this! */
uint32_t numberOfTokens = 0;
/* Split at spaces and commas */
char *tokens = strtok(line, " ,");
while (tokens != NULL)
{
tokens = strtok(NULL, " ,");
numberOfTokens++;
}
return numberOfTokens;
} // end function: countTokens
struct asmInstruction *tokenizeLine(char *charLine)
{
int words = countTokens(charLine);
char *tokens = NULL;
struct asmInstruction *instr = NULL;
if( NULL == (instr = malloc(sizeof( struct asmInstruction)) ) )
{ // then malloc failed
perror( "malloc for struct asmInstruction failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
instr->args = NULL;
if( NULL == (instr->args = malloc(words*sizeof(char*)) ) )
{ // then malloc failed
perror( "malloc for array of char pointers failed:" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
memset( instr->args, '\0', words*sizeof(char*) );
int count = 0;
tokens = strtok(charLine, " ,");
while ( tokens )
{
if( NULL == (instr->args[count] = malloc(strlen(tokens)+1) ) )
{ // then, malloc failed
perror( "malloc for arg failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
strcpy(instr->args[count], tokens );
++count;
tokens = strtok(NULL, " ,");
} // end while
return instr;
} // end function: tokenizeLine
/* Reads a file and returns the number of lines in this file. */
uint32_t countLines(FILE *file)
{
uint32_t lines = 0;
int32_t c;
while (EOF != (c = fgetc(file)))
{
if (c == '\n') {
++lines;
}
}
/* Reset the file pointer to the start of the file */
rewind(file);
return lines;
} // end function: countLines

asmInstruction *tokenizeLine(char *charLine) {
int words = countTokens(charLine);
char *tokens;//don't need malloc for this, because just pointer holder.
asmInstruction *instr = (asmInstruction*) malloc(sizeof(asmInstruction));//allocate size isn't sizeof(asmInstruction*)
instr->args = (char**) malloc((words+1) * sizeof(char*));//+1 for NULL, or add member E.g instr->numOfWords = words
int count = 0;
tokens = strtok(charLine, " ,");
while (tokens) {
instr->args[count] = malloc(strlen(tokens)+1);
strcpy(instr->args[count++], tokens);
//or process for each line
//instr->args[count++] = tokens;//no need allocate for word
tokens = strtok(NULL, " ,");//get next tokens
}
instr->args[count] = NULL;//set sentinel
return instr;
}

Related

I have two lines of data in text file. I have to read these two lines of data from text file and store it in two different arrays [duplicate]

I have a .csv file:
lp;imie;nazwisko;ulica;numer;kod;miejscowosc;telefon;email;data_ur
1;Jan;Kowalski;ul. Nowa;1a;11-234;Budry;123-123-456;jan#go.xxx;1980.05.13
2;Jerzy;Nowak;ul. Konopnicka;13a/3;00-900;Lichowice;(55)333-44-55;jer#wu.to;1990.03.23
And I need to read this in C. I have some code, but only for the connection.
Hopefully this would get you started
See it live on http://ideone.com/l23He (using stdin)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* getfield(char* line, int num)
{
const char* tok;
for (tok = strtok(line, ";");
tok && *tok;
tok = strtok(NULL, ";\n"))
{
if (!--num)
return tok;
}
return NULL;
}
int main()
{
FILE* stream = fopen("input", "r");
char line[1024];
while (fgets(line, 1024, stream))
{
char* tmp = strdup(line);
printf("Field 3 would be %s\n", getfield(tmp, 3));
// NOTE strtok clobbers tmp
free(tmp);
}
}
Output:
Field 3 would be nazwisko
Field 3 would be Kowalski
Field 3 would be Nowak
The following code is in plain c language and handles blank spaces.
It only allocates memory once, so one free() is needed, for each processed line.
http://ideone.com/mSCgPM
/* Tiny CSV Reader */
/* Copyright (C) 2015, Deligiannidis Konstantinos
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://w...content-available-to-author-only...u.org/licenses/>. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* For more that 100 columns or lines (when delimiter = \n), minor modifications are needed. */
int getcols( const char * const line, const char * const delim, char ***out_storage )
{
const char *start_ptr, *end_ptr, *iter;
char **out;
int i; //For "for" loops in the old c style.
int tokens_found = 1, delim_size, line_size; //Calculate "line_size" indirectly, without strlen() call.
int start_idx[100], end_idx[100]; //Store the indexes of tokens. Example "Power;": loc('P')=1, loc(';')=6
//Change 100 with MAX_TOKENS or use malloc() for more than 100 tokens. Example: "b1;b2;b3;...;b200"
if ( *out_storage != NULL ) return -4; //This SHOULD be NULL: Not Already Allocated
if ( !line || !delim ) return -1; //NULL pointers Rejected Here
if ( (delim_size = strlen( delim )) == 0 ) return -2; //Delimiter not provided
start_ptr = line; //Start visiting input. We will distinguish tokens in a single pass, for good performance.
//Then we are allocating one unified memory region & doing one memory copy.
while ( ( end_ptr = strstr( start_ptr, delim ) ) ) {
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token
end_idx[ tokens_found - 1 ] = end_ptr - line; //Store Index of first character that will be replaced with
//'\0'. Example: "arg1||arg2||end" -> "arg1\0|arg2\0|end"
tokens_found++; //Accumulate the count of tokens.
start_ptr = end_ptr + delim_size; //Set pointer to the next c-string within the line
}
for ( iter = start_ptr; (*iter!='\0') ; iter++ );
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token: of last token here.
end_idx[ tokens_found -1 ] = iter - line; //and the last element that will be replaced with \0
line_size = iter - line; //Saving CPU cycles: Indirectly Count the size of *line without using strlen();
int size_ptr_region = (1 + tokens_found)*sizeof( char* ); //The size to store pointers to c-strings + 1 (*NULL).
out = (char**) malloc( size_ptr_region + ( line_size + 1 ) + 5 ); //Fit everything there...it is all memory.
//It reserves a contiguous space for both (char**) pointers AND string region. 5 Bytes for "Out of Range" tests.
*out_storage = out; //Update the char** pointer of the caller function.
//"Out of Range" TEST. Verify that the extra reserved characters will not be changed. Assign Some Values.
//char *extra_chars = (char*) out + size_ptr_region + ( line_size + 1 );
//extra_chars[0] = 1; extra_chars[1] = 2; extra_chars[2] = 3; extra_chars[3] = 4; extra_chars[4] = 5;
for ( i = 0; i < tokens_found; i++ ) //Assign adresses first part of the allocated memory pointers that point to
out[ i ] = (char*) out + size_ptr_region + start_idx[ i ]; //the second part of the memory, reserved for Data.
out[ tokens_found ] = (char*) NULL; //[ ptr1, ptr2, ... , ptrN, (char*) NULL, ... ]: We just added the (char*) NULL.
//Now assign the Data: c-strings. (\0 terminated strings):
char *str_region = (char*) out + size_ptr_region; //Region inside allocated memory which contains the String Data.
memcpy( str_region, line, line_size ); //Copy input with delimiter characters: They will be replaced with \0.
//Now we should replace: "arg1||arg2||arg3" with "arg1\0|arg2\0|arg3". Don't worry for characters after '\0'
//They are not used in standard c lbraries.
for( i = 0; i < tokens_found; i++) str_region[ end_idx[ i ] ] = '\0';
//"Out of Range" TEST. Wait until Assigned Values are Printed back.
//for ( int i=0; i < 5; i++ ) printf("c=%x ", extra_chars[i] ); printf("\n");
// *out memory should now contain (example data):
//[ ptr1, ptr2,...,ptrN, (char*) NULL, "token1\0", "token2\0",...,"tokenN\0", 5 bytes for tests ]
// |__________________________________^ ^ ^ ^
// |_______________________________________| | |
// |_____________________________________________| These 5 Bytes should be intact.
return tokens_found;
}
int main()
{
char in_line[] = "Arg1;;Th;s is not Del;m;ter;;Arg3;;;;Final";
char delim[] = ";;";
char **columns;
int i;
printf("Example1:\n");
columns = NULL; //Should be NULL to indicate that it is not assigned to allocated memory. Otherwise return -4;
int cols_found = getcols( in_line, delim, &columns);
for ( i = 0; i < cols_found; i++ ) printf("Column[ %d ] = %s\n", i, columns[ i ] ); //<- (1st way).
// (2nd way) // for ( i = 0; columns[ i ]; i++) printf("start_idx[ %d ] = %s\n", i, columns[ i ] );
free( columns ); //Release the Single Contiguous Memory Space.
columns = NULL; //Pointer = NULL to indicate it does not reserve space and that is ready for the next malloc().
printf("\n\nExample2, Nested:\n\n");
char example_file[] = "ID;Day;Month;Year;Telephone;email;Date of registration\n"
"1;Sunday;january;2009;123-124-456;jitter#go.xyz;2015-05-13\n"
"2;Monday;March;2011;(+30)333-22-55;buffer#wl.it;2009-05-23";
char **rows;
int j;
rows = NULL; //getcols() requires it to be NULL. (Avoid dangling pointers, leaks e.t.c).
getcols( example_file, "\n", &rows);
for ( i = 0; rows[ i ]; i++) {
{
printf("Line[ %d ] = %s\n", i, rows[ i ] );
char **columnX = NULL;
getcols( rows[ i ], ";", &columnX);
for ( j = 0; columnX[ j ]; j++) printf(" Col[ %d ] = %s\n", j, columnX[ j ] );
free( columnX );
}
}
free( rows );
rows = NULL;
return 0;
}
A complete example which leaves the fields as NULL-terminated strings in the original input buffer and provides access to them via an array of char pointers. The CSV processor has been confirmed to work with fields enclosed in "double quotes", ignoring any delimiter chars within them.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// adjust BUFFER_SIZE to suit longest line
#define BUFFER_SIZE 1024 * 1024
#define NUM_FIELDS 10
#define MAXERRS 5
#define RET_OK 0
#define RET_FAIL 1
#define FALSE 0
#define TRUE 1
// char* array will point to fields
char *pFields[NUM_FIELDS];
// field offsets into pFields array:
#define LP 0
#define IMIE 1
#define NAZWISKo 2
#define ULICA 3
#define NUMER 4
#define KOD 5
#define MIEJSCOw 6
#define TELEFON 7
#define EMAIL 8
#define DATA_UR 9
long loadFile(FILE *pFile, long *errcount);
static int loadValues(char *line, long lineno);
static char delim;
long loadFile(FILE *pFile, long *errcount){
char sInputBuf [BUFFER_SIZE];
long lineno = 0L;
if(pFile == NULL)
return RET_FAIL;
while (!feof(pFile)) {
// load line into static buffer
if(fgets(sInputBuf, BUFFER_SIZE-1, pFile)==NULL)
break;
// skip first line (headers)
if(++lineno==1)
continue;
// jump over empty lines
if(strlen(sInputBuf)==0)
continue;
// set pFields array pointers to null-terminated string fields in sInputBuf
if(loadValues(sInputBuf,lineno)==RET_FAIL){
(*errcount)++;
if(*errcount > MAXERRS)
break;
} else {
// On return pFields array pointers point to loaded fields ready for load into DB or whatever
// Fields can be accessed via pFields, e.g.
printf("lp=%s, imie=%s, data_ur=%s\n", pFields[LP], pFields[IMIE], pFields[DATA_UR]);
}
}
return lineno;
}
static int loadValues(char *line, long lineno){
if(line == NULL)
return RET_FAIL;
// chop of last char of input if it is a CR or LF (e.g.Windows file loading in Unix env.)
// can be removed if sure fgets has removed both CR and LF from end of line
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1) == '\n')
*(line + strlen(line)-1) = '\0';
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1 )== '\n')
*(line + strlen(line)-1) = '\0';
char *cptr = line;
int fld = 0;
int inquote = FALSE;
char ch;
pFields[fld]=cptr;
while((ch=*cptr) != '\0' && fld < NUM_FIELDS){
if(ch == '"') {
if(! inquote)
pFields[fld]=cptr+1;
else {
*cptr = '\0'; // zero out " and jump over it
}
inquote = ! inquote;
} else if(ch == delim && ! inquote){
*cptr = '\0'; // end of field, null terminate it
pFields[++fld]=cptr+1;
}
cptr++;
}
if(fld > NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) exceeded on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
} else if (fld < NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) not reached on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
}
return RET_OK;
}
int main(int argc, char **argv)
{
FILE *fp;
long errcount = 0L;
long lines = 0L;
if(argc!=3){
printf("Usage: %s csvfilepath delimiter\n", basename(argv[0]));
return (RET_FAIL);
}
if((delim=argv[2][0])=='\0'){
fprintf(stderr,"delimiter must be specified\n");
return (RET_FAIL);
}
fp = fopen(argv[1] , "r");
if(fp == NULL) {
fprintf(stderr,"Error opening file: %d\n",errno);
return(RET_FAIL);
}
lines=loadFile(fp,&errcount);
fclose(fp);
printf("Processed %ld lines, encountered %ld error(s)\n", lines, errcount);
if(errcount>0)
return(RET_FAIL);
return(RET_OK);
}
Use fscanf to read the file until you encounter ';' or \n, then just skip it with fscanf(f, "%*c").
int main()
{
char str[128];
int result;
FILE* f = fopen("test.txt", "r");
/*...*/
do {
result = fscanf(f, "%127[^;\n]", str);
if(result == 0)
{
result = fscanf(f, "%*c");
}
else
{
//Put here whatever you want to do with your value.
printf("%s\n", str);
}
} while(result != EOF);
return 0;
}
This code is fairly simple, but effective. It parses comma-separated files with parenthesis. You can easily modify it to suit your needs.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// argv[1] path to csv file
// argv[2] number of lines to skip
// argv[3] length of longest value (in characters)
FILE *pfinput;
unsigned int nSkipLines, currentLine, lenLongestValue;
char *pTempValHolder;
int c;
unsigned int vcpm; // Value character marker
int QuotationOnOff; // 0 - off, 1 - on
nSkipLines = atoi(argv[2]);
lenLongestValue = atoi(argv[3]);
pTempValHolder = (char*)malloc(lenLongestValue);
if(pfinput = fopen(argv[1], "r")) {
rewind(pfinput);
currentLine = 1;
vcpm = 0;
QuotationOnOff = 0;
// currentLine > nSkipLines condition
// skips / ignores first argv[2] lines
while((c = fgetc(pfinput)) != EOF)
{
switch(c)
{
case ',':
if(!QuotationOnOff && currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s,", pTempValHolder);
vcpm = 0;
}
break;
case '\n':
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s\n", pTempValHolder);
vcpm = 0;
}
currentLine++;
break;
case '\"':
if(currentLine > nSkipLines)
{
if(!QuotationOnOff) {
QuotationOnOff = 1;
pTempValHolder[vcpm] = c;
vcpm++;
} else {
QuotationOnOff = 0;
pTempValHolder[vcpm] = c;
vcpm++;
}
}
break;
default:
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = c;
vcpm++;
}
break;
}
}
fclose(pfinput);
free(pTempValHolder);
}
return 0;
}
#include <conio.h>
#include <stdio.h>
#include <string.h>
// Driver Code
int main()
{
// Substitute the full file path
// for the string file_path
FILE* fp = fopen("Movie.csv", "r");
char *wrds[40];
if (!fp)
printf("Can't open file\n");
else {
// Here we have taken size of
// array 1024 you can modify it
char buffer[1024];
int row = 0;
int column = 0;
while (fgets(buffer, 1024, fp)) {
column = 0;
row++;
// To avoid printing of column
// names in file can be changed
// according to need
if (row == 1)
continue;
// Splitting the data
char* value = strtok(buffer, ", ");
while (value) {
// Column 1
if (column == 0) {
printf("Name :");
}
// Column 2
if (column == 1) {
printf("\tAccount No. :");
}
// Column 3
if (column == 2) {
printf("\tAmount :");
}
printf("%s", value);
wrds[column] = value;
value = strtok(NULL, ", ");
column++;
}
printf("\n");
}
// Close the file
fclose(fp);
}
getchar();
return 0;
}

Read character '-' in scanf [duplicate]

I have a .csv file:
lp;imie;nazwisko;ulica;numer;kod;miejscowosc;telefon;email;data_ur
1;Jan;Kowalski;ul. Nowa;1a;11-234;Budry;123-123-456;jan#go.xxx;1980.05.13
2;Jerzy;Nowak;ul. Konopnicka;13a/3;00-900;Lichowice;(55)333-44-55;jer#wu.to;1990.03.23
And I need to read this in C. I have some code, but only for the connection.
Hopefully this would get you started
See it live on http://ideone.com/l23He (using stdin)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* getfield(char* line, int num)
{
const char* tok;
for (tok = strtok(line, ";");
tok && *tok;
tok = strtok(NULL, ";\n"))
{
if (!--num)
return tok;
}
return NULL;
}
int main()
{
FILE* stream = fopen("input", "r");
char line[1024];
while (fgets(line, 1024, stream))
{
char* tmp = strdup(line);
printf("Field 3 would be %s\n", getfield(tmp, 3));
// NOTE strtok clobbers tmp
free(tmp);
}
}
Output:
Field 3 would be nazwisko
Field 3 would be Kowalski
Field 3 would be Nowak
The following code is in plain c language and handles blank spaces.
It only allocates memory once, so one free() is needed, for each processed line.
http://ideone.com/mSCgPM
/* Tiny CSV Reader */
/* Copyright (C) 2015, Deligiannidis Konstantinos
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://w...content-available-to-author-only...u.org/licenses/>. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* For more that 100 columns or lines (when delimiter = \n), minor modifications are needed. */
int getcols( const char * const line, const char * const delim, char ***out_storage )
{
const char *start_ptr, *end_ptr, *iter;
char **out;
int i; //For "for" loops in the old c style.
int tokens_found = 1, delim_size, line_size; //Calculate "line_size" indirectly, without strlen() call.
int start_idx[100], end_idx[100]; //Store the indexes of tokens. Example "Power;": loc('P')=1, loc(';')=6
//Change 100 with MAX_TOKENS or use malloc() for more than 100 tokens. Example: "b1;b2;b3;...;b200"
if ( *out_storage != NULL ) return -4; //This SHOULD be NULL: Not Already Allocated
if ( !line || !delim ) return -1; //NULL pointers Rejected Here
if ( (delim_size = strlen( delim )) == 0 ) return -2; //Delimiter not provided
start_ptr = line; //Start visiting input. We will distinguish tokens in a single pass, for good performance.
//Then we are allocating one unified memory region & doing one memory copy.
while ( ( end_ptr = strstr( start_ptr, delim ) ) ) {
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token
end_idx[ tokens_found - 1 ] = end_ptr - line; //Store Index of first character that will be replaced with
//'\0'. Example: "arg1||arg2||end" -> "arg1\0|arg2\0|end"
tokens_found++; //Accumulate the count of tokens.
start_ptr = end_ptr + delim_size; //Set pointer to the next c-string within the line
}
for ( iter = start_ptr; (*iter!='\0') ; iter++ );
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token: of last token here.
end_idx[ tokens_found -1 ] = iter - line; //and the last element that will be replaced with \0
line_size = iter - line; //Saving CPU cycles: Indirectly Count the size of *line without using strlen();
int size_ptr_region = (1 + tokens_found)*sizeof( char* ); //The size to store pointers to c-strings + 1 (*NULL).
out = (char**) malloc( size_ptr_region + ( line_size + 1 ) + 5 ); //Fit everything there...it is all memory.
//It reserves a contiguous space for both (char**) pointers AND string region. 5 Bytes for "Out of Range" tests.
*out_storage = out; //Update the char** pointer of the caller function.
//"Out of Range" TEST. Verify that the extra reserved characters will not be changed. Assign Some Values.
//char *extra_chars = (char*) out + size_ptr_region + ( line_size + 1 );
//extra_chars[0] = 1; extra_chars[1] = 2; extra_chars[2] = 3; extra_chars[3] = 4; extra_chars[4] = 5;
for ( i = 0; i < tokens_found; i++ ) //Assign adresses first part of the allocated memory pointers that point to
out[ i ] = (char*) out + size_ptr_region + start_idx[ i ]; //the second part of the memory, reserved for Data.
out[ tokens_found ] = (char*) NULL; //[ ptr1, ptr2, ... , ptrN, (char*) NULL, ... ]: We just added the (char*) NULL.
//Now assign the Data: c-strings. (\0 terminated strings):
char *str_region = (char*) out + size_ptr_region; //Region inside allocated memory which contains the String Data.
memcpy( str_region, line, line_size ); //Copy input with delimiter characters: They will be replaced with \0.
//Now we should replace: "arg1||arg2||arg3" with "arg1\0|arg2\0|arg3". Don't worry for characters after '\0'
//They are not used in standard c lbraries.
for( i = 0; i < tokens_found; i++) str_region[ end_idx[ i ] ] = '\0';
//"Out of Range" TEST. Wait until Assigned Values are Printed back.
//for ( int i=0; i < 5; i++ ) printf("c=%x ", extra_chars[i] ); printf("\n");
// *out memory should now contain (example data):
//[ ptr1, ptr2,...,ptrN, (char*) NULL, "token1\0", "token2\0",...,"tokenN\0", 5 bytes for tests ]
// |__________________________________^ ^ ^ ^
// |_______________________________________| | |
// |_____________________________________________| These 5 Bytes should be intact.
return tokens_found;
}
int main()
{
char in_line[] = "Arg1;;Th;s is not Del;m;ter;;Arg3;;;;Final";
char delim[] = ";;";
char **columns;
int i;
printf("Example1:\n");
columns = NULL; //Should be NULL to indicate that it is not assigned to allocated memory. Otherwise return -4;
int cols_found = getcols( in_line, delim, &columns);
for ( i = 0; i < cols_found; i++ ) printf("Column[ %d ] = %s\n", i, columns[ i ] ); //<- (1st way).
// (2nd way) // for ( i = 0; columns[ i ]; i++) printf("start_idx[ %d ] = %s\n", i, columns[ i ] );
free( columns ); //Release the Single Contiguous Memory Space.
columns = NULL; //Pointer = NULL to indicate it does not reserve space and that is ready for the next malloc().
printf("\n\nExample2, Nested:\n\n");
char example_file[] = "ID;Day;Month;Year;Telephone;email;Date of registration\n"
"1;Sunday;january;2009;123-124-456;jitter#go.xyz;2015-05-13\n"
"2;Monday;March;2011;(+30)333-22-55;buffer#wl.it;2009-05-23";
char **rows;
int j;
rows = NULL; //getcols() requires it to be NULL. (Avoid dangling pointers, leaks e.t.c).
getcols( example_file, "\n", &rows);
for ( i = 0; rows[ i ]; i++) {
{
printf("Line[ %d ] = %s\n", i, rows[ i ] );
char **columnX = NULL;
getcols( rows[ i ], ";", &columnX);
for ( j = 0; columnX[ j ]; j++) printf(" Col[ %d ] = %s\n", j, columnX[ j ] );
free( columnX );
}
}
free( rows );
rows = NULL;
return 0;
}
A complete example which leaves the fields as NULL-terminated strings in the original input buffer and provides access to them via an array of char pointers. The CSV processor has been confirmed to work with fields enclosed in "double quotes", ignoring any delimiter chars within them.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// adjust BUFFER_SIZE to suit longest line
#define BUFFER_SIZE 1024 * 1024
#define NUM_FIELDS 10
#define MAXERRS 5
#define RET_OK 0
#define RET_FAIL 1
#define FALSE 0
#define TRUE 1
// char* array will point to fields
char *pFields[NUM_FIELDS];
// field offsets into pFields array:
#define LP 0
#define IMIE 1
#define NAZWISKo 2
#define ULICA 3
#define NUMER 4
#define KOD 5
#define MIEJSCOw 6
#define TELEFON 7
#define EMAIL 8
#define DATA_UR 9
long loadFile(FILE *pFile, long *errcount);
static int loadValues(char *line, long lineno);
static char delim;
long loadFile(FILE *pFile, long *errcount){
char sInputBuf [BUFFER_SIZE];
long lineno = 0L;
if(pFile == NULL)
return RET_FAIL;
while (!feof(pFile)) {
// load line into static buffer
if(fgets(sInputBuf, BUFFER_SIZE-1, pFile)==NULL)
break;
// skip first line (headers)
if(++lineno==1)
continue;
// jump over empty lines
if(strlen(sInputBuf)==0)
continue;
// set pFields array pointers to null-terminated string fields in sInputBuf
if(loadValues(sInputBuf,lineno)==RET_FAIL){
(*errcount)++;
if(*errcount > MAXERRS)
break;
} else {
// On return pFields array pointers point to loaded fields ready for load into DB or whatever
// Fields can be accessed via pFields, e.g.
printf("lp=%s, imie=%s, data_ur=%s\n", pFields[LP], pFields[IMIE], pFields[DATA_UR]);
}
}
return lineno;
}
static int loadValues(char *line, long lineno){
if(line == NULL)
return RET_FAIL;
// chop of last char of input if it is a CR or LF (e.g.Windows file loading in Unix env.)
// can be removed if sure fgets has removed both CR and LF from end of line
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1) == '\n')
*(line + strlen(line)-1) = '\0';
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1 )== '\n')
*(line + strlen(line)-1) = '\0';
char *cptr = line;
int fld = 0;
int inquote = FALSE;
char ch;
pFields[fld]=cptr;
while((ch=*cptr) != '\0' && fld < NUM_FIELDS){
if(ch == '"') {
if(! inquote)
pFields[fld]=cptr+1;
else {
*cptr = '\0'; // zero out " and jump over it
}
inquote = ! inquote;
} else if(ch == delim && ! inquote){
*cptr = '\0'; // end of field, null terminate it
pFields[++fld]=cptr+1;
}
cptr++;
}
if(fld > NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) exceeded on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
} else if (fld < NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) not reached on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
}
return RET_OK;
}
int main(int argc, char **argv)
{
FILE *fp;
long errcount = 0L;
long lines = 0L;
if(argc!=3){
printf("Usage: %s csvfilepath delimiter\n", basename(argv[0]));
return (RET_FAIL);
}
if((delim=argv[2][0])=='\0'){
fprintf(stderr,"delimiter must be specified\n");
return (RET_FAIL);
}
fp = fopen(argv[1] , "r");
if(fp == NULL) {
fprintf(stderr,"Error opening file: %d\n",errno);
return(RET_FAIL);
}
lines=loadFile(fp,&errcount);
fclose(fp);
printf("Processed %ld lines, encountered %ld error(s)\n", lines, errcount);
if(errcount>0)
return(RET_FAIL);
return(RET_OK);
}
Use fscanf to read the file until you encounter ';' or \n, then just skip it with fscanf(f, "%*c").
int main()
{
char str[128];
int result;
FILE* f = fopen("test.txt", "r");
/*...*/
do {
result = fscanf(f, "%127[^;\n]", str);
if(result == 0)
{
result = fscanf(f, "%*c");
}
else
{
//Put here whatever you want to do with your value.
printf("%s\n", str);
}
} while(result != EOF);
return 0;
}
This code is fairly simple, but effective. It parses comma-separated files with parenthesis. You can easily modify it to suit your needs.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// argv[1] path to csv file
// argv[2] number of lines to skip
// argv[3] length of longest value (in characters)
FILE *pfinput;
unsigned int nSkipLines, currentLine, lenLongestValue;
char *pTempValHolder;
int c;
unsigned int vcpm; // Value character marker
int QuotationOnOff; // 0 - off, 1 - on
nSkipLines = atoi(argv[2]);
lenLongestValue = atoi(argv[3]);
pTempValHolder = (char*)malloc(lenLongestValue);
if(pfinput = fopen(argv[1], "r")) {
rewind(pfinput);
currentLine = 1;
vcpm = 0;
QuotationOnOff = 0;
// currentLine > nSkipLines condition
// skips / ignores first argv[2] lines
while((c = fgetc(pfinput)) != EOF)
{
switch(c)
{
case ',':
if(!QuotationOnOff && currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s,", pTempValHolder);
vcpm = 0;
}
break;
case '\n':
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s\n", pTempValHolder);
vcpm = 0;
}
currentLine++;
break;
case '\"':
if(currentLine > nSkipLines)
{
if(!QuotationOnOff) {
QuotationOnOff = 1;
pTempValHolder[vcpm] = c;
vcpm++;
} else {
QuotationOnOff = 0;
pTempValHolder[vcpm] = c;
vcpm++;
}
}
break;
default:
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = c;
vcpm++;
}
break;
}
}
fclose(pfinput);
free(pTempValHolder);
}
return 0;
}
#include <conio.h>
#include <stdio.h>
#include <string.h>
// Driver Code
int main()
{
// Substitute the full file path
// for the string file_path
FILE* fp = fopen("Movie.csv", "r");
char *wrds[40];
if (!fp)
printf("Can't open file\n");
else {
// Here we have taken size of
// array 1024 you can modify it
char buffer[1024];
int row = 0;
int column = 0;
while (fgets(buffer, 1024, fp)) {
column = 0;
row++;
// To avoid printing of column
// names in file can be changed
// according to need
if (row == 1)
continue;
// Splitting the data
char* value = strtok(buffer, ", ");
while (value) {
// Column 1
if (column == 0) {
printf("Name :");
}
// Column 2
if (column == 1) {
printf("\tAccount No. :");
}
// Column 3
if (column == 2) {
printf("\tAmount :");
}
printf("%s", value);
wrds[column] = value;
value = strtok(NULL, ", ");
column++;
}
printf("\n");
}
// Close the file
fclose(fp);
}
getchar();
return 0;
}

How to read a file separated by commas [duplicate]

I have a .csv file:
lp;imie;nazwisko;ulica;numer;kod;miejscowosc;telefon;email;data_ur
1;Jan;Kowalski;ul. Nowa;1a;11-234;Budry;123-123-456;jan#go.xxx;1980.05.13
2;Jerzy;Nowak;ul. Konopnicka;13a/3;00-900;Lichowice;(55)333-44-55;jer#wu.to;1990.03.23
And I need to read this in C. I have some code, but only for the connection.
Hopefully this would get you started
See it live on http://ideone.com/l23He (using stdin)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* getfield(char* line, int num)
{
const char* tok;
for (tok = strtok(line, ";");
tok && *tok;
tok = strtok(NULL, ";\n"))
{
if (!--num)
return tok;
}
return NULL;
}
int main()
{
FILE* stream = fopen("input", "r");
char line[1024];
while (fgets(line, 1024, stream))
{
char* tmp = strdup(line);
printf("Field 3 would be %s\n", getfield(tmp, 3));
// NOTE strtok clobbers tmp
free(tmp);
}
}
Output:
Field 3 would be nazwisko
Field 3 would be Kowalski
Field 3 would be Nowak
The following code is in plain c language and handles blank spaces.
It only allocates memory once, so one free() is needed, for each processed line.
http://ideone.com/mSCgPM
/* Tiny CSV Reader */
/* Copyright (C) 2015, Deligiannidis Konstantinos
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://w...content-available-to-author-only...u.org/licenses/>. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* For more that 100 columns or lines (when delimiter = \n), minor modifications are needed. */
int getcols( const char * const line, const char * const delim, char ***out_storage )
{
const char *start_ptr, *end_ptr, *iter;
char **out;
int i; //For "for" loops in the old c style.
int tokens_found = 1, delim_size, line_size; //Calculate "line_size" indirectly, without strlen() call.
int start_idx[100], end_idx[100]; //Store the indexes of tokens. Example "Power;": loc('P')=1, loc(';')=6
//Change 100 with MAX_TOKENS or use malloc() for more than 100 tokens. Example: "b1;b2;b3;...;b200"
if ( *out_storage != NULL ) return -4; //This SHOULD be NULL: Not Already Allocated
if ( !line || !delim ) return -1; //NULL pointers Rejected Here
if ( (delim_size = strlen( delim )) == 0 ) return -2; //Delimiter not provided
start_ptr = line; //Start visiting input. We will distinguish tokens in a single pass, for good performance.
//Then we are allocating one unified memory region & doing one memory copy.
while ( ( end_ptr = strstr( start_ptr, delim ) ) ) {
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token
end_idx[ tokens_found - 1 ] = end_ptr - line; //Store Index of first character that will be replaced with
//'\0'. Example: "arg1||arg2||end" -> "arg1\0|arg2\0|end"
tokens_found++; //Accumulate the count of tokens.
start_ptr = end_ptr + delim_size; //Set pointer to the next c-string within the line
}
for ( iter = start_ptr; (*iter!='\0') ; iter++ );
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token: of last token here.
end_idx[ tokens_found -1 ] = iter - line; //and the last element that will be replaced with \0
line_size = iter - line; //Saving CPU cycles: Indirectly Count the size of *line without using strlen();
int size_ptr_region = (1 + tokens_found)*sizeof( char* ); //The size to store pointers to c-strings + 1 (*NULL).
out = (char**) malloc( size_ptr_region + ( line_size + 1 ) + 5 ); //Fit everything there...it is all memory.
//It reserves a contiguous space for both (char**) pointers AND string region. 5 Bytes for "Out of Range" tests.
*out_storage = out; //Update the char** pointer of the caller function.
//"Out of Range" TEST. Verify that the extra reserved characters will not be changed. Assign Some Values.
//char *extra_chars = (char*) out + size_ptr_region + ( line_size + 1 );
//extra_chars[0] = 1; extra_chars[1] = 2; extra_chars[2] = 3; extra_chars[3] = 4; extra_chars[4] = 5;
for ( i = 0; i < tokens_found; i++ ) //Assign adresses first part of the allocated memory pointers that point to
out[ i ] = (char*) out + size_ptr_region + start_idx[ i ]; //the second part of the memory, reserved for Data.
out[ tokens_found ] = (char*) NULL; //[ ptr1, ptr2, ... , ptrN, (char*) NULL, ... ]: We just added the (char*) NULL.
//Now assign the Data: c-strings. (\0 terminated strings):
char *str_region = (char*) out + size_ptr_region; //Region inside allocated memory which contains the String Data.
memcpy( str_region, line, line_size ); //Copy input with delimiter characters: They will be replaced with \0.
//Now we should replace: "arg1||arg2||arg3" with "arg1\0|arg2\0|arg3". Don't worry for characters after '\0'
//They are not used in standard c lbraries.
for( i = 0; i < tokens_found; i++) str_region[ end_idx[ i ] ] = '\0';
//"Out of Range" TEST. Wait until Assigned Values are Printed back.
//for ( int i=0; i < 5; i++ ) printf("c=%x ", extra_chars[i] ); printf("\n");
// *out memory should now contain (example data):
//[ ptr1, ptr2,...,ptrN, (char*) NULL, "token1\0", "token2\0",...,"tokenN\0", 5 bytes for tests ]
// |__________________________________^ ^ ^ ^
// |_______________________________________| | |
// |_____________________________________________| These 5 Bytes should be intact.
return tokens_found;
}
int main()
{
char in_line[] = "Arg1;;Th;s is not Del;m;ter;;Arg3;;;;Final";
char delim[] = ";;";
char **columns;
int i;
printf("Example1:\n");
columns = NULL; //Should be NULL to indicate that it is not assigned to allocated memory. Otherwise return -4;
int cols_found = getcols( in_line, delim, &columns);
for ( i = 0; i < cols_found; i++ ) printf("Column[ %d ] = %s\n", i, columns[ i ] ); //<- (1st way).
// (2nd way) // for ( i = 0; columns[ i ]; i++) printf("start_idx[ %d ] = %s\n", i, columns[ i ] );
free( columns ); //Release the Single Contiguous Memory Space.
columns = NULL; //Pointer = NULL to indicate it does not reserve space and that is ready for the next malloc().
printf("\n\nExample2, Nested:\n\n");
char example_file[] = "ID;Day;Month;Year;Telephone;email;Date of registration\n"
"1;Sunday;january;2009;123-124-456;jitter#go.xyz;2015-05-13\n"
"2;Monday;March;2011;(+30)333-22-55;buffer#wl.it;2009-05-23";
char **rows;
int j;
rows = NULL; //getcols() requires it to be NULL. (Avoid dangling pointers, leaks e.t.c).
getcols( example_file, "\n", &rows);
for ( i = 0; rows[ i ]; i++) {
{
printf("Line[ %d ] = %s\n", i, rows[ i ] );
char **columnX = NULL;
getcols( rows[ i ], ";", &columnX);
for ( j = 0; columnX[ j ]; j++) printf(" Col[ %d ] = %s\n", j, columnX[ j ] );
free( columnX );
}
}
free( rows );
rows = NULL;
return 0;
}
A complete example which leaves the fields as NULL-terminated strings in the original input buffer and provides access to them via an array of char pointers. The CSV processor has been confirmed to work with fields enclosed in "double quotes", ignoring any delimiter chars within them.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// adjust BUFFER_SIZE to suit longest line
#define BUFFER_SIZE 1024 * 1024
#define NUM_FIELDS 10
#define MAXERRS 5
#define RET_OK 0
#define RET_FAIL 1
#define FALSE 0
#define TRUE 1
// char* array will point to fields
char *pFields[NUM_FIELDS];
// field offsets into pFields array:
#define LP 0
#define IMIE 1
#define NAZWISKo 2
#define ULICA 3
#define NUMER 4
#define KOD 5
#define MIEJSCOw 6
#define TELEFON 7
#define EMAIL 8
#define DATA_UR 9
long loadFile(FILE *pFile, long *errcount);
static int loadValues(char *line, long lineno);
static char delim;
long loadFile(FILE *pFile, long *errcount){
char sInputBuf [BUFFER_SIZE];
long lineno = 0L;
if(pFile == NULL)
return RET_FAIL;
while (!feof(pFile)) {
// load line into static buffer
if(fgets(sInputBuf, BUFFER_SIZE-1, pFile)==NULL)
break;
// skip first line (headers)
if(++lineno==1)
continue;
// jump over empty lines
if(strlen(sInputBuf)==0)
continue;
// set pFields array pointers to null-terminated string fields in sInputBuf
if(loadValues(sInputBuf,lineno)==RET_FAIL){
(*errcount)++;
if(*errcount > MAXERRS)
break;
} else {
// On return pFields array pointers point to loaded fields ready for load into DB or whatever
// Fields can be accessed via pFields, e.g.
printf("lp=%s, imie=%s, data_ur=%s\n", pFields[LP], pFields[IMIE], pFields[DATA_UR]);
}
}
return lineno;
}
static int loadValues(char *line, long lineno){
if(line == NULL)
return RET_FAIL;
// chop of last char of input if it is a CR or LF (e.g.Windows file loading in Unix env.)
// can be removed if sure fgets has removed both CR and LF from end of line
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1) == '\n')
*(line + strlen(line)-1) = '\0';
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1 )== '\n')
*(line + strlen(line)-1) = '\0';
char *cptr = line;
int fld = 0;
int inquote = FALSE;
char ch;
pFields[fld]=cptr;
while((ch=*cptr) != '\0' && fld < NUM_FIELDS){
if(ch == '"') {
if(! inquote)
pFields[fld]=cptr+1;
else {
*cptr = '\0'; // zero out " and jump over it
}
inquote = ! inquote;
} else if(ch == delim && ! inquote){
*cptr = '\0'; // end of field, null terminate it
pFields[++fld]=cptr+1;
}
cptr++;
}
if(fld > NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) exceeded on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
} else if (fld < NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) not reached on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
}
return RET_OK;
}
int main(int argc, char **argv)
{
FILE *fp;
long errcount = 0L;
long lines = 0L;
if(argc!=3){
printf("Usage: %s csvfilepath delimiter\n", basename(argv[0]));
return (RET_FAIL);
}
if((delim=argv[2][0])=='\0'){
fprintf(stderr,"delimiter must be specified\n");
return (RET_FAIL);
}
fp = fopen(argv[1] , "r");
if(fp == NULL) {
fprintf(stderr,"Error opening file: %d\n",errno);
return(RET_FAIL);
}
lines=loadFile(fp,&errcount);
fclose(fp);
printf("Processed %ld lines, encountered %ld error(s)\n", lines, errcount);
if(errcount>0)
return(RET_FAIL);
return(RET_OK);
}
Use fscanf to read the file until you encounter ';' or \n, then just skip it with fscanf(f, "%*c").
int main()
{
char str[128];
int result;
FILE* f = fopen("test.txt", "r");
/*...*/
do {
result = fscanf(f, "%127[^;\n]", str);
if(result == 0)
{
result = fscanf(f, "%*c");
}
else
{
//Put here whatever you want to do with your value.
printf("%s\n", str);
}
} while(result != EOF);
return 0;
}
This code is fairly simple, but effective. It parses comma-separated files with parenthesis. You can easily modify it to suit your needs.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// argv[1] path to csv file
// argv[2] number of lines to skip
// argv[3] length of longest value (in characters)
FILE *pfinput;
unsigned int nSkipLines, currentLine, lenLongestValue;
char *pTempValHolder;
int c;
unsigned int vcpm; // Value character marker
int QuotationOnOff; // 0 - off, 1 - on
nSkipLines = atoi(argv[2]);
lenLongestValue = atoi(argv[3]);
pTempValHolder = (char*)malloc(lenLongestValue);
if(pfinput = fopen(argv[1], "r")) {
rewind(pfinput);
currentLine = 1;
vcpm = 0;
QuotationOnOff = 0;
// currentLine > nSkipLines condition
// skips / ignores first argv[2] lines
while((c = fgetc(pfinput)) != EOF)
{
switch(c)
{
case ',':
if(!QuotationOnOff && currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s,", pTempValHolder);
vcpm = 0;
}
break;
case '\n':
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s\n", pTempValHolder);
vcpm = 0;
}
currentLine++;
break;
case '\"':
if(currentLine > nSkipLines)
{
if(!QuotationOnOff) {
QuotationOnOff = 1;
pTempValHolder[vcpm] = c;
vcpm++;
} else {
QuotationOnOff = 0;
pTempValHolder[vcpm] = c;
vcpm++;
}
}
break;
default:
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = c;
vcpm++;
}
break;
}
}
fclose(pfinput);
free(pTempValHolder);
}
return 0;
}
#include <conio.h>
#include <stdio.h>
#include <string.h>
// Driver Code
int main()
{
// Substitute the full file path
// for the string file_path
FILE* fp = fopen("Movie.csv", "r");
char *wrds[40];
if (!fp)
printf("Can't open file\n");
else {
// Here we have taken size of
// array 1024 you can modify it
char buffer[1024];
int row = 0;
int column = 0;
while (fgets(buffer, 1024, fp)) {
column = 0;
row++;
// To avoid printing of column
// names in file can be changed
// according to need
if (row == 1)
continue;
// Splitting the data
char* value = strtok(buffer, ", ");
while (value) {
// Column 1
if (column == 0) {
printf("Name :");
}
// Column 2
if (column == 1) {
printf("\tAccount No. :");
}
// Column 3
if (column == 2) {
printf("\tAmount :");
}
printf("%s", value);
wrds[column] = value;
value = strtok(NULL, ", ");
column++;
}
printf("\n");
}
// Close the file
fclose(fp);
}
getchar();
return 0;
}

Spliting a string into an array of strings completly dynamicly allocated

This question is really close to this to this topic but I prefer the lisibility and the pointers clarification I needed offered by this solution.
So I've got a data file and I get a very long array of char from it. I want to split this string into an array with, in each case, a string wich correspond to a line of this file.
I saw solutions but they all use limited arrays, since I don't know the lenght of each line, I really need to allocate all of them dynamicly but I can't find the lenght of the lines because strtokdoesn't put a null character \0at the end of each string.
What I've got for now is this two solutions but neither work:
int get_lines(char *file, char **lines) {
int nb_lines = 0;
char *token = strtok(file, "\n");
for(int i = 0; token != NULL; i++) {
token = strtok(NULL, "\n");
nb_lines = i;
}
nb_lines++;
lines = malloc((nb_lines + 1) * sizeof(char*));
lines[nb_lines] = '\0';
token = strtok(file, "\n");
for(int i = 0; token != NULL; i++) {
token = strtok(NULL, "\n");
int nb_char = 0;
for(int j = 0; token[j] != '\n'; j++) //This will cause SIGSEGV because strtok don't keep the '\n' at the end
nb_char = j;
nb_char++;
token[nb_char] = '\0'; //This cause SIGSEGV because token's allocation finish at [nb_char-1]
lines[i] = malloc(strlen(token) * sizeof(char)); //strlen cause SIGSEGV because I cannot place the '\0' at the end of token
printf("%s", token); //SIGSEGV because printf don't find the '\0'
lines[i] = token;
}
for(int i = 0; i < nb_lines; i++) {
printf("%s", lines[i]); //SIGSEGV
}
return nb_lines;
}
So you can see above the idea of what I want to do and why it doesn't work.
Below you will see an other try I made but I'm stuck at the same point:
int count_subtrings(char* string, char* separator) {
int nb_lines = 0;
char *token = strtok(string, separator);
for(int i = 0; token != NULL; i++) {
token = strtok(NULL, separator);
nb_lines = i;
}
return nb_lines + 1;
}
char** split_string(char* string, char* separator) {
char **sub_strings = malloc((count_subtrings(string, separator) + 1) * sizeof(char*));
for(int i = 0; string[i] != EOF; i++) {
//How to get the string[i] lenght to malloc them ?
}
}
My file is quite big and the lines can be too so I don't want to malloc an other table with a size of (strlen(file) + 1) * sizeof(char) to be sure each line won't SIGSEGV and I also find this solution quite dirty, if you guys had an other idea, I would be really happy.
(Sorry for the english mistakes, I'm not really good)
Your approach with strtok has two drawbacks: First, strtok modifies the string,so you can only pass the original string once. Second, it skips empty lines, because it tretas stretches of nelines as a single token separator.. (I don't know ehether that is a concern to you.)
You can countthe newlines with a single pass through the string. Allocate memory for your line array and make a second pass, where you split the string at newlines:
char **splitlines(char *msg)
{
char **line;
char *prev = msg;
char *p = msg;
size_t count = 0;
size_t n;
while (*p) {
if (*p== '\n') count++;
p++;
}
line = malloc((count + 2) * sizeof(*line));
if (line == NULL) return NULL;
p = msg;
n = 0;
while (*p) {
if (*p == '\n') {
line[n++] = prev;
*p = '\0';
prev = p + 1;
}
p++;
}
if (*prev) line[n++] = prev;
line[n++] = NULL;
return line;
}
I've allocated two more line pointers than the newlines count: One for the case that the last line doesn't end with a newline and another one to place a NULL sentinel at the end, so that you know where yourarray ends. (You could, of course, return the actual line count via a pointer to a size_t.)
the following proposed code:
cleanly compiles
(within the limits of the heap size) doesn't care about the input file size
echo's the resulting array of file lines, double spaced, just to show it worked. for single spacing, replace the puts() with printf()
and now the code
#include <stdio.h> // getline(), perror(), fopen(), fclose()
#include <stdlib.h> // exit(), EXIT_FAILURE, realloc(), free()
int main( void )
{
FILE *fp = fopen( "untitled1.c", "r" );
if( !fp )
{
perror( "fopen for reading untitled1.c failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
char **lines = NULL;
size_t availableLines = 0;
size_t usedLines = 0;
char *line = NULL;
size_t lineLen = 0;
while( -1 != getline( &line, &lineLen, fp ) )
{
if( usedLines >= availableLines )
{
availableLines = (availableLines)? availableLines*2 : 1;
char **temp = realloc( lines, sizeof( char* ) * availableLines );
if( !temp )
{
perror( "realloc failed" );
free( lines );
fclose( fp );
exit( EXIT_FAILURE );
}
// implied else realloc successful
lines = temp;
}
lines[ usedLines ] = line;
usedLines++;
line = NULL;
lineLen = 0;
}
fclose( fp );
for( size_t i = 0; i<usedLines; i++ )
{
puts( lines[i] );
}
free( lines );
}
Given the above code is in a file named: untitled1.c the following is the output.
#include <stdio.h> // getline(), perror(), fopen(), fclose()
#include <stdlib.h> // exit(), EXIT_FAILURE, realloc(), free()
int main( void )
{
FILE *fp = fopen( "untitled1.c", "r" );
if( !fp )
{
perror( "fopen for reading untitled1.c failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
char **lines = NULL;
size_t availableLines = 0;
size_t usedLines = 0;
char *line = NULL;
size_t lineLen = 0;
while( -1 != getline( &line, &lineLen, fp ) )
{
if( usedLines >= availableLines )
{
availableLines = (availableLines)? availableLines*2 : 1;
char **temp = realloc( lines, sizeof( char* ) * availableLines );
if( !temp )
{
perror( "realloc failed" );
free( lines );
fclose( fp );
exit( EXIT_FAILURE );
}
// implied else realloc successful
lines = temp;
}
lines[ usedLines ] = line;
usedLines++;
line = NULL;
lineLen = 0;
}
fclose( fp );
for( size_t i = 0; i<usedLines; i++ )
{
puts( lines[i] );
}
free( lines );
}

Read .csv file in C

I have a .csv file:
lp;imie;nazwisko;ulica;numer;kod;miejscowosc;telefon;email;data_ur
1;Jan;Kowalski;ul. Nowa;1a;11-234;Budry;123-123-456;jan#go.xxx;1980.05.13
2;Jerzy;Nowak;ul. Konopnicka;13a/3;00-900;Lichowice;(55)333-44-55;jer#wu.to;1990.03.23
And I need to read this in C. I have some code, but only for the connection.
Hopefully this would get you started
See it live on http://ideone.com/l23He (using stdin)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* getfield(char* line, int num)
{
const char* tok;
for (tok = strtok(line, ";");
tok && *tok;
tok = strtok(NULL, ";\n"))
{
if (!--num)
return tok;
}
return NULL;
}
int main()
{
FILE* stream = fopen("input", "r");
char line[1024];
while (fgets(line, 1024, stream))
{
char* tmp = strdup(line);
printf("Field 3 would be %s\n", getfield(tmp, 3));
// NOTE strtok clobbers tmp
free(tmp);
}
}
Output:
Field 3 would be nazwisko
Field 3 would be Kowalski
Field 3 would be Nowak
The following code is in plain c language and handles blank spaces.
It only allocates memory once, so one free() is needed, for each processed line.
http://ideone.com/mSCgPM
/* Tiny CSV Reader */
/* Copyright (C) 2015, Deligiannidis Konstantinos
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://w...content-available-to-author-only...u.org/licenses/>. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* For more that 100 columns or lines (when delimiter = \n), minor modifications are needed. */
int getcols( const char * const line, const char * const delim, char ***out_storage )
{
const char *start_ptr, *end_ptr, *iter;
char **out;
int i; //For "for" loops in the old c style.
int tokens_found = 1, delim_size, line_size; //Calculate "line_size" indirectly, without strlen() call.
int start_idx[100], end_idx[100]; //Store the indexes of tokens. Example "Power;": loc('P')=1, loc(';')=6
//Change 100 with MAX_TOKENS or use malloc() for more than 100 tokens. Example: "b1;b2;b3;...;b200"
if ( *out_storage != NULL ) return -4; //This SHOULD be NULL: Not Already Allocated
if ( !line || !delim ) return -1; //NULL pointers Rejected Here
if ( (delim_size = strlen( delim )) == 0 ) return -2; //Delimiter not provided
start_ptr = line; //Start visiting input. We will distinguish tokens in a single pass, for good performance.
//Then we are allocating one unified memory region & doing one memory copy.
while ( ( end_ptr = strstr( start_ptr, delim ) ) ) {
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token
end_idx[ tokens_found - 1 ] = end_ptr - line; //Store Index of first character that will be replaced with
//'\0'. Example: "arg1||arg2||end" -> "arg1\0|arg2\0|end"
tokens_found++; //Accumulate the count of tokens.
start_ptr = end_ptr + delim_size; //Set pointer to the next c-string within the line
}
for ( iter = start_ptr; (*iter!='\0') ; iter++ );
start_idx[ tokens_found -1 ] = start_ptr - line; //Store the Index of current token: of last token here.
end_idx[ tokens_found -1 ] = iter - line; //and the last element that will be replaced with \0
line_size = iter - line; //Saving CPU cycles: Indirectly Count the size of *line without using strlen();
int size_ptr_region = (1 + tokens_found)*sizeof( char* ); //The size to store pointers to c-strings + 1 (*NULL).
out = (char**) malloc( size_ptr_region + ( line_size + 1 ) + 5 ); //Fit everything there...it is all memory.
//It reserves a contiguous space for both (char**) pointers AND string region. 5 Bytes for "Out of Range" tests.
*out_storage = out; //Update the char** pointer of the caller function.
//"Out of Range" TEST. Verify that the extra reserved characters will not be changed. Assign Some Values.
//char *extra_chars = (char*) out + size_ptr_region + ( line_size + 1 );
//extra_chars[0] = 1; extra_chars[1] = 2; extra_chars[2] = 3; extra_chars[3] = 4; extra_chars[4] = 5;
for ( i = 0; i < tokens_found; i++ ) //Assign adresses first part of the allocated memory pointers that point to
out[ i ] = (char*) out + size_ptr_region + start_idx[ i ]; //the second part of the memory, reserved for Data.
out[ tokens_found ] = (char*) NULL; //[ ptr1, ptr2, ... , ptrN, (char*) NULL, ... ]: We just added the (char*) NULL.
//Now assign the Data: c-strings. (\0 terminated strings):
char *str_region = (char*) out + size_ptr_region; //Region inside allocated memory which contains the String Data.
memcpy( str_region, line, line_size ); //Copy input with delimiter characters: They will be replaced with \0.
//Now we should replace: "arg1||arg2||arg3" with "arg1\0|arg2\0|arg3". Don't worry for characters after '\0'
//They are not used in standard c lbraries.
for( i = 0; i < tokens_found; i++) str_region[ end_idx[ i ] ] = '\0';
//"Out of Range" TEST. Wait until Assigned Values are Printed back.
//for ( int i=0; i < 5; i++ ) printf("c=%x ", extra_chars[i] ); printf("\n");
// *out memory should now contain (example data):
//[ ptr1, ptr2,...,ptrN, (char*) NULL, "token1\0", "token2\0",...,"tokenN\0", 5 bytes for tests ]
// |__________________________________^ ^ ^ ^
// |_______________________________________| | |
// |_____________________________________________| These 5 Bytes should be intact.
return tokens_found;
}
int main()
{
char in_line[] = "Arg1;;Th;s is not Del;m;ter;;Arg3;;;;Final";
char delim[] = ";;";
char **columns;
int i;
printf("Example1:\n");
columns = NULL; //Should be NULL to indicate that it is not assigned to allocated memory. Otherwise return -4;
int cols_found = getcols( in_line, delim, &columns);
for ( i = 0; i < cols_found; i++ ) printf("Column[ %d ] = %s\n", i, columns[ i ] ); //<- (1st way).
// (2nd way) // for ( i = 0; columns[ i ]; i++) printf("start_idx[ %d ] = %s\n", i, columns[ i ] );
free( columns ); //Release the Single Contiguous Memory Space.
columns = NULL; //Pointer = NULL to indicate it does not reserve space and that is ready for the next malloc().
printf("\n\nExample2, Nested:\n\n");
char example_file[] = "ID;Day;Month;Year;Telephone;email;Date of registration\n"
"1;Sunday;january;2009;123-124-456;jitter#go.xyz;2015-05-13\n"
"2;Monday;March;2011;(+30)333-22-55;buffer#wl.it;2009-05-23";
char **rows;
int j;
rows = NULL; //getcols() requires it to be NULL. (Avoid dangling pointers, leaks e.t.c).
getcols( example_file, "\n", &rows);
for ( i = 0; rows[ i ]; i++) {
{
printf("Line[ %d ] = %s\n", i, rows[ i ] );
char **columnX = NULL;
getcols( rows[ i ], ";", &columnX);
for ( j = 0; columnX[ j ]; j++) printf(" Col[ %d ] = %s\n", j, columnX[ j ] );
free( columnX );
}
}
free( rows );
rows = NULL;
return 0;
}
A complete example which leaves the fields as NULL-terminated strings in the original input buffer and provides access to them via an array of char pointers. The CSV processor has been confirmed to work with fields enclosed in "double quotes", ignoring any delimiter chars within them.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// adjust BUFFER_SIZE to suit longest line
#define BUFFER_SIZE 1024 * 1024
#define NUM_FIELDS 10
#define MAXERRS 5
#define RET_OK 0
#define RET_FAIL 1
#define FALSE 0
#define TRUE 1
// char* array will point to fields
char *pFields[NUM_FIELDS];
// field offsets into pFields array:
#define LP 0
#define IMIE 1
#define NAZWISKo 2
#define ULICA 3
#define NUMER 4
#define KOD 5
#define MIEJSCOw 6
#define TELEFON 7
#define EMAIL 8
#define DATA_UR 9
long loadFile(FILE *pFile, long *errcount);
static int loadValues(char *line, long lineno);
static char delim;
long loadFile(FILE *pFile, long *errcount){
char sInputBuf [BUFFER_SIZE];
long lineno = 0L;
if(pFile == NULL)
return RET_FAIL;
while (!feof(pFile)) {
// load line into static buffer
if(fgets(sInputBuf, BUFFER_SIZE-1, pFile)==NULL)
break;
// skip first line (headers)
if(++lineno==1)
continue;
// jump over empty lines
if(strlen(sInputBuf)==0)
continue;
// set pFields array pointers to null-terminated string fields in sInputBuf
if(loadValues(sInputBuf,lineno)==RET_FAIL){
(*errcount)++;
if(*errcount > MAXERRS)
break;
} else {
// On return pFields array pointers point to loaded fields ready for load into DB or whatever
// Fields can be accessed via pFields, e.g.
printf("lp=%s, imie=%s, data_ur=%s\n", pFields[LP], pFields[IMIE], pFields[DATA_UR]);
}
}
return lineno;
}
static int loadValues(char *line, long lineno){
if(line == NULL)
return RET_FAIL;
// chop of last char of input if it is a CR or LF (e.g.Windows file loading in Unix env.)
// can be removed if sure fgets has removed both CR and LF from end of line
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1) == '\n')
*(line + strlen(line)-1) = '\0';
if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1 )== '\n')
*(line + strlen(line)-1) = '\0';
char *cptr = line;
int fld = 0;
int inquote = FALSE;
char ch;
pFields[fld]=cptr;
while((ch=*cptr) != '\0' && fld < NUM_FIELDS){
if(ch == '"') {
if(! inquote)
pFields[fld]=cptr+1;
else {
*cptr = '\0'; // zero out " and jump over it
}
inquote = ! inquote;
} else if(ch == delim && ! inquote){
*cptr = '\0'; // end of field, null terminate it
pFields[++fld]=cptr+1;
}
cptr++;
}
if(fld > NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) exceeded on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
} else if (fld < NUM_FIELDS-1){
fprintf(stderr, "Expected field count (%d) not reached on line %ld\n", NUM_FIELDS, lineno);
return RET_FAIL;
}
return RET_OK;
}
int main(int argc, char **argv)
{
FILE *fp;
long errcount = 0L;
long lines = 0L;
if(argc!=3){
printf("Usage: %s csvfilepath delimiter\n", basename(argv[0]));
return (RET_FAIL);
}
if((delim=argv[2][0])=='\0'){
fprintf(stderr,"delimiter must be specified\n");
return (RET_FAIL);
}
fp = fopen(argv[1] , "r");
if(fp == NULL) {
fprintf(stderr,"Error opening file: %d\n",errno);
return(RET_FAIL);
}
lines=loadFile(fp,&errcount);
fclose(fp);
printf("Processed %ld lines, encountered %ld error(s)\n", lines, errcount);
if(errcount>0)
return(RET_FAIL);
return(RET_OK);
}
Use fscanf to read the file until you encounter ';' or \n, then just skip it with fscanf(f, "%*c").
int main()
{
char str[128];
int result;
FILE* f = fopen("test.txt", "r");
/*...*/
do {
result = fscanf(f, "%127[^;\n]", str);
if(result == 0)
{
result = fscanf(f, "%*c");
}
else
{
//Put here whatever you want to do with your value.
printf("%s\n", str);
}
} while(result != EOF);
return 0;
}
This code is fairly simple, but effective. It parses comma-separated files with parenthesis. You can easily modify it to suit your needs.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// argv[1] path to csv file
// argv[2] number of lines to skip
// argv[3] length of longest value (in characters)
FILE *pfinput;
unsigned int nSkipLines, currentLine, lenLongestValue;
char *pTempValHolder;
int c;
unsigned int vcpm; // Value character marker
int QuotationOnOff; // 0 - off, 1 - on
nSkipLines = atoi(argv[2]);
lenLongestValue = atoi(argv[3]);
pTempValHolder = (char*)malloc(lenLongestValue);
if(pfinput = fopen(argv[1], "r")) {
rewind(pfinput);
currentLine = 1;
vcpm = 0;
QuotationOnOff = 0;
// currentLine > nSkipLines condition
// skips / ignores first argv[2] lines
while((c = fgetc(pfinput)) != EOF)
{
switch(c)
{
case ',':
if(!QuotationOnOff && currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s,", pTempValHolder);
vcpm = 0;
}
break;
case '\n':
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = '\0';
printf("%s\n", pTempValHolder);
vcpm = 0;
}
currentLine++;
break;
case '\"':
if(currentLine > nSkipLines)
{
if(!QuotationOnOff) {
QuotationOnOff = 1;
pTempValHolder[vcpm] = c;
vcpm++;
} else {
QuotationOnOff = 0;
pTempValHolder[vcpm] = c;
vcpm++;
}
}
break;
default:
if(currentLine > nSkipLines)
{
pTempValHolder[vcpm] = c;
vcpm++;
}
break;
}
}
fclose(pfinput);
free(pTempValHolder);
}
return 0;
}
#include <conio.h>
#include <stdio.h>
#include <string.h>
// Driver Code
int main()
{
// Substitute the full file path
// for the string file_path
FILE* fp = fopen("Movie.csv", "r");
char *wrds[40];
if (!fp)
printf("Can't open file\n");
else {
// Here we have taken size of
// array 1024 you can modify it
char buffer[1024];
int row = 0;
int column = 0;
while (fgets(buffer, 1024, fp)) {
column = 0;
row++;
// To avoid printing of column
// names in file can be changed
// according to need
if (row == 1)
continue;
// Splitting the data
char* value = strtok(buffer, ", ");
while (value) {
// Column 1
if (column == 0) {
printf("Name :");
}
// Column 2
if (column == 1) {
printf("\tAccount No. :");
}
// Column 3
if (column == 2) {
printf("\tAmount :");
}
printf("%s", value);
wrds[column] = value;
value = strtok(NULL, ", ");
column++;
}
printf("\n");
}
// Close the file
fclose(fp);
}
getchar();
return 0;
}

Resources