how to read text files - c

I am trying to extract key, values from a text file, but I am having trouble determining how to locate the end of a value. Here is a short snippet of the text file.
GIRRAFE: A tall spotted animal
LION: A short carnivore.
Prince: The son of a king.
Princess: The daughter of a king.
This is my code:
FILE *fp;
char line[20], word[20];
int i = 0, endind;
fp = fopen(file, "r");
if (fp==NULL){
printf("Error parsing the file\n");
exit(1);
}
while (!feof(fp)){
fgets(line, 100, fp);
for (i;i<strlen(line);i++){
if (line[i]=='.'){
endind = i;
}
}
for (i;i<endind;i++){
word[i] = line[i];
printf("%s\n",word);
}
}
The code is not very good as Im not able to get a value ending with a complete blank newline.

From the sample data, it looks like the key ends at the first '.'
in the string. Use strchr(3) to find it. But it looks like
the value, and the whole item, ends with two newlines. For
that you will need to write code to read a paragraph into a string.
For that, malloc(3) and realloc(3) will be useful. If you have
a known maximum size, you can of course use a fixed size buffer.
Break the problem into parts. First, read a paragraph, then find
where the key ends, then find where the value starts. Decide if the
two newlines are part of the value, and whether the period is part
of the key.
To read a paragraph, read in a line. If the line is empty, which you can determine with strcmp(line, "\n") then you're done reading the value,
and you can move on. Otherwise, append the line to the paragraph buffer.
Once you've got a whole paragraph as a single string, find the end of the
key with char *keyend = strchr(para, '.'), which will return a pointer to the '.' character. You can replace that character with a null (*keyend = 0)
and now para is a string with the key. Next advance the keyend pointer
to the first non-whitespace character. There are several ways to do that. At this point, keyend will now point to the value. Which
gives you para as a pointer to the key, and keyend as a pointer to the
value. Having that, you can update your hash table.
I would also check for errors along the way, and probably use separate
variables better named for the paragraph, key, and value. Trimming
off the trailing newline and other data validation is optional. For example, what if a paragraph doesn't contain a '.' character at all?

You are on the right track. The simple way to determine if you have an empty line (in your case) is:
fgets(line, 100, fp);
if (*line == '\n')
// the line is empty
(note: if (line[0] == '\n') is equivalent. In each case you are simply checking whether the 1st char in line is '\n'. Index notation of line[x] is equivalent to pointer notation *(line + x), and since you are checking the 1st character, (e.g. x=0), pointer notation is simply *line)
While you are free to use strtok or any other means to locate the 1st '.', using strchr() or simply using a pointer to iterate (walk-down) the buffer until you find the first '.' is probably an easier way to go. Your parsing flow should look something like:
readdef = 0; // flag telling us if we are reading word or definition
offset = 0; // number of chars copied to definition buffer
read line {
if (empty line (e.g. '\n')) { // we have a full word + definition
add definition to your list
reset readdef flag = 0
reset offset = 0
}
else if (readdef == 0) { // line with word + 1st part of definiton
scan forward to 1st '.'
check number of chars will fit in word buffer
copy to word buffer (or add to your list, etc..)
scan forward to start of definition (skip punct & whitespace)
get length of remainder of line (so you can save offset to append)
overwrite \n with ' ' to append subsequent parts of definition
strcpy to defn (this is the 1st part of definition)
update offset with length
set readdef flag = 1
}
else { // we are reading additional lines of definition
get length of remainder of line (so you can save offset to append)
check number of chars will fit in definition buffer
snprintf to defn + offset (or you can use strcat)
update offset with length
}
}
add final defintion to list
The key is looping and handling the different states of your input (either empty-line -- we have a word + full definition, readdef = 0 we need to start a new word + definition, or readdef = 1 we are adding lines to the current definition) You can think of this as a state loop. You are simply handling the different conditions (or states) presented by your input file. Note -- you must add the final definition after your read-loop (you still have the last definition in your definition buffer when fgets returns EOF)
Below is a short example working with your data-file. It simply outputs the word/definition pairs -- where you would be adding them to your list. You can use any combination of strtok, strchr or walking a pointer as I do below to parse the data file into words and definitions. Remember, if you ever find a problem where you can't make strtok fit your data -- you can always walk a pointer down the buffer comparing each character as you go and responding as required to parse your data.
You can also use snprintf or strcat to add the multiple lines of definitions together (or simply a pointer and a loop), but avoid strncpy, especially for large buffers -- it has a few performance penalties as it zeros the unused space every time.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXW 128 /* max chars in word or phrase */
#define MAXC 1024 /* max char for read buffer and definition */
int main (int argc, char **argv) {
int readdef = 0; /* flag for reading definition */
size_t offset = 0, /* offset for each part of definition */
len = 0; /* length of each line */
char buf[MAXC] = "", /* read (line) buffer */
word[MAXW] = "", /* buffer storing word */
defn[MAXC] = ""; /* buffer storing definition */
/* open filename given as 1st argument, (or read stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
char *p = buf; /* pointer to parse word & 1st part of defn */
if (*buf == '\n') { /* empty-line, output definition */
defn[offset-1] = 0; /* remove trailing ' ' left for append */
printf ("defn: %s\n\n", defn);
readdef = 0; /* reset readdef flag - 0 */
offset = 0; /* reset offset - 0 */
}
else if (readdef == 0) { /* line contais word + 1st part of defn */
while (*p && *p != '.') /* find the first '.' */
p++;
if (p - buf + 1 > MAXW) { /* make sure word fits in word */
fprintf (stderr, "error: word exceeds %d chars.\n", MAXW - 1);
return 1;
}
snprintf (word, p - buf + 1, "%s", buf); /* copy to word */
printf ("word: %s\n", word); /* output word */
while (ispunct (*p) || isspace (*p)) /* scan to start of defn */
p++;
len = strlen (p); /* get length 1st part of defn */
if (len && p[len - 1] == '\n') /* chk \n, overwrite with ' ' */
p[len - 1] = ' ';
strcpy (defn, p); /* copy rest of line to defn */
offset += len; /* update offset (no. of chars in defn) */
readdef = 1; /* set readdef flag - 1 */
}
else { /* line contains next part of defn */
len = strlen (buf); /* get length */
if (len && buf[len - 1] == '\n') /* chk \n, overwite w/' ' */
buf[len - 1] = ' ';
if (offset + len + 1 > MAXC) { /* make sure it fits */
fprintf (stderr, "error: definition excees %d chars.\n",
MAXC - 1);
return 1;
}
snprintf (defn + offset, len + 1, "%s", buf); /* append defn */
offset += len; /* update offset */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
defn[offset-1] = 0; /* remove trailing ' ' left for append */
printf ("defn: %s\n\n", defn); /* output final definition */
return 0;
}
Example Input File
$ cat dat/definitions.txt
ACTE. A peninsula; the term was particularly applied by the ancients to
the sea-coast around Mount Athos.
ACT OF COURT. The decision of the court or judge on the verdict, or the
overruling of the court on a point of law.
TELEGRAPH, TO. To convey intelligence to a distance, through the medium
of signals.
TELESCOPIC OBJECTS. All those which are not visible to the unassisted
eye.
TELL OFF, TO. To divide a body of men into divisions and subdivisions,
preparatory to a special service.
TELL-TALE. A compass hanging face downwards from the beams in the cabin,
showing the position of the vessel's head. Also, an index in front of
the wheel to show the position of the tiller.
Example Use/Output
$ /bin/read_def <dat/definitions.txt
word: ACTE
defn: A peninsula; the term was particularly applied by the ancients to the sea-coast around Mount Athos.
word: ACT OF COURT
defn: The decision of the court or judge on the verdict, or the overruling of the court on a point of law.
word: TELEGRAPH, TO
defn: To convey intelligence to a distance, through the medium of signals.
word: TELESCOPIC OBJECTS
defn: All those which are not visible to the unassisted eye.
word: TELL OFF, TO
defn: To divide a body of men into divisions and subdivisions, preparatory to a special service.
word: TELL-TALE
defn: A compass hanging face downwards from the beams in the cabin, showing the position of the vessel's head. Also, an index in front of the wheel to show the position of the tiller.
Look things over and let me know if you have further questions.

Related

Unable to read and print all the information from a File in C

I am simply trying to read all the information from a file and print it back to the screen, but it only prints one row of info.
Also, if you notice from the 3rd row in the txt file the last column has no ID info, and before when I had my code formatted a different way it was reading up until that 3rd row and then became an infinite loop because the ID was non-existent.
However, as it is now it only reads the first row of information from the text file and then comes to a stop and I can't figure out why.
NOTE: I have certain constraints, and I am only able to be used fscanf() to read from the file.
Code:
/*
Room# Equipment Bed# Personnel PatientID (these headings are not supposed to be in the text file)
230 respirator 2 none 1000212,1000217
231 none 4 nurse 1000214
232 none 2 doctor
233 none 1 nurse 1000219
*/
#include <conio.h>
#include <stdio.h>
typedef struct Rooms
{
int roomNum;
char respirator[11];
int bedNum;
char personnel[7];
int currPatients[5];
} rooms;
void aiAssign();
int main(void)
{
aiAssign();
getch();
return 0;
}
void aiAssign()
{
rooms build1;
FILE* rPtr = fopen("rooms.txt", "r");
while(fscanf(rPtr,"%d\t%s\t%d\t%s\t", &build1.roomNum, build1.respirator, &build1.bedNum, build1.personnel) == 4)
{
int i=0;
while(fscanf(rPtr,"%d%*c ", &build1.currPatients[i++]) == 1);
printf("%d %s %d %s", build1.roomNum, build1.respirator, build1.bedNum, build1.personnel);
for (i=0;i<2;i++)
{
if (build1.currPatients[i] >= 1000000)
{
printf("\t%d", build1.currPatients[i]);
}
}
printf("\n");
}
fclose(rPtr);
}
Reading with fscanf() is doable (if you are very careful) but is NOT the correct way to approach this problem. Why?
When reading with fscanf() all conversion specifiers except %c, %[..] and %n discard leading whitespace. The '\n' marking the end of the line is whitespace. So reading with fscanf() you have no way to prevent reading past the '\n' and extracting data from the following line if you attempt to read with more conversions specifiers than there is data for (unless you are very careful)
Normally, you would read the entire line-at-a-time with fgets() or POSIX getline(), then you can trivially pass the line to sscanf() to attempt to extract all data using a single format-string. That way the end-of-line is respected with each read and there is no possibility of reading past the end of line.
That said, it is doable. Before looking at how, let's look at how you will store your data. Unless you simply want to separate and print the data, you will need an array of rooms to store each line (record) worth of data. You should declare the array in main() and pass it as a parameter to aiAssign(). You can declare the array as follows:
#define MAXROOMS 10 /* if you need a constant, #define one (or more) */
...
int main (int argc, char **argv)
{
rooms build1[MAXROOMS] = {{ .roomNum = 0 }}; /* array of rooms, init all zero */
size_t nrooms = 0; /* number of rooms */
(note: it is imperative you initialize the array of struct all zero because your struct Rooms does not contain a counter for the number of elements stored in you currPatients array. Failing to initialize, you will have no way to access the stored values without invoking Undefined Behavior)
Generally, you also want to open and validate your file is open for reading in the calling function and pass the open FILE* pointer as a parameter to the function to use. If you can't open and validate the file is open in the caller, there is no reason to make your function call to begin with.
Additionally, do not hardcode filenames in your code. Pass the filename as an argument to your program (that's what int argc, char **argv are for) or prompt the user and have them input the filename. You should not have to recompile your program just to read from a different input file. You can take the filename as the first argument to your program, or read from stdin by default if no arguments are given as follows:
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Your aiAssign() has another fatal flaw. With a return type of void, you have no way of communicating how many rooms of data were successfully read from the input file back to the calling function (main() here). For any function that needs to return a positive value, a count, or a length, the choice of size_t as the return type of size_t makes sense and generally, but not always, offers a greater range than int. For example, the prototype for your function, incorporating all that has been discussed above could be:
/* pass open FILE* to read, and pointer to array of rooms to fill
* returns number of rooms of data stored in array
*/
size_t aiAssign (FILE* fp, rooms *r);
Using fscanf() To Read The File
With the preliminaries out of the way, let's look at how you can read your input file with fscanf(). For the reasons related to the end-of-line and whitespace discussed above, you cannot use a single format string to read each line entirely. At best you have to break the read of each line worth of data up into two calls to fscanf(), one to read all information through the .personnel member of your struct, and the second call to read the .currPatients[] array.
In between the two calls you must check for EOF (an input failure -- where EOF is reached before the first valid conversion takes place). You must also scan forward in the input stream using fgetc() (or you can use fscanf() with the %c conversion, up to you) to locate either the next non-whitespace character or the '\n' manually. You can only attempt to read data for the .currPatients[] if additional characters exist in the line. Otherwise, since the %d conversion specifier will ignore leading whitespace, it will happily read and discard the '\n' and begin reading the roomNum member from the following line.
Once you have made the determination and either made or skipped the second call to fscanf(), you need a way of determining if you read valid data. Here, given your input file, what you really care about is that at least four successful conversions took place in the first call to fscanf() and from zero to five conversions took place reading the currPatients[] array data. That gives a range of valid returns from four to nine conversions.
(with any input function you do that by checking the return -- if you take nothing else from this answer, understand you cannot use any input function correctly unless you check the return to validate success or failure of the input)
Putting it altogether for your aiAssign() function, you could do something similar to:
/* pass open FILE* to read, and pointer to array of rooms to fill
* returns number of rooms of data stored in array
*/
size_t aiAssign (FILE* fp, rooms *r)
{
size_t n = 0; /* no. of rooms counter */
/* while array not full */
while (n < MAXROOMS) {
int haspts = 0, /* flag indicating if patients present to read */
rtn1 = 0, /* return for first fscanf */
rtn2 = 0; /* return for second fscanf */
/* read roomNum, respirator, bedNum and personnel */
rtn1 = fscanf (fp, "%d %10s %d %6[^ \t\n]", &r[n].roomNum, r[n].respirator,
&r[n].bedNum, r[n].personnel);
if (rtn1 == EOF) /* if input-failure (no conversions before EOF), break */
break;
/* loop to find next character or newline */
for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
if (!isspace(c)) { /* if non-whitespace character */
ungetc (c, fp); /* put it back in input stream */
haspts = 1; /* set has patients flag true */
break;
}
else if (c == '\n') /* if newline, leave has patients false */
break;
if (haspts) /* if patient info to read, attempt to fill array */
rtn2 = fscanf (fp, "%d,%d,%d,%d,%d", &r[n].currPatients[0],
&r[n].currPatients[1], &r[n].currPatients[2],
&r[n].currPatients[3], &r[n].currPatients[4]);
/* no patients or first fscanf all conversions took place */
if ((rtn1 && !rtn2) || (rtn1 == 4)) {
/* validate at least 4 up to 9 total conversions took place */
switch (rtn1 + rtn2) {
case 9:
case 8:
case 7: /* case fall-through intentional */
case 6:
case 5:
case 4:
n++; /* increment counter */
break;
}
}
/* remove all characters up to '\n' */
if (haspts)
for (int c = fgetc(fp); c != '\n' && c != EOF; c = fgetc(fp)) {}
}
return n; /* return number of rooms */
}
(note: the haspts (short for "has patients") flag is used to indicate whether the second call to fscanf() for currPatients data is needed. Note also you cannot read string data with any of the scanf() family of functions without also including the field-width modifier to prevent reading more characters than your array can hold, resulting in a buffer overrun. Without the field-width modifier, reading string data with scanf() is no safer than using gets(), see Why gets() is so dangerous it should never be used!)
A short example using the function to read the data from your file using only fscanf() could be done as:
#include <stdio.h>
#include <ctype.h>
#define MAXROOMS 10 /* if you need a constant, #define one (or more) */
typedef struct Rooms {
int roomNum;
char respirator[11];
int bedNum;
char personnel[7];
int currPatients[5];
} rooms;
/* pass open FILE* to read, and pointer to array of rooms to fill
* returns number of rooms of data stored in array
*/
size_t aiAssign (FILE* fp, rooms *r)
{
size_t n = 0; /* no. of rooms counter */
/* while array not full */
while (n < MAXROOMS) {
int haspts = 0, /* flag indicating if patients present to read */
rtn1 = 0, /* return for first fscanf */
rtn2 = 0; /* return for second fscanf */
/* read roomNum, respirator, bedNum and personnel */
rtn1 = fscanf (fp, "%d %10s %d %6[^ \t\n]", &r[n].roomNum, r[n].respirator,
&r[n].bedNum, r[n].personnel);
if (rtn1 == EOF) /* if input-failure (no conversions before EOF), break */
break;
/* loop to find next character or newline */
for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
if (!isspace(c)) { /* if non-whitespace character */
ungetc (c, fp); /* put it back in input stream */
haspts = 1; /* set has patients flag true */
break;
}
else if (c == '\n') /* if newline, leave has patients false */
break;
if (haspts) /* if patient info to read, attempt to fill array */
rtn2 = fscanf (fp, "%d,%d,%d,%d,%d", &r[n].currPatients[0],
&r[n].currPatients[1], &r[n].currPatients[2],
&r[n].currPatients[3], &r[n].currPatients[4]);
/* no patients or first fscanf all conversions took place */
if ((rtn1 && !rtn2) || (rtn1 == 4)) {
/* validate at least 4 up to 9 total conversions took place */
switch (rtn1 + rtn2) {
case 9:
case 8:
case 7: /* case fall-through intentional */
case 6:
case 5:
case 4:
n++; /* increment counter */
break;
}
}
/* remove all characters up to '\n' */
if (haspts)
for (int c = fgetc(fp); c != '\n' && c != EOF; c = fgetc(fp)) {}
}
return n; /* return number of rooms */
}
int main (int argc, char **argv)
{
rooms build1[MAXROOMS] = {{ .roomNum = 0 }}; /* array of rooms, init all zero */
size_t nrooms = 0; /* number of rooms */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
nrooms = aiAssign (fp, build1); /* read room data, save return */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!nrooms) { /* no rooms of data read */
fputs ("error: nothing read from file.\n", stderr);
return 1;
}
for (size_t i = 0; i < nrooms; i++) { /* output all data */
printf ("%3d %-11s %d %-7s ", build1[i].roomNum, build1[i].respirator,
build1[i].bedNum, build1[i].personnel);
/* possible due to initialization of array all zero at declaration */
for (int j = 0; build1[i].currPatients[j] && j < 5; j++)
printf (j ? ":%d" : "%d", build1[i].currPatients[j]);
putchar ('\n');
}
/* only hold terminal window open on windows */
#if defined (_WIN32) || defined (_WIN64)
getchar(); /* do not use getch() (100% non-portable DOS conio.h required) */
#endif
return 0;
}
(note: the inclusion of the ctype.h header to help when checking for whitespace with isspace() and note the removal of the archaic DOS header conio.h. You should simply use getchar() to hold the terminal open on windows instead of getch(). Including conio.h makes your program 100% NON-portable to anything but windows)
Example Use/Output
With your data in rooms.txt including the entire first line with your comment, you would have:
$ ./bin/buildrooms dat/rooms.txt
230 respirator 2 none 1000212:1000217
231 none 4 nurse 1000214
232 none 2 doctor
233 none 1 nurse 1000219
(note: the currPatients array data is output separate by ':' instead of ',')
While you can read your data with fscanf() (if you are very careful), you really shoudn't. fgets()/sscanf() provides a much easier and more robust approach. However, as an exercise, this is a pretty darn good one to teach you the consideration that go into selection and using the proper input functions as well as on the use (and limitations) of fscanf().
There is a lot involved, so take the review of the code slowly, line-by-line, expression-by-expression and make sure you understand why the code is written the way it is. If you get stuck, drop a comment below and I'm happy to help further.
I think you are over complicating it, if your only purpose is to read the file line by line and then print it to the screen, you could use the simpler functions open, read and write. Check them. Define a buffer and use it to read, then use stdout file descriptor to write the data you read in the buffer . Keep the looping till the number of bytes read returned by the read function is less than the buffer size. The file descriptor for the stdout is 1. Here is a link that explains how these functions work
https://www.google.es/amp/s/www.geeksforgeeks.org/input-output-system-calls-c-create-open-close-read-write/amp/
If you need to convert the raw text file to those data structures then you a more intelligent processing.
The functions fscanf and printf are higher level built on top of these ones I suggest you. They receive formats strings which uses as guidelines to automate processing like the integer conversion from ascii when you specify %d . This was an example .

C code that reads csv files crashes due to the file being too long

I have a program to reads and prints the phrases in a csv file using C programming language. The program to output the phrase after the set number of commas within the csv file (point variable) . The csv file is a very long dataset. The C code below has a problem where after a certain amount of outputs it gives an error and closes the file before it reaches the end of the file. In addition to that I want to add another character like (point) called (point2) however instead of it starting from the 10th comma I want the new point to start from 8 and then gets incremented by 7 again.
#include<stdio.h>
// fetch the phrase based on the point
void fetchPhrase(int point){
// create FILE pointer variable which will access the file
FILE *fp;
// read character from file
char ch;
int flagEndFile = 0;
// store phrase that is readed from file
char phrase[100];
// work as index for phrase
int index = 0;
// track the ","
int counter = 0;
// open the file in read mode
fp = fopen("input.csv", "r");
// check whether file is opened or not
if(fp == NULL){
printf("File not found!");
exit(1);
}
// read the character untill the file is not end
while((ch = fgetc(fp)) != EOF){
// check whether character is ',' or not
if(ch == ','){
// increment the counter
counter++;
}
// when we get the "," as well as correct point
if(ch == ',' && counter == point){
// read the phrase until the program does not found ","
while((ch = fgetc(fp)) != ','){
// if file is ended then break the loop
if(ch == EOF){
break;
}
// otherwise store phrase into the phrase array
phrase[index++] = ch;
}
// break the loop
break;
}
}
// store null into the character array
phrase[index] = '\0';
// check whether the point is found or not in the file
if(ch == EOF && phrase[0] == '\0'){
// point is not found in the file
printf("There is no point in the 'input' file");
}else{
// otherwise print the phrase
printf("\n Price is: %s", phrase);
}
}
void main(){
// create FILE pointer variable which will access the file
FILE *fp;
// store usr input
int point = 10;
// this loop run untill the user does not enter -999.
while(point != -999){
// call the function which display the phrase
fetchPhrase(point);
//Nexct price value is 7 commas after
point = point + 7;
//printf("The price is %d\n", point);
// get the input from the user
//printf("\nAfter what comma do you want the code to print: ");
//scanf("%d", &point);
}
// close the file
fclose(fp);
}
The big issue I see in fetchPhrase() is when you find the ',' you want, you read the comma as part of phrase. The next big issue I see is since you are reading with fgetc(), you are not checking for '\n'. So for example when you update point = point + 7 and there are less than that number of fields left in the current record (line), you then read and ignore the '\n' and start attempting to read fields from the next line. Since a '\n' isn't a ',' you never increment counter so your field count will be off when you begin reading from the next line.
Now it is difficult to know how to handle all your requests for "point" if it wraps and begins reading from the next line without a detailed explanation, but returning if you ask for more fields than are in the line sounds reasonable and could be done with something similar to:
#define PHRASE 100
int fetchPhrase (int point, FILE *fp)
{
char phrase[PHRASE] = ""; /* buffer to hold phrase */
int ch, /* current character */
index = 0, /* phrase index */
counter = 0; /* separator counter */
/* read all chars in line */
while ((ch = fgetc (fp)) != '\n' && ch != EOF) {
/* check if wanted field (point) found */
if (counter == point) {
/* protect phrase bounds, while not next separator */
while (index + 1 < PHRASE && ch != ',' && ch != '\n' && ch != EOF) {
phrase[index++] = ch; /* store character in phrase */
ch = fgetc (fp);
}
phrase[index] = '\0'; /* nul-terminate */
break; /* break read loop */
}
if (ch == ',') /* comma found */
counter++; /* increment counter */
}
if (counter == point && phrase[0] != 0) /* phrase found */
printf ("\n Price is: %s\n", phrase);
else
fputs ("There is no point in line.\n", stderr);
return ch; /* return stream state good (>0) if not EOF */
}
(note: untested, no sample data provided, but the points (fields) are zero indexed, meaning if you ask for point == 0 it will return the 1st field in the line, point == 1 will be the 2nd field (after the 1st ','), and so on...)
That way you are only reading characters in the field, not the ','. Also note phrase is nul-terminated immediately after it is filled.
Note: the other changes. Do not hardcode filenames in functions. Do not use MagicNumbers. Pass the open FILE* stream as a parameter to your function instead of hardcoding input.csv in your function. You should not have to recompile your code just to read from a different filename. For your MagicNumber 100 in phrase[100], #define a constant any time one is needed. That provides a convenient place at the beginning of your code to make adjustments without having to pick through the body of your code.
You must protect your array bounds when filling phrase with characters. If your phrase is longer than your array, you need to stop adding characters while preserving room for the nul-terminating character. Checking index + 1 < PHRASE does that for you.
Any time your function can succeed or fail, you must declare a meaningful return type for the function to indicate success/failure. In this case, since you want to read from the file multiple times, you need a way to indicate whether EOF was reached. Returning an int for true / false is a simple way to indicate success or failure and can be used to indicate whether EOF has been reached by returning the result of ch != EOF.
The correct use of main()
Unless you are programming in a freestanding environment (without the benefit of any OS), in a standards conforming implementation, the allowable declarations for main for are int main (void) and int main (int argc, char *argv[]) (which you will see written with the equivalent char **argv). See: C11 Standard - §5.1.2.2.1 Program startup(p1). See also: What should main() return in C and C++? In a freestanding environment, the name and type of the function called at program startup are implementation-defined. See: C11 Standard - 5.1.2.1 Freestanding environment
Passing the Filename to Read as an Argument
With int main (int argc, char **argv), you can pass information into your program from the command line. argc is your argument-count telling you the number of command line arguments provided (note: there is always 1 argument, argv[0] always holds the program name). So the first argument passed to the program is argv[1] and so on. argv is an array of pointers to the arguments which are nul-terminated strings with the next pointer after the last string set to NULL as a sentinel.
You can check for the filename passed as an argument to your program (or read from stdin by default) using the following:
/* pass filename as 1st argument to program, or read stdin by default */
int main (int argc, char **argv)
{
int point = 10;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Now you can pass the open FILE* pointer to your function as an argument -- your function can rely on the fact the file has been opened and validated open for reading in the caller:
fetchPhrase (point, fp);
You can use also use the return to determine if EOF has been reached:
while (fetchPhrase (point, fp) != EOF) {
...
}
Using a Line-Oriented Approach
While your use of fgetc() is fine, it complicates reading records (lines) in your input file. If you instead read each line with a line-oriented input function such as fgets() or POSIX getline(), then processing requests for offsets from fields in the file would be limited to the fields contained in the line and would eliminate your testing of '\n' to constrain your field requests to a single record.
Those were the thoughts I had on your code. Look things over and let me know if you have further questions.

Split csv file name to get data using C language

I have c variable file_path -
"a/b/c/xx.xxx_LOB_xxxx.caseno_YYYYMMDD.seq_no.csv"
From this file_path variable, I want to get the value of file_name excluding path,LOB,caseno,file_date(YYYYMMDD) and seq_no using C language in different variables. I tried with strtok() but not able to get the values.
Can you suggest how will get the value of each variable?
Thank you.
You have several options to separate the string. (you always have several options for parsing strings in C) You can always use a pair of pointers to work your way down the input string, bracketing and copying any set of characters between the two pointers. (you can operate on a non-mutable string like a String Literal because the original isn't modified)
You can use strtok() to help break the original up into smaller parts (sometimes into exactly what you need). However in this case, since '_' can be both a delimiter as well as be an included character in what you extract, you would still need to manually parse what you need from the tokens separated by strtok(). (strtok() modifies the string it operates on, so it must be mutable)
A third option is to craft a format-string and use sscanf() to parse the variables from the input. Since your format is fixed -- you are in luck and you can simply use sscanf to separate what you need. If you are not intimately familiar with the sscanf format-string and all of the modifiers and conversion specifiers, then spend an hour reading, and understanding, man 3 scanf -- time spent will save you ten-fold hours later.
Your fixed format, assuming no one variable in the string can be greater that 127-characters (adjust as necessary), can be accommodated with the format string:
" %*[^_]_%127[^_]%*[^.].%127[^_]_%127[^.].%127[^.]"
The string is separated into 4 strings. The parts of the string that are not needed are discarded using the assignment suppression operator the '*'. If you are separating the input into an array of strings arr, then you can write a simple function to handle the separation for you, e.g.
int varsfrompath (char (*arr)[MAXLEN], char *str)
{
int i = sscanf (str, " %*[^_]_%127[^_]%*[^.].%127[^_]_%127[^.].%127[^.]",
arr[0], arr[1], arr[2], arr[3]);
return i == EOF ? 0 : i; /* return no. of vars separated */
}
Which returns the number of items successfully parsed from the string. (zero if an input failure occurs)
A working example would be:
#include <stdio.h>
#include <string.h>
#define NELEM 4
#define MAXLEN 128
int varsfrompath (char (*arr)[MAXLEN], char *str)
{
int i = sscanf (str, " %*[^_]_%127[^_]%*[^.].%127[^_]_%127[^.].%127[^.]",
arr[0], arr[1], arr[2], arr[3]);
return i == EOF ? 0 : i; /* return no. of vars separated */
}
int main (void) {
char fname[] = "a/b/c/xx.xxx_LOB_xxxx.caseno_YYYYMMDD.seq_no.csv",
results[NELEM][MAXLEN] = { "" };
int n = varsfrompath (results, fname);
for (int i = 0; i < n; i++)
printf ("results[%2d] = '%s'\n", i, results[i]);
}
Example Use/Output
$ ./bin/varsfrompath
results[ 0] = 'LOB'
results[ 1] = 'caseno'
results[ 2] = 'YYYYMMDD'
results[ 3] = 'seq_no'
This is by far the simplest way to handle your fixed format. A manual parse with a pair of pointers is more involved from an accounting (for where you are in the string standpoint), but no more difficult. (tedious may be the word)
Look things over and if I misinterpreted your separation needs, let me know and I can adjust it.
Manual Parse Using a Pair of Pointers
If rather than spend time with the man 3 scanf man page, you would rather spend time with an 8.5x11 sheet of paper and pencil with your accounting hat on to do the same thing using a pair of pointers, then you could do something similar to the following.
You have a start pointer sp and end pointer ep and you simply work down your line of input to anchor the sp before the variable to extract, and the ep at the end of the variable and then use memcpy() to copy the characters between them. (you will have to adjust by 1 on occasion depending on whether you are pointing at the beginning of the variable you want, or once character before it to the delimiter. (the easy way to get your arithmetic right when working down the string is to only consider there being 1-char between the start and end pointers -- that way whether you need to add or subtract 1 to work around your delimiters will be clear)
You can replace the varsfrompath function above with the one that follows and receive the same results, e.g.:
int varsfrompath (char (*arr)[MAXLEN], const char *str)
{
char *sp, *ep; /* start pointer, end pointer */
int i = 0;
/* set sp to 1st '_' and ep to second '_', copy to arr and nul-terminate */
if (!(sp = strchr (str, '_')) || /* can't find 1st '_' */
!(ep = strchr (sp + 1, '_')) || /* can't find 2nd '_' */
ep - sp - 2 > MAXLEN) /* chars between -1 > MAXLEN */
return 0;
memcpy (arr[i], sp + 1, ep - sp - 1); /* copy ep - sp - 1 chars */
arr[i++][ep - sp - 1] = 0; /* nul-terminate */
sp = ++ep; /* set sp to 1-past ep */
/* set sp to next '.' and ep to next '_", copy to arr and nul-terminate */
if (!(sp = strchr (sp, '.')) || /* can't find next '.' */
!(ep = strchr (sp + 1, '_')) || /* can't find next '_' */
ep - sp - 2 > MAXLEN) /* chars between -1 > MAXLEN */
return i;
memcpy (arr[i], sp + 1, ep - sp - 1); /* copy ep - sp - 1 chars */
arr[i++][ep - sp - 1] = 0; /* nul-terminate */
sp = ++ep; /* set sp to 1-past ep */
/* set ep to next '.', copy to arr and nul-terminate */
if (!(ep = strchr (sp, '.')) || ep - sp - 2 > MAXLEN) /* no '.' or too long */
return i;
memcpy (arr[i], sp, ep - sp); /* copy ep - sp chars */
arr[i++][ep - sp] = 0; /* nul-terminate */
sp = ++ep; /* set sp to 1-past ep */
/* repeate exact same steps for last var */
if (!(ep = strchr (sp, '.')) || ep - sp - 2 > MAXLEN)
return i;
memcpy (arr[i], sp, ep - sp);
arr[i++][ep - sp] = 0;
sp = ++ep;
return i; /* return no. of vars separated */
}
It may look more complicated, but you are actually just using simple string functions like strchr() to position the pointers, and then just extracting the characters between them. Compare and contrast both approaches.

split a user inputed string at a specific letter in c

I am trying to write an if else statement that looks at a user input and then splits it after index[1] if the string includes the letter b or split after index[0] if the string input doesnt include the letter b. How would I approach that? Pretty new to C so not too sure.
This is what I have so far... I think im on the right path and am trying to figure out how I would finish it off so it does what I want it to do.
int split_note_and_chord(char* string, char* note, char* chord)
{
for(user input doesnt have b in it)
{
if(i = 0; i <index; i++)
{
note[i] = string[i];
}
note[index] = 0;
else{ if(i = 0; i < index; i++)
{
note[i] = strlen(string[2]);
}
}
}
C string is nothing but a char array
string.h provides handy functions to check the string contents
you can use if condition and strstr and strchr functions for your logic
For example
#include <stdio.h>
#include <string.h>
int main () {
const char *input = "backwards";
char *ret;
ret = strstr(input, "b");
if( ret != NULL ) {
} else {
}
}
The strstr will return NULL if the b does not exist
You can also use strchr if you want the second argument as single char strchr(input, 'b');
There are a number of ways to approach splitting your input string after the 2nd character if the input contains 'b' or after the 1st character otherwise. Since you are dealing with either a 1 or 2, all you need to do is determine if 'b' is present. The easiest way to do that is with strchr() which will search a given string for the first occurrence of a character, returning a pointer to that character if found, or NULL otherwise. See man 3 strchr
So you can use strchr to test if 'b' is present, if the return isn't NULL split the string after the 2nd character, if it is NULL, split it after the first.
A simple implementation using a ternary to set the split-after size for input read into a buffer buf would be:
char part2[MAXC]; /* buffer to hold 2nd part */
size_t split; /* number of chars to split */
/* if buf contains 'b', set split at 2, otherwise set at 1 */
split = strchr(buf, 'b') ? 2 : 1;
strcpy (part2, buf + split); /* copy part2 from buf */
buf[split] = 0; /* nul-terminate buf at split */
A quick implementation allowing you to enter as many strings as you like and it will split after the 1st or 2nd character depending on the absense, or presence of 'b' would be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer to hold line of input */
fputs ("Enter a string to split (or [Enter] alone to exit)\n\n"
"string: ", stdout);
while (fgets (buf, MAXC, stdin)) { /* loop reading each line */
char part2[MAXC]; /* buffer to hold 2nd part */
size_t split; /* number of chars to split */
if (*buf == '\n') /* if [Enter] alone, exit */
break;
/* if buf contains 'b', set split at 2, otherwise set at 1 */
split = strchr(buf, 'b') ? 2 : 1;
strcpy (part2, buf + split); /* copy part2 from buf */
buf[split] = 0; /* nul-terminate buf at split */
printf (" part1: %s\n part2: %s\nstring: ", buf, part2);
}
}
(note: if you are unfamiliar with the ternary operator, it is simple (test) ? if_true : if_false. Above it is just shorthand for if (strchar (buf, 'b') != NULL) split = 2; else split = 1;)
Example Use/Output
$ ./bin/splitb
Enter a string to split (or [Enter] alone to exit)
string: look out
part1: l
part2: ook out
string: look out below
part1: lo
part2: ok out below
string:
Let me know if this is what you intended. If not, I'm happy to help further. Also, if you have any questions, just let me know.
Edit Based on Comment
It is still unclear what your list of notes are in your header file, but you can simply use a string constant to contain the letters of the notes, e.g.
#define NOTES "abcdefg" /* (that can be a string constant as well) */
(you can add upper case if needed or you can convert the input to lower -- whatever works for you)
If you simply need to find the first occurrence of one of the letters in the NOTES string, then strpbrk() will allow you to do just that returning a pointer to the first character of NOTES found in your string. (you must have some way to handle the user entering, e.g. "the note cflat", which would split on the first 'e' instead of 'c', but you will need to provide further specifics there)
Another consideration is how long note can be. If it is always 1-character, then you can simplify by just comparing against the first character in the string using strchr (NOTES, buf[0]) (which turns the way you normally think about using strchr() around -- using the fist string NOTES and the first char read from user input.
Taking a general approach that would break "---cflat---" into "---c" and "flat---", your function could be similar to:
int split_note_and_chord (char *string, char *note, char *chord)
{
char *p = strpbrk (string, NOTES); /* pointer to first of NOTES in string */
if (p != NULL) { /* if found */
strcpy (note, string); /* copy string to note */
note[p - string + 1] = 0; /* nul-terminate after note */
strcpy (chord, p + 1); /* copy rest to chord */
return 1; /* return success */
}
*note = 0; /* make note and chord empty-string */
*chord = 0;
return 0; /* return failure */
}
(note: if there is no char in NOTES found, then note and chord are made the empty-string by nul-terminating at the first character before returning zero to indication no-note found.)
A quick implementation similar to the first could be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NOTES "abcdefg" /* (that can be a string constant as well) */
int split_note_and_chord (char *string, char *note, char *chord)
{
...
}
int main (void) {
char buf[MAXC], /* buffer to hold line of input */
note[MAXC], /* buffer for node */
chord[MAXC]; /* buffer for chord */
fputs ("Enter a string with node and chord (or [Enter] alone to exit)\n\n"
"string: ", stdout);
/* loop reading each line until [Enter] alone */
while (fgets (buf, MAXC, stdin) && *buf != '\n') {
if (split_note_and_chord (buf, note, chord))
printf (" note : %s\n chord : %s\n", note, chord);
else
fputs ("\nerror: note not found in string.\n\n", stderr);
fputs ("string: ", stdout);
}
return 0;
}
(note: that using fgets() will read and include the '\n' resulting from the user pressing Enter in buf and thus it will also be included in the remainder copied to chord. You can use buf[strcspn (buf, "\n")] = 0; to trim it from buf -- or from chord by substituting chord for buf in the call using strcspn() as the index to nul-terminate at.)
(also note: you can adjust MAXC to fit your needs -- which is why you declare a constant in the first place -- to make it a simple change of one line at the top of your file)
Example Use/Output
Using your function to split various input would result in the following:
$ ./bin/splitb3
Enter a string with node and chord (or [Enter] alone to exit)
string: ---cflat---
note : ---c
chord : flat---
string: asharp
note : a
chord : sharp
string: bflat
note : b
chord : flat
string: hook
error: note not found in string.
string: c
note : c
chord :
There are many, many different way to do this and how to best approach it will depend on how you have your notes and chords defined in your header -- as well as what, if any, limitations you put on what format you require the user to enter. If you need more help, please edit your question and Add the contents of your header so we will know how they are declared and defined, as well as listing any constraints you want to place on what the user can enter.

output formatted text using width and margin

I am trying to write a program that will take the following input and will format it and output it to a text file.
Here is a picture of how it should work
?mrgn left: Each line following the command will be indented left spaces from
the left-­­hand margin. Note that this indentation must be included in the page
width. If this command does not appear in the input file, then the value of left
is 0 (zero).
I did the following so far:
while (fgets(line, MAX_LINE_LEN-1, infile) != NULL){/*Read the first line if it is not empty*/
char last[MAX_WORD_LEN] = {0};
char *p;
for (p = strtok(line, " "); p; p = strtok(NULL, " ")){
if(*last && strcmp(last, width)==0){
iwidth = atoi(p);
printf("width = %d\n", iwidth);
}
if(*last && strcmp(last, margin)==0){
imargin = atoi(p);
printf("margin = %d\n", imargin);
}
strncpy (last, p, MAX_WORD_LEN);
if(iwidth != 0 || imargin != 0){
printf("%s ", p);
}else{
printf("%s", line);
}
}
}
I am able to store the value of width and margin to a variable. I am now stuck on how I can specify the required formatting. I did some research but I couldn't find what I want. Please help!
Thank you!
Cheers!
After you have had several hours to work on the problem, let me give you a few pointers (no pun intended) that may help simplify your approach to the problem. While you can certainly use strtok to parse your option and width from the input file, there really is no need to tokenize when you know the line format containing the option will be "?name value".
A simplified approach, knowing your file contains the option as the first line, would simply be to read the entire line (using fgets or POSIX getline), verify the first character is '?' and then parse the option name and value from the line with sscanf. (you can either include the '?' in your format-string, or simply begin the parse at the 2nd character. (my choice) To accomplish this you could start with something similar to:
#include <stdio.h>
#include <string.h> /* for strcmp, strlen */
#define OPT 32 /* if you need constants, #define one (or more) */
#define WDTH 78
#define MAXC 1024
void str_word_wrap (char *buf, int n); /* wrap function prototype */
int main (void) {
char buf[MAXC] = "", /* buffer to hold words in file */
*p = buf, /* pointer to buf */
opt[OPT] = ""; /* buffer to hold option found in file */
int width = WDTH, /* variable holding width from option */
used = 0, /* number of chars used in buf */
val = 0; /* temp value to read option value from file */
/* option on line 1, read entire line */
if (!fgets (buf, MAXC, stdin) || *buf != '?')
fputs ("error: unexpected file format, using default width.\n\n",
stderr);
/* parse option and value, compare option is "width", use val as width */
if (sscanf (buf + 1, "%s %d", opt, &val) == 2) {
if (strcmp (opt, "width") == 0)
width = val;
}
...
At this point in your code, buf contains the first line, opt contains the option name, and width contains the width specified in the file (or the default width WDTH (78) in the event the first line did not contain the "?width val" information). Ideally, if the first line was not a valid option/value line, you would simply eliminate the excess whitespace from buf, add an ending ' ' and continue, but that code is left to you.
(note: I just redirect the file to stdin so I read from stdin instead of a file here -- but your infile is fine as well. You would just substitute infile where I read stdin)
Since you simply want to eliminate all the additional whitespace from your input file leaving a normally formatted paragraph which you will wrap to the specified width, using fscanf with the "%s" format specifier can handle the whitespace removal automatically. (with the scanf family, "%s" and numeric format specifiers ignore leading whitespace, "%c" and "%[..]" do not). So reading the remainder of the file into your buffer is simply a matter of reading each word in the file, keeping track of how many characters in your buffer you have used (so you know the next word will fit), and adding a ' ' (space) between each word as you add them to your buffer.
You can use strcat if that helps, or you can simply use a pointer and write a ' ' at one past the end of the current buffer and then a nul-terminating character past that on each iteration. Either way, just keep track of how many characters you have used so far and the len (length) of what you are adding, and then update your used count with the length of each word as you go. You could do something like the following:
while (scanf ("%s", p + used) == 1) { /* read each word, ignore WS */
size_t len = strlen (p + used); /* get length of word */
if (used + len + 2 >= MAXC) { /* make sure it fits with ' ' */
fputs ("warning: file truncated.\n", stderr);
break; /* note you can refine to save 1-char space at end */
}
*(p + used + len++) = ' '; /* add space at end of word */
*(p + used + len) = 0; /* nul-termiante after space */
used += len; /* update used with len */
}
*(p + --used) = 0; /* overwrite final ' ' with nul-character */
At this point you could write out your width value and the contents of your filled buffer for a check before you wrap the lines to width. I simply write the width used out before outputting the wrapped lines which completes the main() function of the program, e.g.
printf ("Wrapping file at width: %d\n\n", width);
str_word_wrap (buf, width); /* wrap buffer at width chars/output */
return 0;
}
All that remains is finishing the function to wrap the buffer to no more that width characters per line as you output your buffer. I provided the prototype above for str_word_wrap function and details in my original comment concerning the approach to wrapping the buffer by simply using a sliding-window of width length to work down your buffer, outputting the words that fit within the sliding window each time it is moved down the buffer.
To accomplish the task you generally use three pointers (I name then p the pointer to the current char, sp the start pointer for the window, and ep the end pointer for the window. The scheme is this, you begin with all three initialized to the beginning of your buffer, you then iterate over each char with p until p points to a space between the words, setting the end-pointer ep = p; each time a space is encountered. On each iteration you check if p - sp >= width, where p - sp is simply the current pointer address minus the start pointer address which tells you how many characters you have moved from the start. If that equals or exceeds your width, you know you last set ep (your end-pointer) to the last whitespace in the windows marking the end of the last word to output.
All that remains is outputting the line up to the end-pointer, (and a '\n') and then setting your new start-pointer to the next character after end-pointer and you can set your end-pointer to one after the current pointer (which slides your window forward) and you repeat. Nothing fancy is needed. Something like the following works fine:
void str_word_wrap (char *buf, int n)
{
char *p = buf, /* pointer to current char */
*sp = buf, /* pointer to start of line to print */
*ep = buf; /* pointer to end of line to print */
for (; *p && *p != '\n'; p++) { /* loop over each char (omit '\n')*/
if (*p == ' ') /* if space, set ep */
ep = p;
if (p - sp >= n) { /* if wrap length 'n' reached */
while (sp < ep) /* loop outputting chars sp -> ep */
putchar (*sp++);
putchar ('\n'); /* tidy up with '\n' */
sp = ++ep; /* set start to next after end */
ep = ++p; /* set end to next after current */
}
}
while (*sp && *sp != '\n') /* output last line of chars */
putchar (*sp++);
putchar ('\n'); /* tidy up with final '\n' */
}
Putting that altogether will handle your case, exactly:
Example Input File
$ cat dat/taggedparagraph.txt
?width 30
While there are enough characters here to
fill
at least one line, there is
plenty
of
white space that needs to be
eliminated
from the original
text file.
Now simply running the program using the file as input or redirecting the file to the programs stdin gives:
Example Use/Output
$ ./bin/wrapped_tagged_p < dat/taggedparagraph.txt
Wrapping file at width: 30
While there are enough
characters here to fill at
least one line, there is
plenty of white space that
needs to be eliminated from
the original text file.
Look things over and let me know if you have questions. This all boils down to basic pointer arithmetic and keeping track of where you are within a buffer while iterating over each character in the buffer to extract whatever specific information you need from it. You will often hear that referred to as "walking-a-pointer" over or down the buffer. Using a sliding-window is nothing more than walking-a-pointer while keeping track of the fixed point you started from and limiting the walk to no more than some fixed width of characters, and doing whatever it is you need to over-and-over again until you reach the end.
Help "Learn About Pointers"
Since in your comment below your question you mentioned you were "going to learn about pointers", start with the basics:
A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found. Where you normally think of a variable holding an immediate values, such as int a = 5;, a pointer would simply hold the address where 5 is stored in memory, e.g. int *b = &a;.
To reference the value at the address held by a pointer you dereference the pointer by using the unary '*' character before the pointer name. E.g., b holds the address of a (e.g. b point to a), so to get the value at the address held by b, you simply dereference b, e.g. *b.
It works the same way regardless what type of object the pointer points to. It is able to work that way because the type of the pointer controls the pointer arithmetic, e.g. with a char * pointer, pointer+1 point to the next byte, for an int * pointer (normal 4-byte integer), pointer+1 will point to the next int at an offset 4-bytes after pointer. (so a pointer, is just a pointer.... where arithmetic is automatically handled by the type)
When you are dealing with strings in C, you can iterate from the beginning to the end of the string checking each character and stopping when you reach the nul-terminating character at the end of every string. This nul-character serves as a sentinel for the end of the string. You will see it represented as '\0' or just plain 0. Both are equivalent. The ASCII character '\0' has the integer value 0.
A simple example of walking a pointer may help cement the concept:
#include <stdio.h>
int main (void) {
char buf[] = "walk-a-pointer down buf", /* buffer */
*p = buf; /* initialize p to point to buffer */
/* dereference the pointer to get the character at that address */
while (*p) { /* while *p != 0, or (*p != '\0') */
putchar (*p); /* output each character */
p++; /* advance pointer to next char */
}
putchar ('\n'); /* then tidy up with a newline */
return 0;
}
Example Use/Output
$ ./bin/walkpointer
walk-a-pointer down buf

Resources