I have seen "config" files(yes, text files) for various console applications that look like the following
<token> <value>
How would I go about parsing such a thing in C, where < value > could be a string, a letter or even an integer/float/double?
I read this question " How do I parse a token from a string in C? " where it is mostly recommended to use strtok, but also that it's not thread-safe, and I am planning on spawning multiple threads(provided I am able to finish my application)
P.S
Here is an example of a token and a value
user username123
pass 123456
Ah, I forgot the tricky part. I must also be able to parse a token, which has multiple values either separated by a comma.
I think that fgets() and sscanf() are your friend:
int parseTokens(FILE *filePtr, char **tokens, char **values)
{
int i = 0;
char line[128];
while (fgets(line, 127, filePtr)) {
tokens[i] = malloc(64);
values[i] = malloc(64);
sscanf(line, "%s %s", tokens[i], values[i]);
i++;
}
return i;
}
int main(void)
{
char *tokens[20];
char *values[20];
FILE *filePtr = fopen("~/test.txt", "r");
if (!filePtr)
{
fprintf(stderr, "Error opening file: %s", strerror(errno));
}
int count = parseTokens(filePtr, tokens, values);
for (int i = 0; i < count; i++) {
printf("%s %s\n", tokens[i], values[i]);
free(tokens[i]);
free(values[i]);
}
fclose(filePtr);
}
Using getc(), read characters from an input stream into a per-line buffer. Once you hit a token delimiter, you strncpy() or strdup() the per-line buffer into a token char*. If needed, parse the token again on a within-token delimiter (such as a comma), grabbing a character at a time and storing it in a per-token buffer, until you hit a within-token delimiter. Once you hit a line delimiter, copy the per-line buffer into a value char*. If you know the value is an int, float, etc., use C functions for converting a char* into those primitives (e.g., strtol(), etc.). If you have multiple token-value pairs, either keep an array of or pointers to token and value char* variables. Repeat until EOF (end-of-file).
Try this:
FILE* fp;
fp = fopen("in.txt","r");
if(fp == NULL)
{
printf("Can't open/read file.\n");
exit(1);
}
char* buf = NULL;
char* key = malloc(64);
char* val = malloc(64);
size_t read;
size_t len = 0;
if(key == NULL || val == NULL)
{
printf("malloc failed.\n");
exit(1);
}
while((read = getline(&buf, &len, fp)) != -1)
{
sscanf(buf,"%s %s", key, val);
printf("<%s> <%s>\n", key, val);
}
if(buf != NULL)
{
free(buf);
}
free(key);
free(val);
fclose(fp);
in.txt file:
key value
key1 value1
C application output:
<key> <value>
<key1> <value1>
I hope this help you.
how about use regexp? if you are in linux, you can just #include <regexp.h> to use it. andman regexp.hwill get how to use it. store them by string. and, it they are number, use sprintf to trans them to unmber.
Related
I have a file .txt containing some values formatted like this:
0,30,25,10
Now, I open up the file and store it into an array
char imposta_tratt[300];
FILE *fp;
fp = fopen("/home/pi/Documents/imposta_trattamento.txt", "r");
if (fp == 0) return;
fread(imposta_tratt, sizeof(imposta_tratt), 1, fp);
fclose(fp);
Now I expect to have the array filled with my data. I have the values separated by a , so I go on and parse it:
const char delim[2] = ",";
int t=0;
char *token = strtok(imposta_tratt, delim);
while (token!=NULL){
strcpy(tratt[t],token);
token = strtok(NULL, delim);
tratt[t]=token;
t++;
}
Here, referring to what's in the file .txt, I expect to have tratt[0]=0; tratt[1]=30; tratt[2]=25; and so on, but seems like I am missing something since it's not like this.
All I want is to have the values of the txt file stored in single variables. Can someone help?
What you are trying to achieve can simply be done using fgets():
bool read_file_content(const char *filename, const size_t tsizemax, int tratt[tsizemax], size_t *tsize, const char *delim)
{
// Attempt to open filename.
FILE *fp = fopen(filename, "r");
if (!fp) return false; // Return false upon failure.
// Try to read one line. If you have more, you need a while loop.
char imposta_tratt[300];
if (!fgets(imposta_tratt, sizeof imposta_tratt, fp)) {
fclose(fp);
return false;
}
*tsize = 0;
char tmp[300]; // Temporary buffer. Used for conversion into int.
char *token = strtok(imposta_tratt, delim);
while (token && *tsize < tsizemax) {
strncpy(tmp, token, sizeof tmp);
tratt[(*tsize)++] = atoi(tmp);
token = strtok(NULL, delim);
}
fclose(fp);
return true;
}
const char *filename: The file you want to parse.
const size_t tsizemax: The maximum size of your tratt array. It is important to control the size, otherwise your code will have buffer overflow (think of when your file has more than 100 tokens, for example).
int tratt[tsizemax]: The array that will hold the values.
size_t *tsize: The number of tokens read (used in combination of tsizemax).
const char *delim: The delimiter(s), in your case a ,.
This is your main():
int main(void)
{
int tratt[100];
size_t size = 0;
if (!read_file_content("in.txt", 100, tratt, &size, ",")) {
puts("Failed");
return 1;
}
for (size_t i = 0; i < size; ++i)
printf("%d\n", tratt[i]);
}
Output:
0
30
25
10
Suppose "in.txt" has contents
0,30,25,10
The below program uses fscanf to read the integers into the tratt array, one-by-one. As we read integers using fscanf, we make sure it's return value is as expected. If not, we close the file and exit. In the event that the return value of fscanf is not as expected, the program also prints which type of error occurred. Currently, if any error occurs, the program stops. However, you can make the program behave differently depending on the error that occurred if you like.
As output, the program prints all of the integers read into the tratt array. The output is
0
30
25
10
Now this program assumes we know the number of elements we want to read into tratt. If we do not, we could allow for dynamically allocating more memory should the array need more elements or perhaps "in.txt" could contain a data structure, say, at the beginning/end of the file that records information about the file, such as the number of numbers in the file and the data type (a binary file would be best suited for this). These are just a couple of the possibilities.
A better approach might be to read characters in one-by-one (say, using getc) and use strtol to convert a sequence of character digits to a long int (I would have taken an approach similar to this).
Nevertheless, this approach is more succinct and should suffice.
#include <stdio.h>
#include <stdlib.h>
#define FILE_NAME "in.txt"
#define MAX_LEN 4
int main(void) {
int i, tratt[MAX_LEN];
FILE *fp = fopen(FILE_NAME, "r"); /* open file for reading */
/* if cannot open file */
if (fp == NULL) {
printf("Cannot open %s\n", FILE_NAME);
exit(EXIT_FAILURE);
}
/* read integer, checking return value of scanf as expected */
if (fscanf(fp, "%d", &tratt[0]) != 1) {
if (ferror(fp))
printf("fscanf: read error\n");
else if (feof(fp))
printf("fscanf: end of file\n");
else
printf("fscanf: matching failure\n");
fclose(fp);
exit(EXIT_FAILURE);
}
for (i = 1; i < MAX_LEN; i++)
/* read comma plus integer, checking return value of scanf */
if (fscanf(fp, ",%d", &tratt[i]) != 1) {
if (ferror(fp))
printf("fscanf: read error\n");
else if (feof(fp))
printf("fscanf: end of file\n");
else
printf("fscanf: matching failure\n");
fclose(fp);
exit(EXIT_FAILURE);
}
fclose(fp); /* close file */
/* print integers stored in tratt */
for (i = 0; i < MAX_LEN; i++)
printf("%d\n", tratt[i]);
return 0;
}
I am trying to read a file in C and then store values after a certain word is read. For example, in my input.txt file, the following are the contents:
GREETINGS
Hello 13
Namaste 24
Hola 36
FLAVORS
Vanilla 23
Chocolate 78
I want to read past GREETINGS and then store Hello and its value 13 then Namaste and its value 24, etc. And then read past FLAVORS and store Vanilla and its value 23, etc. And so on...
I know how to open a file but not sure how to proceed from there.
void readInput() {
char input_file[100];
FILE *fp;
printf("Please enter the name of the file you would like to read: \n");
scanf("%s", input_file);
printf("You entered: %s\n", input_file);
fp = fopen(input_file, "r");
if (fp == NULL) {
printf("File does not exist.\n");
exit(1);
}
else
printf("This file exists!\n");
}
From your sample data it is not clear whether the lines with data have the always the same format. So I
suggest that you store in a variable the current mode (that means GREETINGS, FLAVOR, etc) and you call
a specific parsing function for every mode depending of the value of the current
mode.
I'd create this enum first
typedef enum {
MODE_GREETINGS = 0, // <-- important that this value is 0
MODE_FLAVORS,
...
MODE_INVALID
} Modes;
and a variable (this can be a global variable) with all modes:
const char *modes_lookup[] = {
"GREETINGS",
"FLAVORS",
...
};
Then I'd write a function that returns the mode:
Modes get_mode(const char *src)
{
if(src == NULL)
return MODE_INVALID;
for(size_t i = 0; i < sizeof modes_lookup / sizeof modes_lookup[0]; ++i)
{
if(strcmp(src, modes_lookup[i]) == 0)
return i;
}
return MODE_INVALID;
}
Now you can parse the file like this:
char line[1024];
Modes mode = MODE_INVALID;
size_t lineno = 0;
while(fgets(line, sizeof line, fp))
{
lineno++;
// removing newline
line[strcspn(line, "\n")] = 0;
Modes newmode = get_mode(line);
// check if line is a GREETINGS, FLAVOR, etc line
if(newmode != MODE_INVALID)
{
// sets the new mode and continue with the next line
mode = newmode;
continue;
}
// parsing depending on the current mode
if(mode == MODE_GREETINGS)
{
if(parse_greetings(line) == 0)
{
fprintf(stderr, "Cannot parse data on line %zu\n", lineno);
break;
}
continue;
}
if(mode == MODE_FLAVORS) {
if(parse_flavor(line) == 0)
{
fprintf(stderr, "Cannot parse data on line %zu\n", lineno);
break;
}
continue;
}
// more (if mode == MODE_xxx) cases as needed
if(mode == MODE_INVALID)
{
// mode == MODE_INVALID
fprintf(stderr, "File does not start with a mode line.\n");
break;
}
}
Then you've got to write the parse_greetings, parse_flavor, parse_xxx functions. Like I said
before, it's not clear from your sample whether the format will be always the
same, so it depends on the format whether you use strtok or sscanf. Also
your description of how and where you want to store the values is very vague, so
I don't know what would be the best for your.
The easiest would be sscanf and if the data line gets more complex, you can then
use strtok.
For example with sscanf
int parse_greetings(const char *line)
{
if(line == NULL)
return 0;
char name[20];
int val;
if(sscanf(line, "%19s %d", name, val) != 2)
return 0;
// do whatever you need with 'name' and 'val'
return 1;
}
And with strtok:
int parse_greetings(char *line)
{
if(line == NULL)
return 0;
char name[20];
int val;
const char *delim = " \t\r\n";
char *token = strtok(line, NULL);
// line has only delimiters
if(token == NULL)
return 0;
strncpy(name, token, sizeof name / sizeof name[0]);
name[(sizeof name / sizeof name[0]) - 1] = 0;
token = strtok(NULL, delim);
if(token == NULL)
return 0;
char *end;
val = strtol(token, &end, 0);
// check if token is a number
if(*end != '\0')
return 0;
// do whatever you need with 'name' and 'val'
return 1;
}
I'm not sure what exact criteria you're using to choose which lines to skip, but the next step for reading lines is to use fscanf or fgets. This tutorial should help you get started with reading files in C.
Edit, to make this more relevant after your comment:
fgets may be a better choice. You'll need to read each line, then loop through each character, to check if they are all uppercase.
If they are not uppercase, you can use fscanf, using the format %s %d, to scan the data line.
I have a basic .txt file that may contain an unknown amount of pieces of data exactly in this format and I need to extract the second part after the '=' identifier. For example:
variable1=Hello
variable2=How
variable3=Are
variable4=You?
I need to extract "Hello" "How" "Are" and "You?" separately and store them into an array(removing/ignoring the variable name) and being able to call each word individually. I'm doing this in C and here is what I currently have.
#include <stdio.h>
#include <string.h>
int main()
{
char*result;
char copy[256];
FILE * filePtr;
filePtr = fopen("testfile.txt", "r+");
strcpy(copy, "testfile.txt");
while(fgets(copy, 256, filePtr)!= NULL)
{
result = strchr(copy, '=');
result = strtok(NULL, "=");
printf("%s",result);
if(result != 0)
{
*result = 0;
}
result = strtok(copy, "=");
}
return 0;
}
My current output is
(null)How
Are
You?
You do not need strtok, using strchr is enough.
no need to copy the filename to the copy buffer.
probably not necessary to open the file in update mode "%r+" either.
Here is a corrected version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *words[20];
int n = 0;
char *result;
char copy[256];
FILE *filePtr;
filePtr = fopen("testfile.txt", "r");
while (fgets(copy, 256, filePtr) != NULL) {
copy[strcspn(copy, "\n")] = '\0'; /* strip the \n if present */
result = strchr(copy, '=');
if (result != NULL) {
words[n++] = strdup(result + 1);
printf("%s ", result + 1);
}
}
printf("\n");
fclose(filePtr);
return 0;
}
Note the one liner to strip the trailing \n left at the end of copy by fgets(): copy[strcspn(copy, "\n")] = '\0';. It works even if fgets() did not see a \n before the end of the buffer or before the end of file. strcspn counts returns the number of characters in copy that are not in the second argument, thus it returns the length of the line without the \n.
The words are collected into an array words of pointers to strings. Each word is copied into memory allocated by malloc by the strdup function. strdup is not part of Standard C, but part of Posix and probably present in your environment, possibly written as _strdup.
Note also that you should also test for failure to open the file, failure to allocate memory in strdup, and also handle more than 20 strings...
If there is a fixed set of words and you just want to strip the initial parts, you can use a simpler hardcoded approach:
int main(void) {
char word1[20], word2[20], word3[20], word4[20];
FILE *filePtr;
filePtr = fopen("testfile.txt", "r");
if (fscanf(filePtr,
"%*[^=]=%19[^\n]%*[^=]=%19[^\n]%*[^=]=%19[^\n]%*[^=]=%19[^\n]",
word1, word2, word3, word4) == 4) {
printf("%s %s %s %s\n", word1, word2, word3, word4);
// perform whatever task with the arrays
} else {
printf("parse failed\n");
}
fclose(filePtr);
return 0;
}
I'm really new to Sqlite and I want to write a text file (xml actually) from an sqlite database table automatically using C. How can I do that? I got these to select some data from database and print in terminal:
int callback(void * ptr, int resultados, char ** STR1, char **STR2) {
int i;
for(i = 0; STR1[i] != NULL; i++) {
printf("%s = %s\n", STR2[i], STR1[i]);
}
return 0;
}
and inside of main:
/* Open database */
rc = sqlite3_open("test.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stderr, "Opened database successfully\n");
}
/* Create SQL statement */
sql = "SELECT * from COMPANY";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(stdout, "Operation done successfully\n");
}
sqlite3_close(db);
and then I tried to change the callback, to get STR1 and STR2 to write a text file, something like this:
int callback(void * ptr, int resultados, char ** STR1, char **STR2) {
int i;
char teste[1024];
FILE *pFile;
pFile = fopen ("test.txt", "a+");
for(i = 0; STR1[i] != NULL; i++) {
printf("%s = %s\n", STR2[i], STR1[i]);
snprintf (teste, sizeof(teste), "%s %s\n", STR2[i], STR1[i]);
fwrite (teste, 1, sizeof(teste), pFile);
fflush(pFile);
}
return 0;
}
but when I try to open the test.txt, gedit says 'There was a problem opening the file /home/kdan/test/test.txt. The file you opened has some invalid characters', and the text contains a bunch of random characters, numbers and symbols. So I'm not being able to get the selected data and convert to string D: should I try to use something like sqlite_mprintf, sqlite_vmprintf, sqlite_exec_printf or sqlite_get_table_printf? Would it be right? Or is there another way to select the data and format it to a variable char? Sorry for my rusty english.
Edited: It is working now, just needed to use fputs instead of fwrite, here is the code working:
int callback(void * ptr, int resultados, char ** STR1, char **STR2) {
int i;
char teste[1024];
FILE *pFile;
pFile = fopen ("test.txt", "a");
for(i = 0; STR1[i] != NULL; i++) {
printf("%s = %s\n", STR2[i], STR1[i]);
snprintf (teste, sizeof(teste), "%s %s\n", STR2[i], STR1[i]);
fputs (teste, pFile);
fflush(pFile);
}
return 0;
}
Haven't tested your code but I think the problem is with the fwrite() call.
From the linux manpages:
NAME
fread, fwrite - binary stream input/output
SYNOPSIS
[...]
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
DESCRIPTION
[...]
The function fwrite() writes nmemb elements of data, each size bytes
long, to the stream pointed to by stream, obtaining them from the loca‐
tion given by ptr.
STR2[i] and STR1[i] may have a total length less than sizeof teste. For illustrative purpose, let's say STR2[i] is "foo" and STR1[i] is "bar". After the snprintf() call, teste will have the following contents:
teste[0] 'f'
teste[1] 'o'
teste[2] 'o'
teste[3] ' '
teste[4] 'b'
teste[5] 'a'
teste[6] 'r'
teste[7] '\n'
teste[8] '\0'
teste[9] onwards uninitialised
Since fwrite is for writing to a binary stream, it will NOT stop writing after reaching the '\0' byte. Instead it will keep writing all the uninitialised characters in teste. In this case it will write 1015 uninitialised characters.
The proper way to write to a text stream is to use either fputs() (in which case you should remove the '\n' in your string) or fprintf(). Or, if you really want to use fwrite(), ask it to print strlen(teste) bytes (which will be 8 in this case) instead of sizeof teste which is 1024.
Finally, although not related to your problem, if you are not reading from the file you should fopen() it with "a" instead of "a+".
I have been working on a small exercise for my CIS class and am very confused by the methods C uses to read from a file. All that I really need to do is read through a file line by line and use the information gathered from each line to do a few manipulations. I tried using the getline method and others with no luck.
My code is currently as follows:
int main(char *argc, char* argv[]){
const char *filename = argv[0];
FILE *file = fopen(filename, "r");
char *line = NULL;
while(!feof(file)){
sscanf(line, filename, "%s");
printf("%s\n", line);
}
return 1;
}
Right now I am getting a seg fault with the sscanf method and I am not sure why. I am a total C noob and just wondering if there was some big picture thing that I was missing.
Thanks
So many problems in so few lines. I probably forget some:
argv[0] is the program name, not the first argument;
if you want to read in a variable, you have to allocate its memory
one never loops on feof, one loops on an IO function until it fails, feof then serves to determinate the reason of failure,
sscanf is there to parse a line, if you want to parse a file, use fscanf,
"%s" will stop at the first space as a format for the ?scanf family
to read a line, the standard function is fgets,
returning 1 from main means failure
So
#include <stdio.h>
int main(int argc, char* argv[])
{
char const* const fileName = argv[1]; /* should check that argc > 1 */
FILE* file = fopen(fileName, "r"); /* should check the result */
char line[256];
while (fgets(line, sizeof(line), file)) {
/* note that fgets don't strip the terminating \n, checking its
presence would allow to handle lines longer that sizeof(line) */
printf("%s", line);
}
/* may check feof here to make a difference between eof and io failure -- network
timeout for instance */
fclose(file);
return 0;
}
To read a line from a file, you should use the fgets function: It reads a string from the specified file up to either a newline character or EOF.
The use of sscanf in your code would not work at all, as you use filename as your format string for reading from line into a constant string literal %s.
The reason for SEGV is that you write into the non-allocated memory pointed to by line.
In addition to the other answers, on a recent C library (Posix 2008 compliant), you could use getline. See this answer (to a related question).
Say you're dealing with some other delimiter, such as a \t tab, instead of a \n newline.
A more general approach to delimiters is the use of getc(), which grabs one character at a time.
Note that getc() returns an int, so that we can test for equality with EOF.
Secondly, we define an array line[BUFFER_MAX_LENGTH] of type char, in order to store up to BUFFER_MAX_LENGTH-1 characters on the stack (we have to save that last character for a \0 terminator character).
Use of an array avoids the need to use malloc and free to create a character pointer of the right length on the heap.
#define BUFFER_MAX_LENGTH 1024
int main(int argc, char* argv[])
{
FILE *file = NULL;
char line[BUFFER_MAX_LENGTH];
int tempChar;
unsigned int tempCharIdx = 0U;
if (argc == 2)
file = fopen(argv[1], "r");
else {
fprintf(stderr, "error: wrong number of arguments\n"
"usage: %s textfile\n", argv[0]);
return EXIT_FAILURE;
}
if (!file) {
fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
return EXIT_FAILURE;
}
/* get a character from the file pointer */
while(tempChar = fgetc(file))
{
/* avoid buffer overflow error */
if (tempCharIdx == BUFFER_MAX_LENGTH) {
fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
return EXIT_FAILURE;
}
/* test character value */
if (tempChar == EOF) {
line[tempCharIdx] = '\0';
fprintf(stdout, "%s\n", line);
break;
}
else if (tempChar == '\n') {
line[tempCharIdx] = '\0';
tempCharIdx = 0U;
fprintf(stdout, "%s\n", line);
continue;
}
else
line[tempCharIdx++] = (char)tempChar;
}
return EXIT_SUCCESS;
}
If you must use a char *, then you can still use this code, but you strdup() the line[] array, once it is filled up with a line's worth of input. You must free this duplicated string once you're done with it, or you'll get a memory leak:
#define BUFFER_MAX_LENGTH 1024
int main(int argc, char* argv[])
{
FILE *file = NULL;
char line[BUFFER_MAX_LENGTH];
int tempChar;
unsigned int tempCharIdx = 0U;
char *dynamicLine = NULL;
if (argc == 2)
file = fopen(argv[1], "r");
else {
fprintf(stderr, "error: wrong number of arguments\n"
"usage: %s textfile\n", argv[0]);
return EXIT_FAILURE;
}
if (!file) {
fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
return EXIT_FAILURE;
}
while(tempChar = fgetc(file))
{
/* avoid buffer overflow error */
if (tempCharIdx == BUFFER_MAX_LENGTH) {
fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
return EXIT_FAILURE;
}
/* test character value */
if (tempChar == EOF) {
line[tempCharIdx] = '\0';
dynamicLine = strdup(line);
fprintf(stdout, "%s\n", dynamicLine);
free(dynamicLine);
dynamicLine = NULL;
break;
}
else if (tempChar == '\n') {
line[tempCharIdx] = '\0';
tempCharIdx = 0U;
dynamicLine = strdup(line);
fprintf(stdout, "%s\n", dynamicLine);
free(dynamicLine);
dynamicLine = NULL;
continue;
}
else
line[tempCharIdx++] = (char)tempChar;
}
return EXIT_SUCCESS;
}