Hello I'm writing a program right now that read in values from files.
It reads in the values as strings by fgets
and then I wanted to change one of the strings into integers:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#define FILSIZE 1024
typedef struct _node
{
unsigned int uid;
char *uname;
struct _node *next;
} *node;
int main(int argc, char* argv[])
{
FILE *pFile; // pointer file
char currentLine[FILSIZE];
bool isListEmpty = true;
node firstNode = NULL;
printf( "\n\nargc: %d\nargv[0]: %s\nargv[1]: %s\n\n", argc, argv[0], argv[1] );
if(argc == 1)
{
pFile = stdin;
}
else if (argc == 2)
{
char buffer [50];
sprintf(buffer, "%s.txt" ,argv[1]);
printf("%s", buffer);
pFile = fopen(buffer, "r");
}
if(pFile == NULL )
{
printf("Not working");
exit(0);
}
int i=1;
int nameCounter = 1;
int colonCounter=0;
while(!feof( pFile ) && fgets(currentLine, sizeof(currentLine), pFile))
{
unsigned int nameLength;
unsigned int tempuid;
unsigned int idstorlek;
node firstNode = malloc(sizeof(struct _node));
char *temporaryString;
temporaryString = strtok(currentLine, ":");
colonCounter=colonCounter+1;
//printf(" fungera pls");
printf(" %d Namnordning %s \n", nameCounter, temporaryString);
nameLength = strlen(temporaryString);
firstNode->uname = malloc((nameLength+1) * sizeof(char));
strcpy(firstNode->uname, temporaryString);
printf("NAMNET: %s \n", temporaryString);
while(temporaryString != NULL)
{
printf(" %s \n", temporaryString);
if(colonCounter == 3)
{
int tempuid=atoi(temporaryString);
// idstorlek = sizeof(tempuid);
// firstNode->uid = malloc(4);
printf(" IDN: %d \n", tempuid);
firstNode->uid = tempuid;
printf("firstNodeid %d", firstNode->uid);
}
temporaryString = strtok(NULL, ":");
colonCounter=colonCounter+1;
}
if(colonCounter == 6)
{
// printf("FUNGERAR ID: %d NAMN %s \n", tempuid, firstNode->uname);
}
printf("%d Row is done \n", i);
i=i+1;
nameCounter = nameCounter+1;
colonCounter = 0;
}
}
But when I write it out I get:
1 Namnordning mr
NAMNET: mr
mr
x
1171
IDN: 1171
firstNodeid 1171 1101
Mikael R�nnar
/Home/staff/mr
/usr/local/bin/tcsh
1 Row is done
2 Namnordning axelsson
NAMNET: axelsson
axelsson
x
12856
IDN: 12856
firstNodeid 12856 1101
Bj�rn Axelsson
/Home/staff/axelsson
/usr/local/bin/tcsh
2 Row is done
3 Namnordning gabriel
NAMNET: gabriel
gabriel
x
16928
IDN: 16928
firstNodeid 16928 1101
Gabriel Jonsson
/Home/staff/gabriel
/usr/local/bin/tcsh
3 Row is done
4 Namnordning set
NAMNET: set
set
x
12037
IDN: 12037
firstNodeid 12037 1101
Set Norman
/Home/staff/set
/usr/local/bin/tcsh
4 Row is done
5 Namnordning dahlin
NAMNET: dahlin
dahlin
x
12928
IDN: 12928
firstNodeid 12928 1101
Fredrik Dahlin
/Home/staff/dahlin
/usr/local/bin/tcsh
5 Row is done
6 Namnordning fahlgren
NAMNET: fahlgren
fahlgren
x
17847
IDN: 17847
firstNodeid 17847 1101
Daniel Fahlgren
/Home/staff/fahlgren
/usr/local/bin/tcsh
6 Row is done
Why do I get 1101 too there?
The other tempuid part only gives me the correct id.
Am I not locating the memory right? (I been trying to and it only gives me weird errors, with the idsize part).
It's because you're not flushing the print buffer after you call
printf("firstNodeid %d", firstNode->uid);
printf stores a buffer of characters rather than writing a string to stdout as soon as it gets them to avoid the overhead of calling the lower-level write function too often.
Instead calling:
printf("firstNodeid %d\n", firstNode->uid);
Should fix your problem because "\n" will add a newline to the string, and flush the output.
Edit: You should also be careful with types. You're defining tempuid as an int:
int tempuid=atoi(temporaryString);
// idstorlek = sizeof(tempuid);
// firstNode->uid = malloc(4);
printf(" IDN: %d \n", tempuid);
firstNode->uid = tempuid;
printf("firstNodeid %d", firstNode->uid);
But defining it as an unsigned int in your node struct.
Normally you should post a small, complete example demonstrating the behavior (you'll often find the bug yourself when doing this).
But it turns out that you have posted enough information to give a pretty clear indicator: your code is not printing out an extra number. Instead, it's merely failing to print a newline; the next piece of code that does output will start writing on the same line.
Related
So there's a file I'm using fscanf() in. I've set a condition in my code that when (fscanf(...) == EOF, the program needs to break out of the function I'm currently in. The thing is, this condition is never satisfied in any of the cases where there's no more lines of text in the file. EOF is always -1, whereas fscanf(...) returns 4 each time there's a line of code, and 3 when there's nothing left for it to search through (instead of -1). If I add a line of code similar to the other ones, I will simply get one more instance of fscanf() returning 4, and then again, it'll give me a 3.
What could possibly be the issue? Thank you in advance!
Sample text file content:
CHRISTOU GIORGOS,140,VAS. OLGAS 112
MALAKOU MALAKOS,150,DRAS. BAGAS 12
TSIKOU GIJRAN,140,JABS. DRALGAS 1
TSIKOU BIRBAN,140,JABS. DRALGAS 1
DELHDHMHTRIOU SPYROS,50,SPEED. BAGAS 62
FOX SIN,40,BAN. NINJA 1
Code:
#include <stdio.h>
#define M 100
typedef struct {
char name[30];
int apousies;
} studentT;
void readInput (FILE* infile, studentT students[], int *pApousies, int *pStudents);
int main()
{
char inputfilename[30];
FILE* infile;
while (1) {
printf("Input file name :");
gets(inputfilename);
infile = fopen(inputfilename, "r");
if (infile != NULL) break;
printf("Cannot open input file %s. Try again.\n", inputfilename);
}
studentT students[M];
int numberOfStudents = 0, numberOfApousies = 0;
readInput(infile, students, &numberOfApousies, &numberOfStudents);
fclose(infile);
return 0;
}
void readInput (FILE* infile, studentT students[], int *pApousies, int *pStudents)
{
int nscan, apousies, studcount, apouscount, line;
char name[30], comments[68], termch;
line = 0;
while (1)
{
nscan = fscanf(infile, "%30[^,], %d, %68[^\n]%c", name, &apousies, comments, &termch);
/* printf("onoma: %s apousies: %d sxolia: %s terma: %c\n", name, apousies, comments, termch);
printf("%d\n", nscan);
printf("%d\n", EOF);*/
if (nscan == EOF) break;
line++;
if (nscan != 4 || termch != '\n')
{
printf("Error in line %d. Program termination\n", line);
exit(1);
}
}
}
fscanf returns 3 instead of -1 (EOF) at the end of the file
Because the last line lacks a '\n'.
OP's code "works" with the "tmp.txt" the below code makes.
fscanf() is hard to use right. Easier to code and debug with fgets(). Discussion follows.
"%30[^,]" allows too much for char name[30]. Use char name[30+1] or "%29[^,]"
OP's approach can readily fail with seemingly minor parsing problems such as a missing '\n' on the last line. After such a failure, recovery is extraordinary difficult with fscanf()
Debug: Importantly, the below print should not be attempted until code insures nscan >= 4
if (nscan >= 4) // add
printf("onoma: %s apousies: %d sxolia: %s terma: %c\n", name, apousies, comments, termch);
Instead, use fgets(). With line orientated data, this really is the best first step.
fscanf() is challenging to use and cope with errors. Far simpler to read a line with fgets() and then parse.
Using " %n" is a nice way to detect if all the line parsed.
#include <stdio.h>
#include <stdlib.h>
#define M 100
typedef struct {
char name[30];
int apousies;
} studentT;
void readInput(FILE* infile, studentT students[], int *pApousies,
int *pStudents) {
(void) students;
(void) pApousies;
(void) pStudents;
int line = 0;
char buf[200];
while (fgets(buf, sizeof buf, infile)) {
int apousies;
char name[30], comments[68];
int n = 0;
line++;
sscanf(buf, " %29[^,],%d , %67[^\n] %n", name, &apousies, comments, &n);
if (n == 0 || buf[n]) {
fprintf(stderr, "Error in line %d <%s>. Program termination\n", line, buf);
exit(1);
}
printf("Success %d <%s> %d <%s>\n", line, name, apousies, comments);
}
}
Sample use
int main() {
FILE *f = fopen("tmp.txt", "w");
fputs("CHRISTOU GIORGOS,140,VAS. OLGAS 112\n"
"MALAKOU MALAKOS,150,DRAS. BAGAS 12\n"
"TSIKOU GIJRAN,140,JABS. DRALGAS 1\n"
"TSIKOU BIRBAN,140,JABS. DRALGAS 1\n"
"DELHDHMHTRIOU SPYROS,50,SPEED. BAGAS 62\n"
"FOX SIN,40,BAN. NINJA 1\n", f);
fclose(f);
f = fopen("tmp.txt", "r");
studentT st[M];
readInput(f, st, NULL, NULL);
fclose(f);
}
Output
Success 1 <CHRISTOU GIORGOS> 140 <VAS. OLGAS 112>
Success 2 <MALAKOU MALAKOS> 150 <DRAS. BAGAS 12>
Success 3 <TSIKOU GIJRAN> 140 <JABS. DRALGAS 1>
Success 4 <TSIKOU BIRBAN> 140 <JABS. DRALGAS 1>
Success 5 <DELHDHMHTRIOU SPYROS> 50 <SPEED. BAGAS 62>
Success 6 <FOX SIN> 40 <BAN. NINJA 1>
This is part of a project that i'm doing. Essentially i want to read a text file called "Circuit" that has this inside:
Circuit title example
V1 1 0 24
V2 3 0 15
R1 1 2 10000
R2 2 3 8100
R3 2 0 4700
To give you some context, the values represent a circuit like this:
Circuit example
I made a code to save all these values in a structure and printed them out to see if they were being saved correctly.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct
{
char type, name, noMaior, noMenor;
int value;
}line;
int main(void)
{
line ramo[10];
FILE *ficheiro;
int i = 0, j;
char titulo[200];
if ((ficheiro = fopen("circuit.txt", "r")) == NULL)
printf("Error opening file!");
fgets(titulo, 199, ficheiro);
printf("%s", titulo);
while ((fscanf(ficheiro, "%c%c %c %c %d\n", &ramo[i].type, &ramo[i].name, &ramo[i].noMaior, &ramo[i].noMenor, &ramo[i].value)) != EOF)
{
i++;
//if (fgetc(ficheiro)=='.')
// break;
}
fclose(ficheiro);
for (j = 0; j < i; j++)
printf("%c%c %c %c %d\n", ramo[j].type, ramo[j].name, ramo[j].noMaior, ramo[j].noMenor, ramo[j].value);
return 0;
}
It outputs the same text that is in the file, which is what i intended. Now here comes the tricky part, we have to put ".end" or ".END" in the end of our file, so i made those two commented lines to scan the file for a dot and stop reading it if it encounters one but it causes me some problems when saving the values to the structure. This is what i get as an output:
Circuit title example
V1 1 0 24
2 3 0 15
1 1 2 10000
2 2 3 8100
3 2 0 4700
The "break" works as intended, because if i put a dot in the middle of the file it will stop reading whatever comes after but sadly it is ignoring the first letter and according to the debug tool it is saving a ' ' (a space) in the place of the letters (ramo[].type). I tried to learn as much as i could about the behaviours of fscanf and fgetc but i can't draw any conclusion as to why this is happening.
PS: tried to translate some of the variables to make it easier to read but some are still in portuguese, like "ficheiro"=file. Also take it easy on me folks, i just started learning coding!
Use fgets to read each line from the file. Then you can check for a dot or use strncmp or strcmp to test for specific values. If the value is not detected, parse the line using sscanf.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct
{
char type, name, noMaior, noMenor;
int value;
}line;
int main(void)
{
line ramo[10];
FILE *ficheiro;
int i = 0, j;
char titulo[200];
char input[200];
if ((ficheiro = fopen("circuit.txt", "r")) == NULL)
printf("Error opening file!");
fgets(titulo, sizeof titulo, ficheiro);
printf("%s", titulo);
while ( fgets ( input, sizeof input, ficheiro))
{
if ( '.' == input[0]) {//test for a dot at start of line
break;
}
else {
if (( 5 == sscanf(input, "%c%c %c %c %d"
, &ramo[i].type, &ramo[i].name, &ramo[i].noMaior, &ramo[i].noMenor, &ramo[i].value))) {
i++;
}
}
}
fclose(ficheiro);
for (j = 0; j < i; j++)
printf("%c%c %c %c %d\n", ramo[j].type, ramo[j].name, ramo[j].noMaior, ramo[j].noMenor, ramo[j].value);
return 0;
}
***UPDATED WITH NEW CODE
I'm trying to load a file data with an unknown amount of lines of data.
The data is organized as last, first:score1:score2:score3:score5:lettergrade
I'm getting the file to (sort of) load into an array and print.
When the data prints, it doesn't print the first name, just the first. It's printing the same integer for evert data element except for the last one, that comes out at 0 for all. Also, it's not flushing the data that doesn't return 8 elements.
PLEASE help!
Thanks in advance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define FFLUSH while(fgetsc(fp) != '\n')
#define MAX_STUDENTS 45
typedef struct aStudent
{
char name[26];
int id[7];
int test1[4];
int test2[4];
int proj1[4];
int proj2[4];
int proj3[4];
char grade[4];
} aStudent;
int main(void)
{
char file_name[FILENAME_MAX];
FILE* fp;
int i;
int rc;
int dc;
aStudent studentArray[MAX_STUDENTS];
printf("Enter File Name: ");
rc = scanf("%s", &file_name);
if (rc == 0)
{
printf("\n\nError: No file name entered.");
exit(0);
}
fp = fopen(file_name, "r");
if (fp == NULL)
{
printf("Error: Could not open file %s for read", file_name);
exit(0);
}
for(int i=0; i< 45; i++)
{
dc = (fscanf(fp, "%s[^:]%d[^:]%d[^:]%d[^:]%d[^:]%d[^:]%d[^:]%f[^:]%c[^:\n]", studentArray[i].name, studentArray[i].id, studentArray[i].test1, studentArray[i].test2, studentArray[i].proj1, studentArray[i].proj2, studentArray[i].proj3, studentArray[i].grade));
if (dc != 8)
{
FFLUSH;
}
printf("%s %d %d %d %d %d %d %.2f %c", studentArray[i].name, studentArray[i].id, studentArray[i].test1, studentArray[i].test2, studentArray[i].proj1, studentArray[i].proj2, studentArray[i].proj3, studentArray[i].grade);
}
return 0;
}
STUDENT is a type. You cannot fgets into a type. There is no array STUDENT. Try this;
typedef struct aStudent
{
char name[26];
int id[7];
int test1[4];
int test2[4];
int proj1[4];
int proj2[4];
int proj3[4];
int ave[4];
char grade[4];
} aStudent;
and;
aStudent studentArray[MAX_STUDENTS];
and replace gGets with fscanf.
It appears you have your struct set up incorrectly to handle the data you want to read from your file. Your data was:
name:score1:score2:score3:score5:lettergrade
A struct that makes sense (given the multiple scores) would be:
typedef struct {
char name[MAXNM];
int id;
int scores[NGRDS];
float ave;
char grade;
} student;
(you have never accounted for where the id comes from and I'll leave it to use to split name into first, last, as your original question specified name and your recent edits added first, last)
What is clear is you want to read your datafile into a struct and compute the ave (average) from the grades read (which should be a floating-point type instead of int). Given your file format, reading each line of data with the scanf family functions is probably a bit more sensible than reading/tokenizing each line with fgets/strtok.
Your mixing of fgets and fscanf syntax will not work and exemplifies the difficulty in trying to shoehorn mixed data into a scanf type format string. You must take care in crafting the string to handle whitepace and any trailing newlines left in the input buffer. For your set of data, the following will work as well as anything else:
char *fmt = " %[^:]:%d:%d:%d:%d:%c";
Another way to account for the trailing newline is to use the assignment suppression operator to read and discard the trailing newline. When used, the discarded conversion is not added to the returned match count. e.g.
char *fmt = "%[^:]:%d:%d:%d:%d:%c%*c";
There is no need for macros to flush the input buffer if you craft your format string correctly.
Putting the pieces together, and leaving your belated change from name to last, first to you, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* constants for:
NGRDS - max number of grades per student.
MAXNM - max length of student name.
MAXSTD - max number of students in array.
MAXFN - max filename length.
*/
enum { NGRDS = 4, MAXNM = 26, MAXSTD = 45, MAXFN = 128 };
typedef struct {
char name[MAXNM];
int id;
int scores[NGRDS];
float ave;
char grade;
} student;
int main (void) {
char filename[MAXFN]; /* initialize variables */
char *fmt = " %[^:]:%d:%d:%d:%d:%c";
FILE* fp = NULL;
size_t i = 0, nstudents = 0;
student students[MAXSTD] = {{ {""}, 0, {0}, 0.0, 0 }};
printf ("\nEnter File Name: ");
if (scanf("%s", filename) != 1) { /* validate filename read */
fprintf (stderr, "error: invalid file name entered.\n");
return 1;
}
if (!(fp = fopen (filename, "r"))) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'\n.", filename);
return 1;
}
/* read each line in file */
while (fscanf (fp, fmt, students[i].name, &students[i].scores[0],
&students[i].scores[1],&students[i].scores[2],
&students[i].scores[3], &students[i].grade) == 6) {
int j; /* compute sum/average */
for (j = 0; j < NGRDS; j++) students[i].ave += students[i].scores[j];
students[i].ave /= (float)NGRDS;
if (++i == MAXSTD) { /* check against max */
fprintf (stderr, "warning: students array full.\n");
break;
}
}
fclose (fp); /* close file */
nstudents = i; /* save number of students */
for (i = 0; i < nstudents; i++) { /* output student data */
size_t j;
printf ("\n Student[%2zu]\n name : %s\n scores :",
i, students[i].name);
for (j = 0; j < NGRDS; j++) printf (" %d", students[i].scores[j]);
printf ("\n average: %.2f\n grade : %c\n",
students[i].ave, students[i].grade);
}
return 0;
}
Example Data
$cat dat/studentgrades.txt
Joe Cool:80:77:63:88:c
Nancy Smart:96:93:97:99:a
Dwane Dumb:61:60:67:50:f
Al Smith:75:77:78:73:c
Example Use/Output
$ ./bin/studentstruct
Enter File Name: dat/studentgrades.txt
Student[ 0]
name : Joe Cool
scores : 80 77 63 88
average: 77.00
grade : c
Student[ 1]
name : Nancy Smart
scores : 96 93 97 99
average: 96.25
grade : a
Student[ 2]
name : Dwane Dumb
scores : 61 60 67 50
average: 59.50
grade : f
Student[ 3]
name : Al Smith
scores : 75 77 78 73
average: 75.75
grade : c
(note: your letter grade should be computed based on the average, not simply read from the file -- what if the grades change? Also note that your input file format is fixed and inflexible. If the number of fields or their format changes in any way, you must account for the change in your fmt format string.)
Look it over and let me know if you have any questions.
Trying to create a program that takes in a text file and reads it line by line. It then finds the two integers that are on each line and adds them together. It then outputs the new line with the original string and total to a new text file. I need help adding the two integers, getting them from each line, and then putting the new line to a text file.
input text file
good morning hello 34 127
ann 20 45
10 11
fun program and you find the same 90 120
news paper said that 56 11
how do you like 20 5
line number 90 34
Outputs first like would look like: and then continue on
good morning hello 161
Code:
int processTextFile(char * inputFileName, char * outputFileName)
{
FILE *fp = fopen(inputFileName, "r");//open file to to read
char buff[1024];
char *p, *p1;
int num;
while (fgets(buff, 1024, fp)!=NULL)
{
printf("%s\n", buff);
while(scanf(buff, "%*[^0-9]%d", &num)== 1)
printf("%d\n", num);
//fscanf(fp, "%s", buff);
}
return 0;
}
EDIT!!!!::
So now that I've been able to accomplish this. How would I sort it by the number produced? for example:
Time is money 52
here I am 3
21
Would output to a new text file in order like
here I am 3
21
Time is money 52
My version using strcspn() is supposed to work with stdin for input and stdout for output. (so you can do executable <textfile >newtextfile)
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char line[1000];
while (fgets(line, sizeof line, stdin)) {
char *ptr;
size_t x = strcspn(line, "0123456789");
if (line[x]) {
errno = 0;
int n1 = strtol(line + x, &ptr, 10);
if (*ptr && !errno) {
errno = 0;
int n2 = strtol(ptr, &ptr, 10);
if (*ptr && !errno) {
int n3 = n1 + n2;
printf("%.*s%d\n", (int)x, line, n3);
} else {
printf("%s", line); // line includes ENTER
}
} else {
printf("%s", line); // line includes ENTER
}
} else {
printf("%s", line); // line includes ENTER
}
}
return 0;
}
The same version without the error checking
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char line[1000];
while (fgets(line, sizeof line, stdin)) {
char *ptr;
size_t x = strcspn(line, "0123456789");
int n1 = strtol(line + x, &ptr, 10);
int n2 = strtol(ptr, &ptr, 10);
int n3 = n1 + n2;
printf("%.*s%d\n", (int)x, line, n3);
}
return 0;
}
The approach should be:
open two files, one for input, one for output.
use sscanf() to read the input buffer.
scan the leading string, and then two number.
if previous sscanf() fails, only check for two number.
if either of the above scanning is success, print the sum to the output file.
A sample code, should look like
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE *fpin = fopen("ipfile", "r");//open file to to read
if (!fpin)
{
printf("Error in ipfile opening\n");
exit (-1);
}
FILE *fpout = fopen("opfile", "w");//open file to to write
if (!fpout)
{
printf("Error in opfile opening\n");
exit (-1);
}
char buff[1024] = {0};
char str[1024] = {0};
int num1 =0, num2= 0;
while (fgets(buff, 1024, fpin)!=NULL)
{
memset(str, 0, sizeof(str));
//printf("%s\n", buff);
if(sscanf(buff, "%[^0-9]%d %d", str, &num1, &num2)== 3)
fprintf(fpout, "%s %d\n", str, (num1+num2));
else if (sscanf(buff, "%d %d", &num1, &num2)== 2)
fprintf(fpout, "%d\n", (num1+num2));
}
return 0;
}
Note:
The above procedure, is a kind of workaround. If the data pattern in the file changes, lot of changes will be required to maintain a code like that. Instead of usinf sscnaf(), for a better and roubust approach, you should
read the line from file
start tokenizing the input buffer (strtok()) and check for ints as tokens (strtol()).
save the returned tokens and ints seperately.
once the strtok() returns NULL, you print the string tokens and the sum of the ints to the o/p file.
I intend not to change your code completely, so I just added some snippets for improvement.
int processTextFile(char *inputFileName, char *outputFileName) {
FILE *fp = fopen(inputFileName, "r");
FILE *out = fopen(outputFileName, "w");
char line[1024];
if (!fp) {
perror(inputFileName);
return;
}
while (fgets(line, sizeof(line), fp) != NULL) {
int num1 = 0, num2 = 0;
char textPart[1024] = "";
if ( !sscanf(line, "%[a-zA-Z' ']%d%d", textPart, &num1, &num2) {
sscanf(line, "%d%d", &num1, &num2);
}
fprintf(out, "%s %d\n", textPart, num1 + num2);
}
fclose(fp);
fclose(out);
}
Explanation:
I scanned the text file, extracted the text part and the two ints. Since I noticed that the ints are placed at the end of each line, I just used sscanf() for that matter.
sscanf(line, "%[a-zA-Z' ']%d%d", textPart, &num1, &num2);
Here, "%[a-zA-Z' ']%d%d" format specifiers means to get only alphabets and spaces.
Since it will only get letters and spaces, the line "10 11" in your input file won't be put to num1 and num2. Because the code inspects first for a string containing letters and spaces. Since 10 and 11 are not of the qualified types, then the line is just skipped.
That's why I added an if-else statement, which checks if sscanf wrote anything to memory. If sscanf returned 0, then it means that no text part is present. Just digits. So the program will scan the two digits.
if ( !sscanf(line, "%[a-zA-Z' ']%d%d", textPart, &num1, &num2) ) {
sscanf(line, "%d%d", &num1, &num2);
}
I also added file checking for input file. It checks if file doesn't exist or can't be opened by the filestream.
if (!fp) {
perror(inputFileName);
return;
}
Here is the content of output file after execution:
good morning hello 161
ann 65
21
fun program and you find the same 210
news paper said that 67
how do you like 25
line number 124
I'm trying to create a hexdump like xxd but there are some differences that I'm trying to resolve. Currently the program processes 10 characters per line as seen on the utmost right column vs 16 in xxd. It also only shows 1 octet per column instead of pairs of 2 octet's.
xxd
0000000: 2369 6e63 6c75 6465 203c 7374 6469 6f2e #include <stdio.
my output
0: 23 69 6E 63 6C 75 64 65 20 3C #include <
EDIT:
To add some clarification, I am trying to achieve two things.
1) I would like this program to output exactly like xxd. For this it needs to output 32 Hex numbers (8x columns of 4).
2) I would also like the program to list the hex numbers in row's columns of 4 like in xxd.
I've tried to edit the "10" in the source below to something like "12" but it creates errors in the output, it seems to be a magic number.
source:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#define BYTE_OFFSET_INIT 8
#define CHAR_OFFSET_INT 39
#define LINE_LENGTH 50
static void print_e(int e, char *program, char *file)
{
fprintf(stderr, "%s: %s: %s\n", program, file, strerror(e));
}
static void print_line(char *line)
{
int i;
/* sprintf leaves terminators mid-line, so clear them out so we can print the full line */
for (i = BYTE_OFFSET_INIT; i < CHAR_OFFSET_INT; i++)
if (line[i] == '\0')
line[i] = ' ';
printf("%s\n", line);
}
int main(int argc, char *argv[])
{
char line[LINE_LENGTH + 1];
int ch;
int character = 0;
int line_offset = 0;
int byte_offset = BYTE_OFFSET_INIT, char_offset = CHAR_OFFSET_INT;
if (argc != 2) {
fprintf(stderr, "Usage: %s [file]\n", argv[0]);
exit(EXIT_FAILURE);
}
FILE *fp = fopen(argv[1], "rb");
if (!fp) {
print_e(errno, argv[0], argv[1]);
exit(EXIT_FAILURE);
}
printf("Offset Bytes Characters\n");
printf("------ ----------------------------- ----------\n");
while ((ch = fgetc(fp)) != EOF) {
if (character == 0) {
sprintf(line, "%6d ", line_offset);
line_offset += 10;
}
sprintf(line + byte_offset, "%02X ", ch);
sprintf(line + char_offset, "%c", isprint(ch) ? ch : '.');
character++;
char_offset++;
byte_offset += 3;
if (character == 10) {
print_line(line);
character = 0;
char_offset = CHAR_OFFSET_INT;
byte_offset = BYTE_OFFSET_INIT;
}
}
if (ferror(fp)) {
print_e(errno, argv[0], argv[1]);
exit(EXIT_FAILURE);
}
if (character > 0)
print_line(line);
if (fclose(fp) == EOF) {
print_e(errno, argv[0], argv[1]);
exit(EXIT_FAILURE);
}
return 0;
}
While it's possible to scan one byte at a time and write it into the output string at the correct position, it is by no means necessary. It is way easier to read DISPLAY_LENGTH bytes at once and loop over the thus read number of bytes twice; first outputting the hex representation, then again for the ASCII characters. The only (minor) caveat is what to do at the end of the file; but since fread returns the number of characters, you can just keep on counting and output spaces for as long as necessary to fill the hex line.
This leads to the following program. DISPLAY_LENGTH is the total number of bytes to display per line, GROUP_BYTES is the number of single bytes in each hexadecimal group (setting it to 1 will display a 'regular' spaced hex output, 2 will group as in your xxd example, and higher values should also work).
I had some fun figuring out the magic formulae to correctly center the text Bytes and calculating how many dashes to display for the separator. The rest is very straightforward.
I don't know what xxd output looks like, apart from your one-line example, so I use stat to read out the length of the file in advance (with an added opportunity to display an error for "not a file" -- try for example with a folder) and display the correct number of dashes and spaces to line up the line counter. I set this value to a minimum of 6 so there is always room for the text Offset.
If your compiler is not a modern one, it may complain about the %zu format string. If so, use %lu; you may also need to change all occurrences of size_t to unsigned long.
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#define DISPLAY_LENGTH 21
#define GROUP_BYTES 2
static void print_e(int e, char *program, char *file)
{
fprintf(stderr, "%s: %s: %s\n", program, file, strerror(e));
}
int main(int argc, char *argv[])
{
size_t i;
struct stat fs;
int n_digit;
unsigned char read_buf[DISPLAY_LENGTH];
size_t bytes_read, cpos = 0;
if (argc != 2)
{
fprintf(stderr, "Usage: %s [file]\n", argv[0]);
exit(EXIT_FAILURE);
}
FILE *fp = fopen(argv[1], "rb");
if (!fp)
{
print_e (errno, argv[0], argv[1]);
exit(EXIT_FAILURE);
}
if (stat(argv[1], &fs) == -1)
{
print_e (errno, argv[0], argv[1]);
exit(EXIT_FAILURE);
}
if ((fs.st_mode & S_IFMT) != S_IFREG) /* regular file */
{
fprintf(stderr, "Not a regular file: %s\n", argv[1]);
exit(EXIT_FAILURE);
}
n_digit = 0;
while (fs.st_size > 0)
{
fs.st_size /= 10;
n_digit++;
}
if (n_digit < 6)
n_digit = 6;
printf("%*s ", n_digit, "Offset");
printf("%*s%-*s", ((2*DISPLAY_LENGTH+(DISPLAY_LENGTH+GROUP_BYTES-1)/GROUP_BYTES)+2)/2, "Bytes", ((2*DISPLAY_LENGTH+(DISPLAY_LENGTH+GROUP_BYTES-1)/GROUP_BYTES)+2-5)/2, "");
printf (" Characters\n");
for (i=0; i<n_digit; i++)
printf ("-");
printf(" ");
for (i=1; i<2*DISPLAY_LENGTH+(DISPLAY_LENGTH+GROUP_BYTES-1)/GROUP_BYTES; i++)
printf ("-");
printf (" ");
for (i=0; i<DISPLAY_LENGTH; i++)
printf ("-");
printf ("\n");
while ( (bytes_read = fread (read_buf, 1, DISPLAY_LENGTH, fp)))
{
printf ("%*zu ", n_digit, cpos);
for (i=0; i<bytes_read; i++)
{
if (!(i % GROUP_BYTES))
printf (" ");
printf ("%02X", read_buf[i]);
}
while (i < DISPLAY_LENGTH)
{
if (!(i % GROUP_BYTES))
printf (" ");
printf (" ");
i++;
}
printf (" ");
for (i=0; i<bytes_read; i++)
printf ("%c", isprint(read_buf[i]) ? read_buf[i] : '.');
printf ("\n");
cpos += bytes_read;
}
if (ferror(fp))
{
print_e (errno, argv[0], argv[1]);
exit(EXIT_FAILURE);
}
if (fclose(fp))
{
print_e (errno, argv[0], argv[1]);
exit(EXIT_FAILURE);
}
return 0;
}
Sample output, displaying its own compiled executable with a display length of 21 and grouped per 2 bytes:
Offset Bytes Characters
------ ---------------------------------------------------- ---------------------
0 CFFA EDFE 0700 0001 0300 0080 0200 0000 0D00 0000 70 ....................p
21 0600 0085 0020 0000 0000 0019 0000 0048 0000 005F 5F ..... .........H...__
42 5041 4745 5A45 524F 0000 0000 0000 0000 0000 0000 00 PAGEZERO.............
... (673 very similar lines omitted) ...
14196 7075 7473 005F 7374 6174 2449 4E4F 4445 3634 005F 73 puts._stat$INODE64._s
14217 7472 6572 726F 7200 6479 6C64 5F73 7475 625F 6269 6E trerror.dyld_stub_bin
14238 6465 7200 0000 der...