Im attempting the starts of a VM in C and i'm attempting to understand how to read out the binary file ive produced from the assembler. As per instructions of assignment we are to allocate a global memory as an unsigned byte of 1k memory space and and then using a load function are to read this binary file into the memory and then using a fetch function read these bytes into their instructions. The issue im having is part 1, how do i read this binary file into this unsigned int array and then decode it in a way that can be worked with? At the moment what i have prints out a value that isnt at all what is to be expected.
MAIN.c
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
unsigned int memory[1028];
int loads(char *filename){
FILE *file = fopen(filename, "r");
ssize_t read;
if(file == NULL){
exit(-1);
}
while(fread(&memory, sizeof(unsigned int), 1, file) == 1){
printf("%d\n", *memory);
}
fclose(file);
exit(0);
}
int main(int argc, char** argv){
if (argc <= 1){
printf("No file found\n");
return -1;
}
char *filename = argv[1];
loads(filename);
}
inputfile.txt
t#w## (this is what it shows which is unreadable but when using od -t x1 output.txt | head -5 it prints out to be 0000000 74 40 77 40 11 23
0000006)
Current Output
1081557108
Desired output
74 40 77 40 11 23
When reading with fread, fread reads bytes. It has no idea about any concept of a line. The byte '\n' (0xa) is just a byte like any other byte in the file. The declaration for fread is:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
Where ptr is the starting address for a block of memory sufficient to hold size * nmemb (size * no. of members) values from stream. Where size is the element or object size (in your case sizeof (unsigned) and nmemb is the number you will read (e.g. 1028 in the case of your unsigned memory[1028];) See man 3 fread
Where you have problems with your code is your use of &memory as the pointer. That is incorrect. (and why you get 2-values) &memory is type unsigned (*)[1028] (e.g. a pointer-to-array-of unsigned[1028]) What is the sizeof(unsigned)? (hint: 4-bytes) What is the sizeof (a_pointer)? (hint: 8-bytes on x86_64). So you are able to store 2-unsigned values in the storage of a pointer while invoking Undefined Behavior with the remainder of the read.
The correct parameter for fread is simply memory which, as an array, is converted to a pointer on access subject to the four exceptions listed in C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
In your case you declare an unsigned array memory as:
#define MAXU 1028 /* if you need a constant, #define one (or more) */
unsigned memory[MAXU];
(note: you want to avoid the use of global variables unless absolutely necessary. Instead, declare the array in the scope needed, e.g. in main() and then pass a pointer to any function where it is required)
When handling files, instead of passing a filename as a parameter to a function, instead, open the file and validate it is open in the caller (main() here), and pass an open FILE* pointer as a parameter. Unless the file can be opened, there is no need to make the function call and set up the function stack to begin with. So in main() you can do something similar to the following to pass the filename as the first argument to your program, e.g.
int main(int argc, char **argv) {
int n = 0;
if (argc < 2) { /* validate at least 1 argument given for filename */
fprintf (stderr, "usage: %s filename\n", argv[0]);
return 1; /* do NOT return negative values to the shell */
}
/* use filename provided as 1st argument */
FILE *fp = fopen (argv[1], "r");
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
(note: do NOT return a negative value to the shell. Instead return 1 or EXIT_FAILURE (which has the value 1) to indicate error)
Now you want to call your loads function passing the open file-stream fp, e.g.
if ((n = loads (fp)) == 0) { /* validate return of loads */
fputs ("error: loads() read zero bytes or error occurred.\n", stderr);
return 1;
}
Your loads() function reduces to:
int loads (FILE *fp)
{
return fread (memory, sizeof *memory, MAXU, fp);
}
(note: fread returns the number of members read, which is only equal to the number of bytes read when size == 1. So by choosing the size of unsigned it will return the number of unsigned values read).
A complete example could be:
#include <stdio.h>
#include <stdlib.h>
#define MAXU 1028 /* if you need a constant, #define one (or more) */
unsigned memory[MAXU];
int loads (FILE *fp)
{
return fread (memory, sizeof *memory, MAXU, fp);
}
int main(int argc, char **argv) {
int n = 0;
if (argc < 2) { /* validate at least 1 argument given for filename */
fprintf (stderr, "usage: %s filename\n", argv[0]);
return 1; /* do NOT return negative values to the shell */
}
/* use filename provided as 1st argument */
FILE *fp = fopen (argv[1], "r");
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if ((n = loads (fp)) == 0) { /* validate return of loads */
fputs ("error: loads() read zero bytes or error occurred.\n", stderr);
return 1;
}
for (int i = 0; i < n; i++) { /* loop n times outputting values */
if (i && i % 10 == 0) /* output 10 columns for convenience */
putchar ('\n');
printf (" %4x", memory[i] & 0xffff); /* & 0xffff is for my file */
}
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
In my file ../dat/100000int.bin I have 100,000 integer values (both positive and negative in the range of short, so I have masked the upper 2-bytes in each value with memory[i] & 0xffff to prevent the sign-extended output as an unsigned value, e.g. 0xffff7d77, when the stored values is less than zero.
$ ./bin/freadunsignedval ../dat/100000int.bin
7d77 6cad c544 21f8 723f 54d1 8a81 2c6a 1ba9 f95b
1858 7565 f4b 28e4 7fdd 5a92 b5df 7a3f 4e1a 7e19
669 f365 34c0 95e 903 689d 66f2 abf2 1223 1290
372f f9b 7f3d 71eb ce6d 717c 46bc 2712 1de6 6265
d248 363e 57cb 3d03 5f23 57a8 1795 2944 51e7 65af
275d 5851 724a 5c1e 61af 7b4d 44bb 48a2 4f5b 56de
5b32 68b 6679 5a6f 7876 180c 4beb 3f33 3f1f 69d1
2198 6cd7 200f 7963 29da 7f32 510b 4170 2877 22f3
271f 4fd4 84bc 196a 2bf2 5cf3 14b7 70ad 2595 6413
...
6503 b2 f135 15f6 776c b7f3 1ffd 1365 1e4d 129b
23f 6c3e 20c a8c 2ef6 f72b 4d4 793a 1b6b 425
79d5 6bac ba8 6527 6239 17ea 644e 1175 4464 1c88
346d 2967 1d3a 4339 3f5d 14a6 b46 5f5a
(which is 103 lines of output with 10-values per-line and 8 values in the final line)
Look things over and let me know if you have further questions.
Related
I have a text file that has 200 x 150 data in it (every number is separated with a space). I am trying to store these numbers into a matrix. My code is this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int i,j, matrix[200][150];
void getMatrix(){
FILE *file;
file = fopen("PICTURE.txt", "r");
char *line, *number;
j = 0;
while(!feof(file)){
i = 0;
fscanf(file, "%s", line);
number = strtok(NULL, " ");
while(number!= NULL){
matrix[i][j] = atoi(number);
}
printf("\n");
j++;
}
}
void printMatrix(){
int a, b;
for(a=0; a<i; a++){
for(b=0; b<j; b++){
printf("%d", matrix[a][b]);
}
printf("\n");
}
}
int main(){
getMatrix();
printMatrix();
}
It prints out literally nothing. I don't understand why. How can I fix my code or do you have any other suggestions for rewriting it?
if you have a file, with 200x150 integer values (either on 200 lines, or all values on one line), then simply looping though the file with fscanf() reading and storing an integer at a time is all that is needed. You must validate each read and provide a way to communicate any failure within a function through the function return (or though a pointer updated within the function)
To begin, do not use MagicNumbers in your code and do not hardcode filenames. Instead, #define the constants you need (or use a global enum) and pass the open FILE* pointer to your read-function as a parameter after having opened (and validated that the file is open for reading) in the calling function. (If the file open fails -- there is no reason to make the read-function call to begin with)
In your case, you only have two needed constants:
#define ROWS 200 /* if you need a constant, #define one (or more) */
#define COLS 150
Do not use global variables unless absolutely necessary (there will be virtually no case where globals are needed when you begin learning C). Passing the matrix (2D array) as a parameter to your read-function, it can be written as:
/* fill m[ROWS][COLS] with values read from fp.
* returns 1 on success, 0 otherwise.
*/
int getmatrix (int (*m)[COLS], FILE *fp)
{
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
if (fscanf (fp, "%d", &m[row][col]) != 1) {
fprintf (stderr, "error reading m[%d][%d]\n", row, col);
return 0;
}
}
}
return 1;
}
(note: how the return type for the function is int where 1 (true) is returned on success and 0 (false) is returned on failure. Also note that if 200x150 values are unable to be read, failure is returned)
Pass the filename to read from as the 1st argument to your program (that's what int argc, char **argv parameters to main() are for). Alternatively, you can prompt the user and read the filename as input. You can provide a fixed filename as a default value if no filename is entered (stdin is used as the default below), but otherwise do not hardcode filenames. You should not have to recompile your program simply to read from a different filename.
With those improvements made, your main() could be:
int main (int argc, char **argv) {
int matrix[ROWS][COLS] = {{0}}; /* do not use global variables */
/* 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 in caller */
perror ("file open failed");
return 1;
}
if (!getmatrix (matrix, fp)) { /* pass array and open FILE*, validate */
return 1;
}
fclose (fp);
prnmatrix (matrix); /* output results */
}
The complete program is:
#include <stdio.h>
#define ROWS 200 /* if you need a constant, #define one (or more) */
#define COLS 150
/* fill m[ROWS][COLS] with values read from fp.
* returns 1 on success, 0 otherwise.
*/
int getmatrix (int (*m)[COLS], FILE *fp)
{
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
if (fscanf (fp, "%d", &m[row][col]) != 1) {
fprintf (stderr, "error reading m[%d][%d]\n", row, col);
return 0;
}
}
}
return 1;
}
void prnmatrix (int (*m)[COLS])
{
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
printf (col ? " %4d" : "%4d", m[row][col]);
}
putchar ('\n');
}
}
int main (int argc, char **argv) {
int matrix[ROWS][COLS] = {{0}}; /* do not use global variables */
/* 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 in caller */
perror ("file open failed");
return 1;
}
if (!getmatrix (matrix, fp)) { /* pass array and open FILE*, validate */
return 1;
}
fclose (fp);
prnmatrix (matrix); /* output results */
}
Example Use/Output
With 200 x 150 random values less than 10000 generated and written to dat/int-200x150.txt, you would run the program as:
$ ./bin/read2darrint dat/int-200x150.txt
9240 5939 3063 5789 7401 7869 4363 3321 7788 1008 7850 ...
2760 5263 5338 6390 8146 155 324 916 4489 3718 1616 ...
...
Where the output is 200 lines of 150 numbers each.
There are many ways to do this. One (better) way would be to loop reading each line of integer values as a string with fgets() and converting each set of ASCII digits into an integer value with strtol(). I say "better" in a sense that strtol() provides far better error reporting in the case of a failed conversion that the simple pass/fail that can be obtained from fscanf().
The read before looping with strtol() would be with fgets() into a sufficiently sized buffer. That way if a matching-failure occurs, the error only impacts that line of data and no offending data is left in your input stream unread (as will occur with a matching-failure and fscanf().
That said, with a properly formatted space-separated file full of data, there is nothing wrong with using fscanf() directly, just be aware there are more robust ways to do the read and conversion.
To use strtol(), to work through each line of values, you simply use a pointer to the beginning of each set of digits calling strtol() which then updates the endptr to one past the last digit converted allowing you to work your way though each number by updating the pointer (nptr) to endptr after each validated conversion. See man 3 strtol. You could updated the code above with:
...
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
...
#define MAXC 2048 /* (150 int + space) max of 1800 chars per-line */
and finally change your function to use strtol(), e.g.
/* fill m[ROWS][COLS] with values read from fp.
* returns 1 on success, 0 otherwise.
*/
int getmatrix (int (*m)[COLS], FILE *fp)
{
char buf[MAXC]; /* buffer to hold each line */
for (int row = 0; row < ROWS; row++) {
char *nptr = buf, *endptr = nptr; /* nptr and endptr for strtol */
if (!fgets (buf, MAXC, fp)) { /* read line of input */
fprintf (stderr, "error: failed to read line %d\n", row);
return 0;
}
for (int col = 0; col < COLS; col++) {
errno = 0; /* reset errno */
long tmp = strtol (nptr, &endptr, 10); /* convert w/strtol */
if (nptr == endptr) { /* no ASCII digits coverted to a number */
fputs ("error: no digits converted.\n", stderr);
return 0;
}
else if (errno) { /* error in converstion, under/overflow */
fputs ("error: under/overflow occurred in conversion.\n", stderr);
return 0;
}
/* conversion outside range of int */
else if (tmp < INT_MIN || INT_MAX < tmp) {
fputs ("error: conversion out of range of integer.\n", stderr);
return 0;
}
m[row][col] = tmp; /* assign tmp to m[row][col] */
nptr = endptr; /* update nptr to endptr */
}
}
return 1;
}
If an error occurs, you will know exactly what caused the failure. Look things over and let me know if you have further questions.
Many problems:
Never use while(!feof(file)){. Instead check the return value of fscanf(file, ...)
Do not use "%s" with out a width limit.
Do not use pass line to a function without initializing/assigning it first.
i never incremented.
fclose(file) when done.
Check success of fopen(...);
GTG, maybe more later.
my text file is "Foo bar!!", name foo.txt
So i want to create a new variable in my main function and copy the text file into new variable.
#include <stdio.h>
#include <stdlib.h>
FILE *fopen_with_error (const char *f, const char *mode){
FILE *fp;
if((fp = fopen(f,mode)) == NULL){
printf("Error opening %s\n",f);
exit(EXIT_FAILURE);
}
return fp;
}
int main(int argc, char* argv[]){
FILE *fp;
int a, num;
if(argc != 2){
printf("Usage: program input file\n");
return 0;
}
fp = fopen_with_error(argv[1],"rb");
}
I want to store the copied text into 'a' variable.
I tried to use fread function but everytime i try, it fails somehow.
In addition to using stat to obtain the number of bytes in the file to accurately size your buffer for reading the file into a single variable, you can also use fseek and ftell to accomplish the same thing.
The bigger issue is that you post the contents of a text file with a single-line (e.g. "Foo bar!!") and then proceed to explain you want to read the entire file into a variable. For binary input, that makes more sense. For text, you generally want to read and store individual lines rather than a file-at-once.
(you can store an entire text file line-by-line by using a pointer to pointer to char and allocating pointer and then storage for each line)
Now, don't get me wrong, you can do either, but if you need specific information from each line, and the lines differ in length, etc.., it is a whole lot easier to iterate over pointers than it is trying to scan through one giant buffer of text picking out newlines. You can think through what you need to do with the data you read from the file and make the call.
As your question is written, the basic approach is simply to open the file in binary mode, fseek forward to the end of file, use ftell to report the number of bytes in the file, allocate/validate memory to hold the file, and then read the file using fread into the allocated block of memory. (note: mmap provides another option as well)
Putting that pieces together, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
char *read_file (char* fname, size_t *nbytes)
{
long bytes = 0;
char* file_content;
FILE *file = fopen(fname, "rb");
if (!file) /* validate file open for reading */
return NULL;
fseek (file, 0, SEEK_END); /* fseek end of file */
if ((bytes = ftell (file)) == -1) { /* get number of bytes */
fprintf (stderr, "error: unable to determine file length.\n");
return NULL;
}
fseek (file, 0, SEEK_SET); /* fseek beginning of file */
/* allocate memory for file */
if (!(file_content = malloc (bytes))) { /* allocate/validate memory */
perror ("malloc - virtual memory exhausted");
return NULL;
}
/* read all data into file in single call to fread */
if (fread (file_content, 1, (size_t)bytes, file) != (size_t)bytes) {
fprintf (stderr, "error: failed to read %ld-bytes from '%s'.\n",
bytes, fname);
return NULL;
}
fclose (file); /* close file */
*nbytes = (size_t)bytes; /* update nbytes making size avialable */
return file_content; /* return pointer to caller */
}
int main (int argc, char **argv) {
size_t nbytes;
char *content;
if (argc < 2) { /* validate required argument givent */
fprintf (stderr, "error: insufficient input. filename req'd.\n");
return 1;
}
if ((content = read_file (argv[1], &nbytes))) { /* read/validate */
printf ("read %zu bytes of data from %s\n"
"------content------\n%s\n-------------------\n",
nbytes, argv[1], content);
free (content);
}
return 0;
}
Example Input File
$ cat dat/foo.txt
"Foo bar!!"
Example Use/Output
$ ./bin/freadbinfoo dat/foo.txt
read 12 bytes of data from dat/foo.txt
------content------
"Foo bar!!"
-------------------
Don't forget to use a memory use & error checking program (like valgrind on Linux) to insure there are no memory errors and that you have freed all memory you have allocated.
Here's a simple example of how to do this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
int main(int argc, char **argv) {
char *fname = "./someFileNameHere";
struct stat st;
stat(fname, &st);
char *content = malloc(st.st_size+1);
content[st.st_size] = 0;
FILE *infile = fopen(fname, "rb");
size_t read = fread(content, sizeof(char), st.st_size, infile);
fclose(infile);
puts(content);
return 0;
}
I've left all of the necessary error handling to you.
My code pulls data from one text file and then totals the points and enters it into a separate text file, so I would like the program to organize the teams and scores by total points prior to frpintf into the text file. So far the program pulls and runs the data and totals it out and fprintf just fine, should I use qsort to sort and print into the text file, and where in my code do I place it. Here is the text it is reading.
Indians 54 45 5
Twins 45 53 7
Tigers 43 59 8
White_Sox 35 64 9
Royals 30 69 3
Also I know there are no ties in the MLB lol just throwing in an additional variable.
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main(void)
{
struct records {
char filename;
char team[50];
int wins;
int tie;
int loss;
int points;
};
struct records roster;
FILE *ifp = NULL;
FILE *afd = NULL;
const int argv;
char filename2[64] = {0};
char filename[64] = {0};
int points;
int points2;
int total;
printf("\nPlease enter the the .txt file you would like to open: ");
scanf("%63s", filename);
printf("Opened file name %s",filename);
ifp = fopen(filename,"r");
if (ifp == NULL)
{
printf("Could not open");
printf("\nPlease enter the the .txt file you would like to open:");
scanf("%63s", filename);
printf("Opened file name %s",filename);
ifp = fopen(filename,"r");
}
printf("\nReading the file %s \n", filename);
while(fscanf(ifp, "%s %d %d%d" , roster.team, &roster.wins, &roster.loss,
&roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d\n", roster.team, roster.wins,
roster.loss, roster.tie);
printf("\nWins are worth 2 points ties are worth 1 point and losses are
worth \nzero in overall league standings.");
printf("\nHere are the overall ALCentral Standings!\n");
ifp = fopen(filename, "r");
fopen(filename, "r"); while(fscanf(ifp, "%s %d %d %d", roster.team,
&roster.wins, &roster.loss, &roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d Total league points:%d\n",
roster.team, roster.wins, roster.loss, roster.tie, (roster.wins * 2 +
roster.tie * 1), total);
printf("closing %s", filename);
fclose(ifp);
printf("\nPlease enter the the .txt file you would like to write to: ");
scanf("%63s", filename2);
printf("Opened file name %s", filename2);
afd = fopen(filename2, "a+");
if (afd == NULL)
{
printf("Could not open"); printf("\nPlease enter the the .txt file you
would like to open: ");
scanf("%63s", filename2);
printf("Opened file name %s", filename2);
afd = fopen(filename2,"a+");
}
ifp = fopen(filename,"r");
fopen(filename, "r");
points = roster.wins * 2;
points2 = roster.tie * 1;
total = points + points2;
while(fscanf(ifp, "%s %d %d %d", roster.team, &roster.wins, &roster.loss,
&roster.tie) != EOF)
fprintf(afd, "%s Wins:%d Losses:%d ties:%d total league points:%d\n",
roster.team, roster.wins, roster.loss, roster.tie, (roster.wins *2 +
roster.tie *1 ), total, filename2);
printf("\nYour stats have been recorded to the selected
file!\nClosing all open files! \nThanks for using the STAT-Master!
Have a great day!
fclose(afd);
fclose(ifp);
return 0;
}
Your logic is quite jumbled. If I understand what you are attempting, you want to read each teams wins, loss and tie record from your data file, and then compute the points that are then used to sort the teams into descending (or perhaps ascending) order and then output the "the overall ALCentral Standings" to either stdout or a file, or both.
Before starting let's talk about your definition of struct records inside of main(). While legal, you generally will want your struct declaration to be at file scope so that the struct type is available to any functions you write to help process the data in your code. Here, you must move the declaration outside of main() because the qsort compare function will need to know what the type struct records is. The compare function cannot be declared and defined in main() because C does not allow nested function declarations. So move your struct outside of main(), e.g.
#define MAXR 16 /* if you need a constant, #define one (or more) */
#define MAXC 64
#define MAXB 1024
typedef struct records {
char team[MAXC];
int wins,
loss,
tie,
points;
} record_t;
(note: using a typedef can make things more convenient)
To begin with, your multiple attempts to read the filename is awkward and never insures you actually have a file open. It just makes a couple of attempts and then effectively gives up validation and just blindly assumes there is a valid file stream to read and write to and presses ahead. This is a recipe for invoking Undefined Behavior.
When ever you do anything in C, and especially when you are dealing with user input or file operations, you must validate every step. You must know whether or not the file is open for reading before you attempt to read. (no matter how many times you attempt to open it) You must validate what you have read before you begin using the values in your code. You must validate the state of your input stream before you attempt your next read, and handle any characters that remain.
With that in mind you can prompt for the filename and attempt to open the filename provided in a continual loop until you validate the file is actually open, e.g.
/** helper function to empty stdin between inputs */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
...
FILE *fp = NULL; /* file pointer */
do { /* loop until valid filename given and file open */
printf("\nenter input filename: ");
if (scanf (" %63[^\n]", filename) != 1) {
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
empty_stdin(); /* remove '\n' and any add'l chars in input buffer */
fp = fopen (filename, "r"); /* open filename */
if (fp == NULL) /* validate file open for reading */
perror ("fopen-input");
} while (fp == NULL);
printf("Opened file name %s\n\n",filename);
(note: rather than using scanf for user input, fgets provides a better approach to user input and far fewer pitfalls that scanf, example below with roster)
Next, your understanding of how to collect the data in memory in order to sort it, is not evident from your post. When you want to sort a collection of team information, you must read all team information into memory before you can pass the information to qsort for sorting.
You have a struct that holds all of information for each team, you simply need to read each line (record) from your input file into an array or struct which can then easily be passed to qsort for sorting.
To use qsort you first must define a compare function that is used to qsort to order your data. The compare function has a specific declaration:
int compare (const void *a, const void *b);
Where each const void * parameter will be a pointer to one of the elements in your array of records. (in C a void pointer, e.g. void* is a generic pointer that can be assigned from, or to, any other pointer type without a cast -- that is why the compare function's declaration uses that type for the parameters) Your job in writing a compare function is simply to provide a type to the pointers (either by assignment or by explicit cast, your choice) and then a comparison between the values that you wish to sort by. For example, your compare on the points member of each struct could simply be:
/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
const record_t *ta = a;
const record_t *tb = b;
return (ta->points < tb->points) - (ta->points > tb->points);
}
(where the (a < b) - (a > b) simply evaluates to -1, 0, 1 depending on the inequalites, e.g. if a = 4 and b = 3, you have (0) - (1) which equals -1 meaning a sorts before b)
All that remains is reading each teams data into your array and computing points and then passing your array to qsort.
To read your data into an array, read each line into a buffer, parse the data in the buffer into the individual values, validate that each of the individual values where parsed correctly, compute the points value for each team, increment your array index and then go read the next line. You could do something like the following using fgets to read each line of data:
record_t roster[MAXR] = {{ .team = "" }}; /* array to hold teams */
...
/* read up to MAXR team records from file */
while (ndx < MAXR && fgets (buf, MAXB, fp)) {
/* parse data from buffer filled, saving return */
int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team,
&roster[ndx].wins, &roster[ndx].loss,
&roster[ndx].tie);
if (rtn < 4) /* if less than 4 conversions, get next line */
continue;
/* compute points (2 * wins + ties) */
roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
ndx++; /* increment index */
}
fclose (fp); /* close file */
(note: you can add a strlen() check of buf to determine if additional characters remain in the line unread -- that is left to you).
With all you data in your array of structs roster, all that remains is passing roster to qsort and then outputting your sorted data.
The following sorts the data and outputs the results to stdout. Writing the output to a file is left to you, but hint, you don't need both ifp and afp and you don't need filename and filename2 or points2, you are only dealing with one file/filename at a time. The sort and output is simple:
/* sort roster based on points (descending order) */
qsort (roster, ndx, sizeof *roster, compare);
/* outpout results */
printf ("Here are the overall ALCentral Standings!\n\n"
"Team Wins Loss Tie Points\n\n");
for (int i = 0; i < ndx; i++)
printf ("%-12s %4d %4d %4d %4d\n", roster[i].team,
roster[i].wins, roster[i].loss, roster[i].tie,
roster[i].points);
Putting it altogether in an example, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXR 16 /* if you need a constant, #define one (or more) */
#define MAXC 64
#define MAXB 1024
typedef struct records {
char team[MAXC];
int wins,
loss,
tie,
points;
} record_t;
/** helper function to empty stdin between inputs */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
const record_t *ta = a;
const record_t *tb = b;
return (ta->points < tb->points) - (ta->points > tb->points);
}
int main (void) {
int ndx = 0; /* index for roster array */
char buf[MAXB] = "", /* buffer for fgetrs */
filename[MAXC] = ""; /* filename to read */
record_t roster[MAXR] = {{ .team = "" }}; /* array to hold teams */
FILE *fp = NULL; /* file pointer */
do { /* loop until valid filename given and file open */
printf("\nenter input filename: ");
if (scanf (" %63[^\n]", filename) != 1) {
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
empty_stdin(); /* remove '\n' and any add'l chars in input buffer */
fp = fopen (filename, "r"); /* open filename */
if (fp == NULL) /* validate file open for reading */
perror ("fopen-input");
} while (fp == NULL);
printf("Opened file name %s\n\n",filename);
/* read up to MAXR team records from file */
while (ndx < MAXR && fgets (buf, MAXB, fp)) {
/* parse data from buffer filled, saving return */
int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team,
&roster[ndx].wins, &roster[ndx].loss,
&roster[ndx].tie);
if (rtn < 4) /* if less than 4 conversions, get next line */
continue;
/* compute points (2 * wins + ties) */
roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
ndx++; /* increment index */
}
fclose (fp); /* close file */
/* sort roster based on points (descending order) */
qsort (roster, ndx, sizeof *roster, compare);
/* outpout results */
printf ("Here are the overall ALCentral Standings!\n\n"
"Team Wins Loss Tie Points\n\n");
for (int i = 0; i < ndx; i++)
printf ("%-12s %4d %4d %4d %4d\n", roster[i].team,
roster[i].wins, roster[i].loss, roster[i].tie,
roster[i].points);
return 0;
}
Example Input File
The file is provided as given, with blank lines in between records.
$ cat dat/teams.txt
Indians 54 45 5
Twins 45 53 7
Tigers 43 59 8
White_Sox 35 64 9
Royals 30 69 3
Example Use/Output
$ ./bin/qsortstrcmp
enter input filename: dat/teams.txt
Opened file name dat/teams.txt
Here are the overall ALCentral Standings!
Team Wins Loss Tie Points
Indians 54 45 5 113
Twins 45 53 7 97
Tigers 43 59 8 94
White_Sox 35 64 9 79
Royals 30 69 3 63
Look things over and let me know if you have further questions.
So I've got a project I'm working on in ANSI C (C89) for class. I've gotten stuck in the process. I'm getting a
segmentation fault: 11
issue, and no matter what I look up, I can't seem to solve the issue. can someone look at my code and point me in the right direction?
/* CS315 Lab 3: C data types */
#include <stdio.h>
#include <stdlib.h> /*added this to provide a declaration for malloc*/
#define TENMB 1048576 /*1024 kilobytes or 10 megabytes */
#define ONEB 1
FILE * fp = NULL;
End(FILE * fp)/*made END a function */
{
fclose(fp); /* close and free the file */
exit(EXIT_SUCCESS); /* or return 0; */
}
initialize(int argc, char ** argv)
{
/* Open the file given on the command line */
if( argc != 2 )
{
printf( "Usage: %s filename.mp3\n", argv[0] );
return(EXIT_FAILURE);
}
FILE * fp = fopen(argv[1], "rb");
if( fp == NULL )
{
printf( "Can't open file %s\n", argv[1] );
return(EXIT_FAILURE);
}
return 0; /*this might need to change */
}
readFile(FILE * fp)
{
/* How many bytes are there in the file? If you know the OS you're
on you can use a system API call to find out. Here we use ANSI
standard function calls. */
long size = 0;
fseek(fp, 0, SEEK_END ); /* go to 0 bytes from the end */
size = ftell(fp); /* how far from the beginning? */
rewind(fp); /* go back to the beginning */
if( size < ONEB || size > TENMB )
{
printf("File size is not within the allowed range\n");
End(fp); /* switched from goto END:*/
}
printf( "File size: %.2ld MB\n", size/TENMB ); /* change %d to %ld, added .2 to print to 2 decimal places (maybe use f instead) */
/* Allocate memory on the heap for a copy of the file */
unsigned char * data = (unsigned char *)malloc(size);
/* Read it into our block of memory */
size_t bytesRead = fread( data, sizeof(unsigned char), size, fp );
free(data); /* deallocation */
if( bytesRead != size )
{
printf( "Error reading file. Unexpected number of bytes read: %zu\n",bytesRead ); /* changed from %d to %zu */
End(fp); /* switched from goto END:*/
return 0;
}
return 0;
}
int main( int argc, char ** argv )
{
initialize(argc, argv);
readFile(fp);
/* We now have a pointer to the first byte of data in a copy of the file, have fun
unsigned char * data <--- this is the pointer */
}
Thanks for any help!
In your code you have declared fp twice, both globally and locally
FILE * fp = NULL;
End(FILE * fp)/*made END a function */
{
fclose(fp); /* close and free the file */
exit(EXIT_SUCCESS); /* or return 0; */
}
initialize(int argc, char ** argv)
{
...
FILE * fp = fopen(argv[1], "rb");
...
Even though you are writing C89 there is no need to pick up the bad things with the standard. E.g. declare functions properly with a return type.
You say that this should be C89 code, but there are some aspects of this code that are not C89 compliant. I have included a modified version of your code below that fixes these things, compiles without warnings, and seems to run correctly. By way of a disclaimer, I never write in C89, so someone else may take issue with something here.
The segfault that you report is due to the double-declaration of fp, as pointed out by #BLUEPIXY in the comments to your question. You declare this file pointer first at file scope (i.e., fp is a "global" variable), and then at block scope within the function initialize(). The second declaration hides the first one within the block, so you open a file in initialize() and assign the resulting pointer to the block scope fp (which has automatic storage duration). When main() then calls readFile(), it is the file scope fp that is passed, and this fp is initialized to NULL. So fseek(fp, 0, SEEK_END) is called on a NULL pointer, and this causes the segfault. Changing the second declaration to an assignment fixes this problem:
fp = fopen(argv[1], "rb");
You can't mix variable declarations and code in C89, so you need to move the declarations to the beginning of the functions. Also, C89 does not support the %zu format specifier for size_t variables. Instead, use %lu and cast the size_t value bytesRead to (unsigned long).
I added a definition: ONEMB 1048576, and removed the TENMB definition. In the calculation of the file size, you were dividing the number of bytes by 10MB instead of 1MB, and I take it that you were in fact trying to calculate the number of MB.
I added a check for errors to the call to ftell(). Since ftell() returns a long, I added a size_t fileSize variable and cast the value of size to size_t before assigning it to fileSize. I changed theprintf() statement to:
printf( "File size: %.2f MB\n", (fileSize * 1.0)/ONEMB );
so that the file size is reported with more precision; this way, files smaller than 1 MB will not be reported as having a size of 0 MB.
I removed the cast from the call to malloc(), as it is absolutely not needed in C.
The assignment to bytesRead calls fread(), which takes a size_t parameter for the third argument, not a long. You originally had the long value size here, and this is one reason for the new fileSize variable. The following line had a comparison between bytesRead, which is size_t, and size. This should generate compiler warnings (you do have them on, don't you?) as using signed and unsigned types in the same expression can lead to difficulties, and so here I used the fileSize variable again.
You were missing the return statement at the end of main(), and your function definitions were missing their return type specifiers, so I also added these. I also noticed that your program was segfaulting when invoked with no arguments. This is because on errors in your initialize() function, you were returning to the calling function, which then called readFile() with a NULL pointer. You should instead exit() from the program. I have made these changes to all file error traps in the modified code.
There are some other issues with your code. I would prefer not to use the End() function. It does not test for errors during closing files, and will segfault if you pass in a NULL pointer. Also, the "Unexpected number of bytes read" error in readFile() will end up exiting successfully, since End() always exits with EXIT_SUCCESS.
I don't love the casting from long to size_t that I did, and someone else may have a better approach. What I was trying to manage here was that ftell() returns a long, which you use to calculate the file size, and fread() both takes and returns size_t values.
Most of what I did was in the readFile() function, and I removed your comments from this function to make it easier to see what was changed.
I compiled with:
gcc -std=c89 -Wall -Wextra -pedantic
#include <stdio.h>
#include <stdlib.h> /*added this to provide a declaration for malloc*/
#define ONEMB 1048576
#define ONEB 1
FILE * fp = NULL;
void End(FILE * fp)/*made END a function */
{
fclose(fp); /* close and free the file */
exit(EXIT_SUCCESS); /* or return 0; */
}
int initialize(int argc, char ** argv)
{
/* Open the file given on the command line */
if( argc != 2 )
{
printf( "Usage: %s filename.mp3\n", argv[0] );
exit(EXIT_FAILURE);
}
fp = fopen(argv[1], "rb");
if( fp == NULL )
{
printf( "Can't open file %s\n", argv[1] );
exit(EXIT_FAILURE);
}
return 0; /*this might need to change */
}
int readFile(FILE * fp)
{
long size = 0;
unsigned char *data;
size_t fileSize, bytesRead;
fseek(fp, 0, SEEK_END );
if ((size = ftell(fp)) == -1) {
fprintf(stderr, "ftell() error\n");
exit(EXIT_FAILURE);
};
rewind(fp);
if( size < ONEB || size > 10 * ONEMB )
{
printf("File size is not within the allowed range\n");
End(fp);
}
fileSize = (size_t) size;
printf( "File size: %.2f MB\n", (fileSize * 1.0)/ONEMB );
data = malloc(fileSize);
bytesRead = fread( data, sizeof(unsigned char), fileSize, fp );
free(data);
if( bytesRead != fileSize )
{
printf( "Error reading file. Unexpected number of bytes read: %lu\n", (unsigned long) bytesRead );
End(fp);
exit(EXIT_FAILURE);
}
return 0;
}
int main( int argc, char ** argv )
{
initialize(argc, argv);
readFile(fp);
/* We now have a pointer to the first byte of data in a copy of the file, have fun
unsigned char * data <--- this is the pointer */
End(fp);
return 0;
}
Operating System Used:Ubuntu 11.04
Compiler Used: GCC
Program related files:Git Hub Link
I'm trying a implement program which will do a job, same as that of CPP (The C preprocessor) when I compile a .c file.
In this particular code Copy_file_to_buf function not copying the whole file into the buffer.
Acutal size of the is 117406C,but ftell in the copy_file_to_buf function showing it as 114689.
EDIT:
There is no data loss when I copied the contents of dummyfile to a buffer using same program but I've written copy_file_to_buf program seperately in temp.c file.
temp.c
#include<stdio.h>
main(int argc,char **argv)
{
FILE *inputFile;
int sizeofFile, rc;
char *source_buf;
fprintf(stderr, "In Copy_file_to_buf\n");
sleep(1);
inputFile=fopen(argv[1],"r");
if (!inputFile) {
fprintf(stderr, "Oops, failed to open inputfile \"%s\"\n", argv[1] );
return NULL;
}
fseek(inputFile,0,SEEK_END);
sizeofFile=ftell(inputFile);
fseek(inputFile,0,SEEK_SET);
source_buf=calloc(1,1+sizeofFile);
rc = fread(source_buf,sizeofFile,1,inputFile);
/* now check rc */
fprintf(stderr, "Size of the file=%d; fread returned %d.\n", sizeofFile, rc);
//sleep(5);
fclose(inputFile);
source_buf[sizeofFile] = 0;
puts(source_buf);
return source_buf;
}
Looks like the fseek() and ftell aren't working as expected for the below code.
puts("Copying dummyfile contents");
test_buf=Copy_file_to_buf("dummyfile");
puts(test_buf);
Filename: preprocessor.c
#include"myheader.h"
/* agrv[1]=preprocessor
* argv[2]=test.c
*
* Program on PREPROCESSOR
*
* Steps:
* 1.Removal of comments.
* 2.Inclusion of headerfiles.
* 3.Macro substitution.
* a.function like arguments
* b.Stringification
* c.Concatenation
* 4.Conditional compilation
* a.#Ifdef
* b.#If
* c.#defined
* d.#Else
* e.#Elif
*/
int
main(int argc, char *argv[])
{
char *source_buf,*subBuf,*rmc_buf,*test_buf;
char **main_header_names,**sub_header_names;
int main_header_count,sub_header_count;
source_buf=(char *)Copy_file_to_buf(argv[1]);//...
rmc_buf=removeComments(source_buf);//...
main_header_names=(char **)getMainHeaderNames(rmc_buf);//...
source_buf=(char *)Copy_file_to_buf(argv[1]);//...
rmc_buf=removeComments(source_buf);//...
main_header_count=mainHeaderCounter(rmc_buf);//...
printf("Main Header Count=%d",main_header_count);//...
includeHeaders(main_header_names,main_header_count);
subBuf=(char *)Copy_file_to_buf("pre.i");//...
sub_header_names=(char **)getSubHeadersNames(subBuf);//...
subBuf=(char *)Copy_file_to_buf("pre.i");//...
sub_header_count=subHeadersCounter(subBuf);//...
WriteSubHeadersToFile(sub_header_count,sub_header_names,"dummyfile");//...
puts("Copying dummyfile contents");
test_buf=Copy_file_to_buf("dummyfile");
puts(test_buf);
/*test_buf=removeComments(test_buf);
puts(test_buf);
sub_header_names=(char **)getSubHeadersNames(test_buf);
test_buf=(char *)Copy_file_to_buf("dummyfile");
sub_header_count=subHeadersCounter(test_buf);
WriteSubHeadersToFile(sub_header_count,sub_header_names,"dummyfile2");
printf("Line:%d File:%s",__LINE__,__FILE__);
*/
return 0;
}
Filename:CopyFile.c
#include"myheader.h"
//Copying input file data into source_buf
char * Copy_file_to_buf(char *fileName)
{
FILE *inputFile;
int sizeofFile;
char *source_buf;
puts("In Copy_file_to_buf");
inputFile=fopen(fileName,"r");
fseek(inputFile,0,2);
sizeofFile=ftell(inputFile);
sizeofFile++;
fseek(inputFile,0,0);
source_buf=calloc(1,sizeofFile);
fread(source_buf,sizeofFile,1,inputFile);
printf("SIZE OF THE FILE=%d",sizeofFile);
//sleep(5);
fclose(inputFile);
return source_buf;
}
Check the return value of fseek() (and all other library calls!)
if (fseek(inputFile, 0, SEEK_END)) perror("seek to end");
sizeofFile = ftell(inputFile);
if (sizeofFile == -1) perror("ftell");
You are asking fread to read one block sizeofFile long. If it cannot read a block that size it will fail and return zero. Instead you would do better to request sizeofFile blocks of 1 byte long. Then it will report exactly how may bytes it managed to read, which may be fewer than sizeofFile for a number of reasons.
rc = fread( source_buf, 1, sizeofFile inputFile ) ;
In your case it will always be fewer than sizeofFile because you previously incremented it, so there never were sizeofFile bytes to be read. You should have read the file size not the buffer size, but the reading multiple blocks of one byte is still preferable in any case. In fact I would suggest the following changes:
sizeofFile = ftell( inputFile ) ;
// REMOVED sizeofFile++ ;
fseek( inputFile, 0, SEEK_SET ) ;
source_buf = calloc( 1, sizeofFile + 1 ) ; // Don't confuse file size and buffer size here.
rc = fread( source_buf, 1, sizeofFile, inputFile ) ; // SWAPPED param 2 and 3
It makes sense to use a block size other than 1 if you are reading fixed length records. If reading a byte stream of arbitrary length, the size should generally be 1, and the count used to determine the amount of data read.
char * Copy_file_to_buf(char *fileName)
{
FILE *inputFile;
int sizeofFile, rc;
char *source_buf;
fprintf(stderr, "In Copy_file_to_buf\n");
inputFile=fopen(fileName,"r");
if (!inputFile) {
fprintf(stderr, "Oops, failed to open inputfile \"%s\"\n", fileName );
return NULL;
}
fseek(inputFile,0,SEEK_END);
sizeofFile=ftell(inputFile);
fseek(inputFile,0,SEEK_SET);
source_buf=calloc(1,1+sizeofFile);
rc = fread(source_buf,sizeofFile,1,inputFile);
/* now check rc */
fprintf(stderr, "SIZE OF THE FILE=%d; fread returned %d.\n", sizeofFile, rc);
//sleep(5);
fclose(inputFile);
source_buf[sizeofFile] = 0;
return source_buf;
}
UPDATE: for testing added main + includes...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv)
{
char *lutser;
lutser = Copy_file_to_buf(argv[1] );
fprintf (stderr, "Strlen(lutser)=%u\n", (unsigned) strlen(lutser) );
puts (lutser);
return 0;
}
Output:
-rw-r--r-- 1 Plasser pis 1008 2012-03-31 15:10 lutsbuf.c
-rwxr-xr-x 1 Plasser pis 8877 2012-03-31 15:11 a.out
Plasser#Pisbak$ ./a.out lutsbuf.c
In Copy_file_to_buf
SIZE OF THE FILE=1008; fread returned 1.
Strlen(lutser)=1008
...
<contents of file>
...
CopyNewFile doesn't close its file. WriteSubHeadersToFile may not always (hard to follow the flow). How are you determining the "real" size of the file?