I'm using JSON library. It's quite light and easy to understand, but I have one problem with json_decode. I'm reading data(JSON) from file:
FILE *instream = fopen("/tmp/file.dat", "r");
char ch;
int count = 0;
do {
ch = getc(instream);
inbuffer[count] = ch;
count++;
} while (!feof(instream) && ch != '\0');
My file look like below, so inbuffer has the same text
[
{
"MBV": 0,
"CRRC": 0,
"LFrei": 0
}
]
I try to decode it to have JsonNode variable
static char *chomp(char *s) //function taken from CCAN JSON example
{
char *e;
if (s == NULL || *s == 0)
return s;
e = strchr(s, 0);
if (e[-1] == '\n')
*--e = 0;
return s;
}
const char *s = chomp(inbuffer);
JsonNode *jin = json_decode(s);
printf("JSON: %s\n", jin);
After I run my program I get
JSON: (null)
Can somebody tell me why json_decode function doesn't want to read JSON formatted file even if the file created using this library?
I don't know the JSON library you ar using but I suspect the error occurs because inbuffer is not correctly NUL terminated. On end of file, getc() returns EOF (-1) what you copy into inbuffer before feof() returns TRUE. I would do:
while( (ch = getc( instream )) != EOF ) {
inbuffer[count] = ch;
count++;
}
inbuffer[count] = '\0';
or just use fread():
count = fread( inbuffer, 1, sizeof( inbuffer ) - 1, stream );
inbuffer[count] = '\0';
This code prints all the key-value pairs found in the first object of the array (your example shows an array containing only one object, which in turn contains three key-value items):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ccan/json/json.h>
int main(int argc, char **argv)
{
FILE *fd;
long filesize;
char *buffer;
JsonNode *jin, *node;
if ((fd = fopen(argv[1], "r")) == NULL) {
perror("Error opening file");
return EXIT_FAILURE;
}
fseek(fd, 0, SEEK_END);
filesize = ftell(fd);
rewind(fd);
buffer = (char *) malloc(sizeof(char) * filesize+1);
if (fread(buffer, sizeof(char), filesize, fd) != filesize) {
fprintf(stderr, "Error reading file\n");
return EXIT_FAILURE;
}
buffer[filesize] = '\0';
jin = json_decode(buffer);
json_foreach(node, jin->children.head)
printf("%s: %g\n", node->key, node->number_);
free(buffer);
return EXIT_SUCCESS;
}
Take into account that the traversal I just did heavily depends on the structure of the JSON you're trying to parse. If it fits your needs, fine, but maybe you'll need to find a more general solution. Take a look at the json.h header file included with the library to understand how the JSON structure is stored in memory once decoded from the file.
Also, in my personal experience, I've found the JSON-C library much easier to use than this one.
Related
I have a text file, and I open it and read one line of it, and close the text file. I'm calling my function under a for loop, but each time this function reads the first line of a text file, how can I fix it to read from the continuation
You can use fseek to reposition yourself in the file after closing and reopening, but it is very unusual to do so. So unusual, in fact, that I would suggest it is completely wrong. Here's some sample code that demonstrates how to do that, as well as a more typical loop. Each loop here reads the first 2 lines of the file, assuming each line is sufficiently small; handling long lines is beyond the scope of this question.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
FILE * xfopen(const char *path, const char *mode);
void xfseek(FILE *stream, long offset, int whence, const char *);
long xftell(FILE *stream, const char *);
void xfclose(FILE *stream, const char *);
int
main(int argc, char *argv[])
{
const char *path = argc > 1 ? argv[1] : "input";
/* Read the first two lines of the file, closing the file on each
* iteration. This is ** not ** the usual way to do this, and is
* included here for demonstration
* purposes only. DO NOT DO THIS.
* It is very unusual to close and re-open the file on each iteration.
*/
long position = 0;
for( int line = 1; line < 3; line++ ){
FILE *ifp = xfopen(path, "r");
char buf[1024];
xfseek(ifp, position, SEEK_SET, path);
fgets(buf, sizeof buf, ifp); /* (1) */
printf("line %d: %s", line, buf);
position = xftell(ifp, path);
xfclose(ifp, path); /* !! */
}
/* The more usual way to read each line of a file is to simply
* read it with repeated calls to the appropriate read method
* (fgets, fread, fgetc, etc.) Each subsequent read starts
* where the previous read finished.
*/
FILE *ifp = xfopen(path, "r");
for( int line = 1; line < 3; line++ ){
char buf[1024];
fgets(buf, sizeof buf, ifp); /* (1) */
printf("line %d: %s", line, buf);
}
xfclose(ifp, path);
return 0;
}
FILE *
xfopen(const char *path, const char *mode)
{
FILE *fp = path[0] != '-' || path[1] != '\0' ? fopen(path, mode) :
*mode == 'r' ? stdin : stdout;
if( fp == NULL ){
perror(path);
exit(EXIT_FAILURE);
}
return fp;
}
void
xfseek(FILE *stream, long offset, int whence, const char *name)
{
if( fseek(stream, offset, whence) == -1){
perror(name);
exit(EXIT_FAILURE);
}
}
long
xftell(FILE *stream, const char *name)
{
long ret = ftell(stream);
if( ret == -1 ){
perror(name);
exit(EXIT_FAILURE);
}
return ret;
}
void
xfclose(FILE *stream, const char *name)
{
if( fclose(stream) ){
perror(name);
exit(EXIT_FAILURE);
}
}
Notes: (1) It is left as an exercise for the reader how best to handle short reads (eg, when fgets returns NULL) or long lines (eg, when fgets completely fills the buffer but fails to read an entire line). Perhaps it is a bit of a cop-out to leave that as an exercise, but the annoyance of dealing with those issues points strongly towards reasons for using the standard idiom. If you want to print the first two lines of the file, use some variation of for( int count = 0; (c = fgetc(fp)) != NULL && count < 2; ) { if( c == '\n' ) count += 1; putchar(c); }. Putting the read function as a condition of the loop is (almost) always the best choice.
The comments have already made suggestions on other alternatives for what you are attempting. But regardless whether it is the right approach or not, it seems pretty clear that your stated ask is clear about wanting to use fseek() et. al to view successive lines when opening and closing a file.
To open and close a file, and each time access and display a successive line, you must first know where each of the locations to be viewed are located within that file. Indeed, as you have tagged, fseek(), (as well as ftell()) can be used to do this. The following pseudo code steps illustrate one possibility:
//store file pointer locations of each line in file:
FILE *fp = fopen(fn, "r");
if(fp)
{
for(int i = 0; i < l_cnt; i++)
{
pos[i] = ftell(fp);
fgets(line, sizeof line, fp);
}
}
fclose(fp);
Then...
//alternately open and close file to view successive lines at stored positions
for(int i = 0; i < line_cnt; i++)
{
FILE *fp = fopen(fn, "r");
if(fp)
{
fseek(fp, pos[i], 0);
fgets(line, sizeof line, fp);
printf("line %d: %s\n", i, line);
fclose(fp);
}
}
There is a more complete source and run-time example here
I am new to C, this is my first project and have been teaching myself. Within my program, one of my functions needs to read a line from a file, and store it in a char array. When I trace the program with gdb the array (line[]) is simply zeros. This leads to my program returning the error "Error: a line in the asset file lacks a ':' separator\n"
Here is my code:
//return the line number (0 based) that the cmd is on, -1 if absent
int locateCmd(char cmd[]) {
int lineIndex = -1; //-1, because lineIndex is incramented before the posible return
char cmdTemp[10] = "\0";
//create a compareable cmd with correct cmd that has its remaining values zeroed out
char cmdCmp[10] = "\0";
memset(cmdCmp, 0, sizeof(cmdCmp));
for (int i = 0; i < strlen(cmd); i++) {
cmdCmp[i] = cmd[i];
}
FILE *file = fopen(ASSET_FILE, "r");
//loop until target line is reached
while (strcmp(cmdTemp, cmdCmp) != 0) {
//check if last line is read
if (lineIndex == lineCounter(file)-1) {
return -1;
}
memset(cmdTemp, 0, sizeof(cmdTemp));
char line[61];
fgets(line, 61, file);
//set cmdTemp to the command on current line
lineIndex++;
for (int i = 0; line[i] != ':'; i++) {
cmdTemp[i] = line[i];
//return error if line doesn't contain a ':'
if (line[i] = '\n') {
printf("Error: a line in the asset file lacks a ':' separator\n");
exit(1);
}
}
}
return lineIndex;
}
Some context, this function is passed a command, and its job is to read a document that appears like this:
command:aBunchOfInfoOnTheComand
anotherCommand:aBunchOfInfoOnTheComand
and pick out the line that the passed command (cmd[]) is stored on.
The issue is with the fgets on line 24. I have separated the relevant portion of this code out into a smaller test program and it works fine.
The test program that works is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[]) {
FILE *file = fopen("tutorInfo.txt", "r");
char line[61];
fgets(line, 61, file);
printf("%s\n", line);
}
The proper exicution of my test program leads me to believe other code in my function is causing the issue, but i'm not sure what. It may be important to note, the problematic code has the same imports as my sample program. Any help would be much appreciated.
As OP didn't provide a Minimal, Complete, and Verifiable example, I have to base my answer on the functional description provided in the question.
I already covered some error and corner cases, but I'm sure I missed some. The approach is also inefficient, as the file is read over and over again, instead of parsing it once and returning a hash/map/directory for easy lookup. In real life code I would use something like GLib instead of wasting my time trying to re-invent the wheel(s)...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_BUFFER_LENGTH 200
unsigned int locateCmd(FILE *fh, const char *key, const char **cmd_line) {
unsigned int found = 0;
size_t key_length = strlen(key);
*cmd_line = NULL;
/* make sure to start read from start of file */
rewind(fh);
unsigned int line_no = 0;
static char buffer[LINE_BUFFER_LENGTH];
while (!feof(fh) && (found == 0)) {
// NOTE: EOF condition will be checked on the next iteration
fgets(buffer, sizeof(buffer), fh);
size_t length = strlen(buffer);
line_no++;
if (buffer[length - 1] != '\n') {
printf("line %u is too long, aborting!\n", line_no);
return(0);
}
if ((strncmp(key, buffer, key_length) == 0) &&
(buffer[key_length] == ':')) {
found = line_no;
buffer[length - 1] = '\0'; // strip line ending
*cmd_line = &buffer[key_length + 1];
}
}
return(found);
}
int main(int argc, char *argv[]) {
FILE *fh = fopen("dummy.txt", "r");
if (!fh) {
perror("file open");
return(1);
}
int ret = 0;
while (--argc > 0) {
const char *cmd;
const char *key = *++argv;
unsigned line_no = locateCmd(fh, key, &cmd);
if (line_no != 0) {
printf("key '%s' found on line %u: %s\n", key, line_no, cmd);
ret = 0;
} else {
printf("key '%s' not found!\n", key);
};
}
if (fclose(fh) != 0) {
perror("file close");
return(1);
}
return(ret);
}
Test input dummy.txt:
command:aBunchOfInfoOnTheComand
anotherCommand:aBunchOfInfoOnTheComand
brokenline
foo:bar
toolong:sadflkjaLKFJASDJFLKASJDFLKSAJ DLFKJ SLDKJFLKASDFASDFKJASKLDJFLKASJDFLKJASDLKFJASLKDFJLKASDJFLKASJDLFKJASDKLFJKLASDJFLKSAJDFLKJASDLKFJKLASDJFLKASJDFKLJASDLKFJLKASDJFLKASJDFLKJSADLKFJASLKDJFLKC
Some test runs:
$ gcc -Wall -o dummy dummy.c
$ ./dummy command foo bar
key 'command' found on line 1: aBunchOfInfoOnTheComand
key 'foo' found on line 5: bar
line 6 is too long, aborting!
key 'bar' not found!
I'm writing a 'C' code that stores the TCP payload of captured packets in a file (payload of each packet is separated by multiple "\n" characters). Using C, is it possible to search for a particular string in the file after all the packets are captured?
P.S : The file can be very large, depending upon the number of captured packets.
Read the file line by line and search using strstr.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
char * pos;
int found = -1;
fp = fopen("filename", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1)
{
pos = strstr(line,"search_string");
if(pos != NULL)
{
found = 1;
break;
}
}
if(found==1)
printf("Found");
else
printf("Not Found");
fclose(fp);
if (line)
free(line);
exit(EXIT_SUCCESS);
}
This may be a simple question with a straightforward answer, but searching through the site I didn't find anything (maybe because I'm new to C programming) except for python codes, which I already wrote and it's quite inefficient.
Suppose I have a list of data in a timestamps.txt file, formatted like this:
<large integer>, <integer between 1 and 8>
<large integer>, <integer between 1 and 8>
and so on (the file is about 4GB)...
What I want to do is to copy only the second column to another file, say singles.txt.
What I did so far works, but it's a rather naive approach and takes too much time. Here's my code:
int main(int argc, char const *argv[])
{
FILE *input_file;
FILE *output_file;
char ch;
int check = 0;
input_file = fopen("timestamps.txt","r");
output_file = fopen("singles.dat","w");
if (!input_file)
return -1;
while((ch = getc(input_file))!=EOF)
{
if(check==1)
{putc(ch,output_file);putc('\n',output_file);}
if(ch == ',')
check = 2;
else
check -= 1;
}
fclose(input_file);
fclose(output_file);
return 0;
}
I'm sure there is a faster way, but I can't seem to make anything work.
Any help would be much appreciated.
Using fgets and fputs is faster than multiple calls to getc and putc, all you need is a buffer (a little buffer in this case) to store the current line:
int main(int argc, char const *argv[])
{
FILE *input_file;
FILE *output_file;
char buf[128];
char *ptr;
input_file = fopen("timestamps.txt","r");
output_file = fopen("singles.dat","w");
if (!input_file)
return -1; /* use EXIT_FAILURE instead of -1 */
/* you forget to check output_file */
while (fgets(buf, sizeof buf, input_file)) {
ptr = strchr(buf, ','); /* find the comma */
if (ptr != NULL) {
fputs(ptr + 1, output_file); /* +1 to skip the comma */
}
}
fclose(input_file);
fclose(output_file);
return 0;
}
Your idea isn't so bad, but you should make your variable check either 0 or 1, depending on whether you want to copy the current input data or not. And you must reset the check with each new line.
Alternatively, you could countthe current field your are in and copy the data when the field is the one you want.
Here's a version that copies the column want delimited by sep verbatim to the output file:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
FILE *in = stdin;
FILE *out = stdout;
int want = 1;
int col = 0;
int sep = ',';
for (;;) {
int c = getc(in);
if (c == EOF) break;
if (c == sep) {
col++;
} else if (c == '\n') {
col = 0;
putc(c, out);
} else if (col == want) {
putc(c, out);
}
}
return 0;
}
(I've used stdin and stdout, because I was lazy and didn't want to do the flie opening and closing stuff.)
I'm new at C and recently finished my work with files. I tried to create a program which will find an entered name in a file but it does not work. Could you try to repair it? I'll be thankful.
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fr;
int i,p,counter,final,length,j,c,k = 0;
char name[256];
char buffer[1024];
fr = fopen("C:/Users/prixi/Desktop/HESLA.TXT","r");
while ((c = fgetc(fr)) != EOF)
counter++;
printf("Enter the name");
scanf("%s",&name);
length = strlen(name);
while (fscanf(fr, " %1023[^\n]", buffer) != EOF) {
for (i = 0; i <= counter; i++)
if (name[0] == buffer[i]){
for (j = 0;j < length; j++ )
if (name[j] == buffer[i+j])
p++;
else
p = 0;
/* The 2nd condition is there because after every name there is ' '. */
if (p == length && buffer[i+j+1] == ' ')
final = 1;
}
}
if ( final == 1 )
printf("its there");
else
printf("its not there");
return 0;
}
It loads the inside of the file to the buffer and then scans char by char depending on how long the file is. I know that it's inefficient and slow, but I have been learning C only for like 4 days. I would really like you to help me fixing my own code otherwise :D I probably wont be able to fall asleep.
There are a lot of way to search a string into a File.
Try this:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char *loadFile(const char *fileName);
int main (void) {
const char *fileName = "test.txt";
const char *stringToSearch = "Addams";
char *fileContent = loadFile(fileName);
if (strstr(fileContent, stringToSearch)){
printf("%s was Found\n",stringToSearch);
}else{
printf("%s was not Found\n",stringToSearch);
}
free(fileContent);
return 0;
}
char *loadFile(const char *fileName){
size_t length,size;
char *buffer;
FILE *file;
file = fopen (fileName , "r" );
if (file == NULL){
printf("Error fopen, please check the file\t%s\n",fileName);
exit(EXIT_FAILURE);
}
fseek (file , 0 , SEEK_END);
length = (size_t)ftell (file);
fseek (file , 0 , SEEK_SET);
buffer = malloc(length+1);
if (buffer == NULL){
fputs ("Memory error",stderr);
exit (2);
}
size = fread (buffer,1,length,file);
if (size != length){
fputs ("Reading error",stderr);
exit(3);
}
buffer[length] = '\0';
fclose (file);
return buffer;
}
Output:
Addams was Found
I have inside the file "test.txt" the following:
Michael Jackson
Bryan Addams
Jack Sparrow
There are multiple problems with your code. You did not post the variable definitions, so we cannot verify if they are used consistently, especially name that should be an array of char.
The main issue is this: you count the number of bytes in fr by reading it, but you do not rewind the stream before scanning it for instances of the string.