I want to read a certain part of a line in C - c

I want to read a specific part of a line. I don't want to add anything after "// " as shown in the example. I've defined a flag for it. But I'm having a hard time getting to the bottom line. How can I solve this ?
void read_file(char *filename) {
int flag = 0;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while(fscanf(f, "%s", line) != EOF) {
if(!strcmp(line, "//")) {
flag = 1;
}
if(flag == 0) {
printf("%s", line);
}
}
}

Your fscanf is wrong. It won't return EOF. You want to compare against != 1 instead.
But, if you want to strip the comment, and just print the [preceding] data, you'll want strstr instead of strcmp
Also, I'd use fgets instead of fscanf ...
Here's your code refactored:
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
void
read_file(char *filename)
{
int flag = 0;
char *cp;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while (1) {
cp = fgets(line,sizeof(line),f);
if (cp == NULL)
break;
cp = strstr(line,"//");
if (cp != NULL) {
*cp++ = '\n';
*cp = 0;
}
printf("%s", line);
}
}
You may want to clean this up a bit more to do better handling of the newline [which fgets retains].
UPDATE:
Yes, my code is fixed. Thank you so much. But I don't understand here : *c = 0
A string in C is a char array that just "happens" to have a zero byte as the string terminator.
fgets will guarantee that the buffer has a trailing 0
Likewise, printf expects line to be 0 terminated.
We're trying to clip out the // and everything else on the line that follows it.
It's not enough to just add back a newline there. We have to add [back] the 0 at the now shortened string as the new place for the string terminator.
If the original line was:
foo // bar<newline><0x00>
We want:
foo <newline><0x00>
If you want to see why, just comment out the *cp = 0;. You'll get "interesting" results ...
UPDATE #2:
In order to not change your code so much, I left the newline in the string.
But, normally, I always strip the newline out before doing any processing. I find that to be a bit cleaner [even if a bit slower].
Here's a version that does that and is closer to what I would have written from scratch:
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
void
read_file(char *filename)
{
int flag = 0;
char *cp;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while (1) {
cp = fgets(line,sizeof(line),f);
if (cp == NULL)
break;
// strip newline
cp = strchr(line,'\n');
if (cp != NULL)
*cp = 0;
// find [and strip] the comment
cp = strstr(line,"//");
if (cp != NULL)
*cp = 0;
printf("%s\n", line);
}
}
Note that because the newline has been pre-stripped [if it exists on the line], the printf has to be modified to add one when printing.
UPDATE #3:
Using strchr and strstr are the easy thing to do. But, this requires that line be scanned twice, which is a bit wasteful.
The str* functions in libc are a convenience, but we can write a custom function that does a single pass on the line and strips either the comment or the newline whichever comes first.
This may help explain what a "string" in C really is, since we're doing "all the magic" so to speak:
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
void
fixline(char *line)
{
int chr;
for (chr = *line++; chr != 0; chr = *line++) {
// strip newline
if (chr == '\n') {
line[-1] = 0;
break;
}
// wait for first "/"
if (chr != '/')
continue;
// peek at next char (is it '/' -- if so, we have "//")
chr = *line;
if (chr == '/') {
line[-1] = 0;
break;
}
}
}
void
read_file(char *filename)
{
int flag = 0;
char *cp;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while (1) {
cp = fgets(line,sizeof(line),f);
if (cp == NULL)
break;
// strip newline and/or comment
fixline(line);
printf("%s\n", line);
}
}
UPDATE #4:
Thank you I got it. But when I try it this way, why can't I get the right result? if(!strcmp("data", line)) { printf("helloo"); } break; When you add this part to the end of the code, it doesn't work correctly.
If you've added this after the newline strip, it should work, because the data line has nothing else on it and it starts in the first position of the buffer.
But, indented this is:
if (!strcmp("data", line)) {
printf("helloo");
}
break;
You probably want:
if (!strcmp("data", line)) {
printf("helloo");
break;
}
Both strchr and strstr scan the entire string looking for a matching character [strchr]. Or, the start of matching substring [strstr].
strcmp does not scan the string in that sense. It just loops through both strings, char-by-char and stops on EOS. It just checks for equal strings, both starting at the first char of each array.
Real strcmp will use special instructions to make it very fast [as will other libc functions], but here is what strcmp actually does:
int
strcmp(const char *s1,const char *s2)
{
int c1;
int c2;
int cmp = 0;
while (1) {
c1 = *s1++;
c2 = *s2++;
if (c1 < c2) {
cmp = -1;
break;
}
if (c1 > c2) {
cmp = +1;
break;
}
// end of string -- both strings equal
if (c1 == 0)
break;
}
return cmp;
}

Format %s reads words (i.e. everything until the next space, tab, newline, ...) but not lines; That's probably why you have a hard time.
For reading in complete lines (i.e. everything up to a new line), use function fgets;
For cutting the line off at a "//", use strstr as follows:
char *beginOfComment = strstr(line,"//");
if (beginOfComment) {
*beginOfComment = 0x0;
}

#Craig Estey
while (1)
{
cp = fgets(line, sizeof(line), f);
if (cp == NULL)
break;
if(!strcmp("actions", cp)) {
break;
}
cp = strstr(line, "//");
if (cp != NULL)
{
*cp++ = '\n';
*cp = 0;
}
printf("%s", line);
}
Why is the strcmp function not working in this way?

Related

Find text inside the beg and end () parentheses in textile and read/print into a buffer. IN C

I am new to C and am getting very frustrated with learning this language. Currently I'm trying to write a program that reads in a program textfile, reads and prints all the string literals, and tokens each on separate line. I have most of it except for one snag. within the text file there is a line such as: (..text..). I need to be able to search, read and print all the text is inside the parentheses on it's own line. Here is an idea I have so far:
#define KEY 32
#define BUFFER_SIZE 500
FILE *fp, *fp2;
int main()
{
char ch, buffer[BUFFER_SIZE], operators[] = "+-*%=", separators[] = "(){}[]<>,";
char *pus;
char source[200 + 1];
int i, j = 0, k = 0;
char *words = NULL, *word = NULL, c;
fp = fopen("main.txt", "r");
fp2 = fopen ("mynewfile.txt","w") ;
while ((ch = fgetc(fp)) != EOF)
{
// pus[k++] = ch;
if( ch == '(')
{
for ( k = 0;, k < 20, K++){
buffer[k] = ch;
buffer[k] = '\0';
}
printf("%s\n", buffer)
}
....
The textfile is this:
#include <stdio.h>
int main(int argc, char **argv)
{
for (int i = 0; i < argc; ++i)
{
printf("argv[%d]: %s\n", i, argv[i]);
}
}
So far I've been able to read char by char and place it into a buffer. But this idea just isn't working, and I'm stumped. I've tried dabbling with strcopy(), ands strtok, but they all take char arrays. Any ideas would be appreciated thank you.
Most likely the best way would be to use fgets() with a file to read in each line as a string (char array) and then delimit that string. See the short example below:
char buffer[BUFFER_SIZE];
int current_line = 0;
//Continually read in lines until nothing is left...
while(fgets(buffer, BUFFER_SIZE - 1, fp) != NULL)
{
//Line from file is now in buffer. We can delimit it.
char copy[BUFFER_SIZE];
//Copy as strtok will overwrite a string.
strcpy(copy, buffer);
printf("Line: %d - %s", current_line, buffer); //Print the line.
char * found = strtok(copy, separators); //Will delmit based on the separators.
while(found != NULL)
{
printf("%s", found);
found = strtok(NULL, separators);
}
current_line++;
}
strtok will return a char pointer to where the first occurrence of a delimiter is. It will replace the delimiter with the null terminator, thereby making "new" string. We can pass NULL to strtok to tell it to continue where it left off. Using this, we can parse line by line from a file based on multiple delimiters. You could save these individual string or evaluate them further.

Using strcat to concatenate lines from a file

I'm trying to read a file and a lines next line, to see if it starts with a space, for instance:
First line
second line
Third line
fourth line
Where when I'm reading the file in, I want to check and see if the following line, has a space, if it does, I want to strcat the two lines (in this case the first and second line).
So for the first example:
1.) Read in the first line, read ahead to the second, see that their is a space, and strcat both strings, making "First linesecond line" (Repeat for any other lines that follow this pattern).
Here's my go at it:
int main(void) {
FILE * file = fopen("file.txt","r");
if(file==NULL) { return 0; }
char * fileBuffer = malloc(sizeof(char)*100);
char * temp = malloc(sizeof(char)*100);
while(fgets(fileBuffer,100,file) != NULL) {
if(isspace(fileBuffer[0])) {
strcpy(temp,fileBuffer);
//store line that has a space in static temporary variable
}
else {
if(strcmp(temp,"") != 0) { //if temp is not empty
strcat(fileBuffer,temp);
}
}
}
free(fileBuffer);
free(temp);
return 0;
}
However this doesn't work. What happens is when fgets gets executed, it reads the first line, it sees there is no white space, and then goes to the next line, sees there is white space, stores it, but now fileBuffer doesn't contain the first line anymore, but the second.
So when I strcat the next time,I don't get the right result of "First linesecond line".
Instead i get the result of the third line mixed with the second, which is not what I want.
I'm not exactly sure how to fix my logic here, any ideas?
fix your logic like this:
#define LINE_SIZE 100
int main(void) {
FILE * file = fopen("file.txt","r");
if(file==NULL) { perror("fopen"); return -1; }
char * fileBuffer = malloc(LINE_SIZE);
char * temp = malloc(LINE_SIZE * 2);//Binding of the string is only once
while(fgets(fileBuffer, LINE_SIZE, file) != NULL) {
if(isspace(fileBuffer[0])) {
temp[strcspn(temp, "\n")] = 0;//remove newline
strcat(temp, &fileBuffer[1]);
printf("%s", temp);
}
else {
strcpy(temp, fileBuffer);
}
}
fclose(file);
free(fileBuffer);
free(temp);
return 0;
}
You have several additional considerations you will need to deal with. The first being, you need to remove the trailing '\n' include in the read by fgets. To do that, you will need the length of the line read. You can then remove the trialing '\n' by overwriting it with a nul-terminating character. e.g.:
while (fgets (buf1, MAXC, fp)) {
size_t len1 = strlen (buf1); /* get length */
if (len1 && buf1[len1-1] == '\n') buf1[--len1] = 0; /* remove \n */
Another consideration is how to manage allocation and freeing of the memory you use for the combined line. Since you are reading both the first part and then second part of your final line from relatively fixed length strings, it would make more sense to use 2 static buffers, one for reading the line, and one for holding a copy of the first. You can allocate for the final result. e.g.
enum { MAXC = 100 }; /* constant for max characters */
...
int main (int argc, char **argv) {
char buf1[MAXC] = {0};
char buf2[MAXC] = {0};
char *both = NULL;
Once you have both parts of your line ready to combine, you can allocate exactly the space needed, e.g.
if (*buf1 == ' ' && *buf2) {
both = malloc (len1 + strlen (buf2) + 1);
strcpy (both, buf2);
strcat (both, buf1);
...
Don't forget to free both after each allocation. Putting the pieces together, and fixing the logic of your comparisons, you could end up with a solution like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 100 };
int main (int argc, char **argv) {
char buf1[MAXC] = {0};
char buf2[MAXC] = {0};
char *both = NULL;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n,", argv[1]);
return 1;
}
while (fgets (buf1, MAXC, fp)) {
size_t len1 = strlen (buf1);
if (len1 && buf1[len1-1] == '\n') buf1[--len1] = 0;
if (*buf1 == ' ' && *buf2) {
both = malloc (len1 + strlen (buf2) + 1);
strcpy (both, buf2);
strcat (both, buf1);
printf ("'%s'\n", both);
free (both);
*buf2 = 0;
}
else
strcpy (buf2, buf1);
}
if (fp != stdin) fclose (fp);
return 0;
}
Output
$ ./bin/cpcat ../dat/catfile.txt
'First line second line'
'Third line fourth line'
Look it over and let me know if you have any questions.
Really a quick and dirty way
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main(void) {
FILE * file = fopen("file.txt", "r");
if (file == NULL) { return 0; }
char fileBuffer[100];
char temp[100];
int first = 1;
while (fgets(fileBuffer, 100, file) != NULL) {
if (isspace(fileBuffer[0])) {
strcat(temp, fileBuffer);
//store line that has a space in static temporary variable
}
else {
if (first == 1){
strncpy(temp, fileBuffer, sizeof(temp));
// Remove the end line
temp[strlen(temp) - 1] = 0;
strcat(temp, " ");
first = 0;
}
else{
if (strcmp(temp, "") != 0) { //if temp is not empty
strcat(temp, fileBuffer);
// Remove the end line
temp[strlen(temp) - 1] = 0;
strcat(temp, " ");
}
}
}
}
printf("%s", temp);
free(fileBuffer);
free(temp);
return 0;
}
Output:

c read block of lines and store them [duplicate]

I am really new to C, and the reading files thing drives me crazy...
I want read a file including name, born place and phone number, etc. All separated by tab
The format might be like this:
Bob Jason Los Angeles 33333333
Alice Wong Washington DC 111-333-222
So I create a struct to record it.
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
I tried many ways to read this file into struct but it failed.
I tired fread:
read_file = fopen("read.txt", "r");
Person temp;
fread(&temp, sizeof(Person), 100, read_file);
printf("%s %s %s \n", temp.name, temp.address, temp.phone);
But char string does not recorded into temp separated by tab, it read the whole file into temp.name and get weird output.
Then I tried fscanf and sscanf, those all not working for separating tab
fscanf(read_file, "%s %s %s", temp.name, temp.address, temp.phone);
Or
fscanf(read_file, "%s\t%s\t%s", temp.name, temp.address, temp.phone);
This separates the string by space, so I get Bob and Jason separately, while indeed, I need to get "Bob Jason" as one char string. And I did separate these format by tab when I created the text file.
Same for sscanf, I tried different ways many times...
Please help...
I suggest:
Use fgets to read the text line by line.
Use strtok to separate the contents of the line by using tab as the delimiter.
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
char* token = strtok(line, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
// Copy token at most the number of characters
// temp.name can hold. Similar logic applies to address
// and phone number.
temp.name[0] = '\0';
strncat(temp.name, token, sizeof(temp.name)-1);
}
token = strtok(NULL, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.address[0] = '\0';
strncat(temp.address, token, sizeof(temp.address)-1);
}
token = strtok(NULL, "\n");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.phone[0] = '\0';
strncat(temp.phone, token, sizeof(temp.phone)-1);
}
Update
Using a helper function, the code can be reduced in size. (Thanks #chux)
// The helper function.
void copyToken(char* destination,
char* source,
size_t maxLen;
char const* delimiter)
{
char* token = strtok(source, delimiter);
if ( token != NULL )
{
destination[0] = '\0';
strncat(destination, token, maxLen-1);
}
}
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
copyToken(temp.name, line, sizeof(temp.name), "\t");
copyToken(temp.address, NULL, sizeof(temp.address), "\t");
copyToken(temp.phone, NULL, sizeof(temp.phone), "\n");
This is only for demonstration, there are better ways to initialize variables, but to illustrate your main question i.e. reading a file delimited by tabs, you can write a function something like this:
Assuming a strict field definition, and your struct definition you can get tokens using strtok().
//for a file with constant field definitions
void GetFileContents(char *file, PERSON *person)
{
char line[260];
FILE *fp;
char *buf=0;
char temp[80];
int i = -1;
fp = fopen(file, "r");
while(fgets(line, 260, fp))
{
i++;
buf = strtok(line, "\t\n");
if(buf) strcpy(person[i].name, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].address, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].phone, buf);
//Note: if you have more fields, add more strtok/strcpy sections
//Note: This method will ONLY work for consistent number of fields.
//If variable number of fields, suggest 2 dimensional string array.
}
fclose(fp);
}
Call it in main() like this:
int main(void)
{
//...
PERSON person[NUM_LINES], *pPerson; //NUM_LINES defined elsewhere
//and there are better ways
//this is just for illustration
pPerson = &person[0];//initialize pointer to person
GetFileContents(filename, pPerson); //call function to populate person.
//...
return 0;
}
First thing,
fread(&temp, sizeof(temp), 100, read_file);
will not work because the fields are not fixed width, so it will always read 20 characters for name 30 for address and so on, which is not always the correct thing to do.
You need to read one line at a time, and then parse the line, you can use any method you like to read a like, a simple one is by using fgets() like this
char line[100];
Person persons[100];
int index;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
persons[i++] = parseLineAndExtractPerson(line);
}
Now we need a function to parse the line and store the data in you Person struct instance
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
Here is a sample implementation of a loop to read at most 100 records
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/path/to/the/file.type", "r");
if (read_file == NULL)
return -1;
index = 0;
while ((index < 100) && (fgets(line, sizeof(line), read_file) != NULL))
{
size_t length;
/* remove the '\n' left by `fgets()'. */
length = strlen(line);
if ((length > 0) && (line[length - 1] == '\n'))
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Here is a complete program that does what I think you need
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/home/iharob/data.dat", "r");
if (read_file == NULL)
return -1;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
size_t length;
length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Parsing strings returned by fgets can be very annoying, especially when input is truncated. In fact, fgets leaves a lot to be desired. Did you get the correct string or was there more? Is there a newline at the end? For that matter, is the end 20 bytes away or 32768 bytes away? It would be nice if you didn't need to count that many bytes twice -- once with fgets and once with strlen, just to remove a newline that you didn't want.
Things like fscanf don't necessarily work as intended in this situation unless you have C99's "scanset" feature available, and then that will automatically add a null terminator, if you have enough room. The return value of any of the scanf family is your friend in determining whether success or failure occurred.
You can avoid the null terminator by using %NNc, where NN is the width, but if there's a \t in those NN bytes, then you need to separate it and move it to the next field, except that means bytes in the next field must be moved to the field after that one, and the 90th field will need its bytes moved to the 91st field... And hopefully you only need to do that once... Obviously that isn't actually a solution either.
Given those reasons, I feel it's easier just to read until you encounter one of the expected delimiters and let you decide the behavior of the function when the size specified is too small for a null terminator, yet large enough to fill your buffer. Anyway, here's the code. I think it's pretty straightforward:
/*
* Read a token.
*
* tok: The buffer used to store the token.
* max: The maximum number of characters to store in the buffer.
* delims: A string containing the individual delimiter bytes.
* fileptr: The file pointer to read the token from.
*
* Return value:
* - max: The buffer is full. In this case, the string _IS NOT_ null terminated.
* This may or may not be a problem: it's your choice.
* - (size_t)-1: An I/O error occurred before the last delimiter
* (just like with `fgets`, use `feof`).
* - any other value: The length of the token as `strlen` would return.
* In this case, the string _IS_ null terminated.
*/
size_t
read_token(char *restrict tok, size_t max, const char *restrict delims,
FILE *restrict fileptr)
{
int c;
size_t n;
for (n = 0; n < max && (c = getchar()) != EOF &&
strchr(delims, c) == NULL; ++n)
*tok++ = c;
if (c == EOF)
return (size_t)-1;
if (n == max)
return max;
*tok = 0;
return n;
}
Usage is pretty straightforward as well:
#include <stdio.h>
#include <stdlib.h>
typedef struct person {
char name[20];
char address[30];
char phone[20];
} Person;
int
main(void)
{
FILE *read_file;
Person temp;
size_t line_num;
size_t len;
int c;
int exit_status = EXIT_SUCCESS;
read_file = fopen("read.txt", "r");
if (read_file == NULL) {
fprintf(stderr, "Error opening read.txt\n");
return 1;
}
for (line_num = 0;; ++line_num) {
/*
* Used for detecting early EOF
* (e.g. the last line contains only a name).
*/
temp.name[0] = temp.phone[0] = 0;
len = read_token(temp.name, sizeof(temp.name), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.address, sizeof(temp.address), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.phone, sizeof(temp.phone), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
// Do something with the input here. Example:
printf("Entry %zu:\n"
"\tName: %.*s\n"
"\tAddress: %.*s\n"
"\tPhone: %.*s\n\n",
line_num + 1,
(int)sizeof(temp.name), temp.name,
(int)sizeof(temp.address), temp.address,
(int)sizeof(temp.phone), temp.phone);
}
if (ferror(read_file)) {
fprintf(stderr, "error reading from file\n");
exit_status = EXIT_FAILURE;
}
else if (feof(read_file) && temp.phone[0] == 0 && temp.name[0] != 0) {
fprintf(stderr, "Unexpected end of file while reading entry %zu\n",
line_num + 1);
exit_status = EXIT_FAILURE;
}
//else feof(read_file) is still true, but we parsed a full entry/record
fclose(read_file);
return exit_status;
}
Notice how the exact same 8 lines of code appear in the read loop to handle the return value of read_token? Because of that, I think there's probably room for another function to call read_token and handle its return value, allowing main to simply call this "read_token handler", but I think the code above gives you the basic idea about how to work with read_token and how it can apply in your situation. You might change the behavior in some way, if you like, but the read_token function above would suit me rather well when working with delimited input like this (things would be a bit more complex when you add quoted fields into the mix, but not much more complex as far as I can tell). You can decide what happens with max being returned. I opted for it being considered an error, but you might think otherwise. You might even add an extra getchar when n == max and consider max being a successful return value and something like (size_t)-2 being the "token too large" error indicator instead.

Read files separated by tab in c

I am really new to C, and the reading files thing drives me crazy...
I want read a file including name, born place and phone number, etc. All separated by tab
The format might be like this:
Bob Jason Los Angeles 33333333
Alice Wong Washington DC 111-333-222
So I create a struct to record it.
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
I tried many ways to read this file into struct but it failed.
I tired fread:
read_file = fopen("read.txt", "r");
Person temp;
fread(&temp, sizeof(Person), 100, read_file);
printf("%s %s %s \n", temp.name, temp.address, temp.phone);
But char string does not recorded into temp separated by tab, it read the whole file into temp.name and get weird output.
Then I tried fscanf and sscanf, those all not working for separating tab
fscanf(read_file, "%s %s %s", temp.name, temp.address, temp.phone);
Or
fscanf(read_file, "%s\t%s\t%s", temp.name, temp.address, temp.phone);
This separates the string by space, so I get Bob and Jason separately, while indeed, I need to get "Bob Jason" as one char string. And I did separate these format by tab when I created the text file.
Same for sscanf, I tried different ways many times...
Please help...
I suggest:
Use fgets to read the text line by line.
Use strtok to separate the contents of the line by using tab as the delimiter.
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
char* token = strtok(line, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
// Copy token at most the number of characters
// temp.name can hold. Similar logic applies to address
// and phone number.
temp.name[0] = '\0';
strncat(temp.name, token, sizeof(temp.name)-1);
}
token = strtok(NULL, "\t");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.address[0] = '\0';
strncat(temp.address, token, sizeof(temp.address)-1);
}
token = strtok(NULL, "\n");
if ( token == NULL )
{
// Deal with error.
}
else
{
temp.phone[0] = '\0';
strncat(temp.phone, token, sizeof(temp.phone)-1);
}
Update
Using a helper function, the code can be reduced in size. (Thanks #chux)
// The helper function.
void copyToken(char* destination,
char* source,
size_t maxLen;
char const* delimiter)
{
char* token = strtok(source, delimiter);
if ( token != NULL )
{
destination[0] = '\0';
strncat(destination, token, maxLen-1);
}
}
// Use an appropriate number for LINE_SIZE
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, sizeof(line), read_file) == NULL )
{
// Deal with error.
}
Person temp;
copyToken(temp.name, line, sizeof(temp.name), "\t");
copyToken(temp.address, NULL, sizeof(temp.address), "\t");
copyToken(temp.phone, NULL, sizeof(temp.phone), "\n");
This is only for demonstration, there are better ways to initialize variables, but to illustrate your main question i.e. reading a file delimited by tabs, you can write a function something like this:
Assuming a strict field definition, and your struct definition you can get tokens using strtok().
//for a file with constant field definitions
void GetFileContents(char *file, PERSON *person)
{
char line[260];
FILE *fp;
char *buf=0;
char temp[80];
int i = -1;
fp = fopen(file, "r");
while(fgets(line, 260, fp))
{
i++;
buf = strtok(line, "\t\n");
if(buf) strcpy(person[i].name, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].address, buf);
buf = strtok(NULL, "\t\n");
if(buf) strcpy(person[i].phone, buf);
//Note: if you have more fields, add more strtok/strcpy sections
//Note: This method will ONLY work for consistent number of fields.
//If variable number of fields, suggest 2 dimensional string array.
}
fclose(fp);
}
Call it in main() like this:
int main(void)
{
//...
PERSON person[NUM_LINES], *pPerson; //NUM_LINES defined elsewhere
//and there are better ways
//this is just for illustration
pPerson = &person[0];//initialize pointer to person
GetFileContents(filename, pPerson); //call function to populate person.
//...
return 0;
}
First thing,
fread(&temp, sizeof(temp), 100, read_file);
will not work because the fields are not fixed width, so it will always read 20 characters for name 30 for address and so on, which is not always the correct thing to do.
You need to read one line at a time, and then parse the line, you can use any method you like to read a like, a simple one is by using fgets() like this
char line[100];
Person persons[100];
int index;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
persons[i++] = parseLineAndExtractPerson(line);
}
Now we need a function to parse the line and store the data in you Person struct instance
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
Here is a sample implementation of a loop to read at most 100 records
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/path/to/the/file.type", "r");
if (read_file == NULL)
return -1;
index = 0;
while ((index < 100) && (fgets(line, sizeof(line), read_file) != NULL))
{
size_t length;
/* remove the '\n' left by `fgets()'. */
length = strlen(line);
if ((length > 0) && (line[length - 1] == '\n'))
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Here is a complete program that does what I think you need
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Person{
char name[20];
char address[30];
char phone[20];
} Person;
char *extractToken(const char *const line, char *buffer, size_t bufferLength)
{
char *pointer;
size_t length;
if ((line == NULL) || (buffer == NULL))
return NULL;
pointer = strpbrk(line, "\t");
if (pointer == NULL)
length = strlen(line);
else
length = pointer - line;
if (length >= bufferLength) /* truncate the string if it was too long */
length = bufferLength - 1;
buffer[length] = '\0';
memcpy(buffer, line, length);
return pointer + 1;
}
Person parseLineAndExtractPerson(const char *line)
{
Person person;
person.name[0] = '\0';
person.address[0] = '\0';
person.phone[0] = '\0';
line = extractToken(line, person.name, sizeof(person.name));
line = extractToken(line, person.address, sizeof(person.address));
line = extractToken(line, person.phone, sizeof(person.phone));
return person;
}
int main(void)
{
char line[100];
Person persons[100];
int index;
FILE *read_file;
read_file = fopen("/home/iharob/data.dat", "r");
if (read_file == NULL)
return -1;
index = 0;
while (fgets(line, sizeof(line), read_file) != NULL)
{
size_t length;
length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
persons[index++] = parseLineAndExtractPerson(line);
}
fclose(read_file);
while (--index >= 0)
printf("%s: %s, %s\n", persons[index].name, persons[index].address, persons[index].phone);
return 0;
}
Parsing strings returned by fgets can be very annoying, especially when input is truncated. In fact, fgets leaves a lot to be desired. Did you get the correct string or was there more? Is there a newline at the end? For that matter, is the end 20 bytes away or 32768 bytes away? It would be nice if you didn't need to count that many bytes twice -- once with fgets and once with strlen, just to remove a newline that you didn't want.
Things like fscanf don't necessarily work as intended in this situation unless you have C99's "scanset" feature available, and then that will automatically add a null terminator, if you have enough room. The return value of any of the scanf family is your friend in determining whether success or failure occurred.
You can avoid the null terminator by using %NNc, where NN is the width, but if there's a \t in those NN bytes, then you need to separate it and move it to the next field, except that means bytes in the next field must be moved to the field after that one, and the 90th field will need its bytes moved to the 91st field... And hopefully you only need to do that once... Obviously that isn't actually a solution either.
Given those reasons, I feel it's easier just to read until you encounter one of the expected delimiters and let you decide the behavior of the function when the size specified is too small for a null terminator, yet large enough to fill your buffer. Anyway, here's the code. I think it's pretty straightforward:
/*
* Read a token.
*
* tok: The buffer used to store the token.
* max: The maximum number of characters to store in the buffer.
* delims: A string containing the individual delimiter bytes.
* fileptr: The file pointer to read the token from.
*
* Return value:
* - max: The buffer is full. In this case, the string _IS NOT_ null terminated.
* This may or may not be a problem: it's your choice.
* - (size_t)-1: An I/O error occurred before the last delimiter
* (just like with `fgets`, use `feof`).
* - any other value: The length of the token as `strlen` would return.
* In this case, the string _IS_ null terminated.
*/
size_t
read_token(char *restrict tok, size_t max, const char *restrict delims,
FILE *restrict fileptr)
{
int c;
size_t n;
for (n = 0; n < max && (c = getchar()) != EOF &&
strchr(delims, c) == NULL; ++n)
*tok++ = c;
if (c == EOF)
return (size_t)-1;
if (n == max)
return max;
*tok = 0;
return n;
}
Usage is pretty straightforward as well:
#include <stdio.h>
#include <stdlib.h>
typedef struct person {
char name[20];
char address[30];
char phone[20];
} Person;
int
main(void)
{
FILE *read_file;
Person temp;
size_t line_num;
size_t len;
int c;
int exit_status = EXIT_SUCCESS;
read_file = fopen("read.txt", "r");
if (read_file == NULL) {
fprintf(stderr, "Error opening read.txt\n");
return 1;
}
for (line_num = 0;; ++line_num) {
/*
* Used for detecting early EOF
* (e.g. the last line contains only a name).
*/
temp.name[0] = temp.phone[0] = 0;
len = read_token(temp.name, sizeof(temp.name), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.address, sizeof(temp.address), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
len = read_token(temp.phone, sizeof(temp.phone), "\t",
read_file);
if (len == (size_t)-1)
break;
if (len == max) {
fprintf(stderr, "Skipping bad line %zu\n", line_num + 1);
while ((c = getchar()) != EOF && c != '\n')
; /* nothing */
continue;
}
// Do something with the input here. Example:
printf("Entry %zu:\n"
"\tName: %.*s\n"
"\tAddress: %.*s\n"
"\tPhone: %.*s\n\n",
line_num + 1,
(int)sizeof(temp.name), temp.name,
(int)sizeof(temp.address), temp.address,
(int)sizeof(temp.phone), temp.phone);
}
if (ferror(read_file)) {
fprintf(stderr, "error reading from file\n");
exit_status = EXIT_FAILURE;
}
else if (feof(read_file) && temp.phone[0] == 0 && temp.name[0] != 0) {
fprintf(stderr, "Unexpected end of file while reading entry %zu\n",
line_num + 1);
exit_status = EXIT_FAILURE;
}
//else feof(read_file) is still true, but we parsed a full entry/record
fclose(read_file);
return exit_status;
}
Notice how the exact same 8 lines of code appear in the read loop to handle the return value of read_token? Because of that, I think there's probably room for another function to call read_token and handle its return value, allowing main to simply call this "read_token handler", but I think the code above gives you the basic idea about how to work with read_token and how it can apply in your situation. You might change the behavior in some way, if you like, but the read_token function above would suit me rather well when working with delimited input like this (things would be a bit more complex when you add quoted fields into the mix, but not much more complex as far as I can tell). You can decide what happens with max being returned. I opted for it being considered an error, but you might think otherwise. You might even add an extra getchar when n == max and consider max being a successful return value and something like (size_t)-2 being the "token too large" error indicator instead.

How do I return a specific line from a file if it partially matches a string parameter? [C]

I am new to C but I am trying to write a function that returns a line from a file depending on the parameter used. It will return the last line that contains that parameter. I think it's better explained using an example:
Here is the contents of the file:
1 one onehello
2 two twohello
3 one threehello
So, if I call the function like this:
lineContaining("one")
It should return "one threehello"
Here is what I have so far, it also includes a main function that tests the function:
char *readStringedCommand(char *str1)
{
int size = 1024;
char *buffer = malloc(size);
char *result = malloc(size);
FILE *fp;
fp = fopen("test.txt", "r");
while(fgets(buffer, 1024, fp)) //get a line from a file
{
printf("while1 entered: %s", buffer);
int i = 0;
int j = 0;
int k = 0;
while(buffer[i] != '\n') //read all the way to the end of a line
{
printf("while2 entered: %s", buffer+i);
k = i;
while(buffer[k]==str1[j]) //while two characters match
{
printf("while3 entered");
k++;
j++;
strcat(result, buffer+k); //append the character to the result
if(str1[j] = '\0') //if the next character of str1 is the last one
{
strncat(result, buffer+k, 20); //append the rest of buffer to the result
return result;
printf("result = %s", result);
}
}
result[0] = '\0'; //clear result for the next line
j = 0; //set str1's position to 0
k = 0;
i++;
}
}
return "errorrrrrr";
}
int main(int argc, const char* argv[])
{
int num1 = 1;
char str1[] = "one onehello";
int num2 = 2;
char str2[] = "two twohello";
int num3 = 3;
char str3[] = "one threehello";
hwrite(num1, str1); //a function I made that writes a line to a file
hwrite(num2, str2);
hwrite(num3, str3);
printf("%s", readStringedCommand("one"));
return 0;
}
Okay, the function gives me an error:
while1 entered: 1 one onehello
while2 entered: 1 one onehello
while2 entered: one onehello
while2 entered: one onehello
Segmentation fault (core dumped)
Considering it gives me the error at the third while loop, I think the problem is there. I unfortunately don't know what is wrong here. I am sure there are more errors after that point but this one is confusing me.
MY QUESTIONS:
How do I fix this segmentation error?
The code is obviously very ugly but I suck at C. Is there a better way to solve this question?
Thanks for reading all of this and I would appreciate some help. =(
EDIT: After fixing some errors suggested by you guys, I no longer get the segmentation error. The function returns " onehello" instead, which is wrong. It should return "one threehello". But I am making progress, and for that I am thankful.
if(str1[j] = '\0')
should be
if(str1[j] == '\0')
you probably want to compare values
the loop while(buffer[i] != '\n') might not exit if your file is missing a newline character, what might happen in the last line of in .txt file.
So if I understand your problem correctly, your goal can be achieved in a much simpler way:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char *readStringedCommand(char *str1)
{
int size = 1024;
char *buffer = malloc(size);
char *result = malloc(size);
/* Check allocations */
if (!buffer || !result) {
fprintf(stderr, "Allocation failure, exiting.\n");
exit(EXIT_FAILURE);
}
result[0] = 0; // 0-terminate result
FILE *fp;
fp = fopen("test.txt", "r");
/* Check whether file was opened successfully */
if (!fp) {
fprintf(stderr, "Couldn't open file.\n");
exit(EXIT_FAILURE);
}
while(fgets(buffer, size, fp)) //get a line from a file
{
if (strstr(buffer, str1)) { // Check whether the line contains the needle
strcpy(result, buffer); // Copy the line if it does
}
}
// close file
fclose(fp);
// Free memory we don't need anymore
free(buffer);
// Now, what if no line contained the needle?
if (result[0] == 0) {
// Hmm, maybe
free(result);
return NULL; // ?
} else {
return result;
}
}
Just use strstr from the standard library to check whether str1 is contained in each line, copy each line that contains it to result, ignore the others. Then at the end, result contains the last line that contained str1 - if any.
If you don't want the entire line, but only the part starting at the first occurrence of str1, capture the pointer that strstr returns, and use that as the source for the strcpy:
char *needle;
if ((needle = strstr(buffer, str1))) {
strcpy(result, needle);
}

Resources