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...
Related
I want to read a PNG image file with C without any library. From PNG (Portable Network Graphics) Specification Version 1.0 any PNG file has a signature that distinguishes it from other image formats. The signature is the first 8 bytes of the image.
Some sources like the above RFC mentioned the signature as:
137 80 78 71 13 10 26 10 (decimal)
Or like Not able to read IHDR chunk of a PNG file mentioned the signature as:
89 50 4E 47 0D 0A 1A 0A (ASCii)
So, I write a simple code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE (8)
int main(int argc, char **argv){
if(argc != 2) {
printf("Usage: %s <png file>\n", argv[0]);
return 1;
}
char *buf = (char *)malloc(MAX_SIZE);
if(!buf) {
fprintf(stderr, "Couldn't allocate memory\n");
return 1;
}
FILE *f = fopen(argv[1], "r");
if(!f) {
perror("fopen");
printf("Invalid file\n");
free(buf);
return 1;
}
int size = fread(buf, 1, MAX_SIZE, f);
printf(%c\n", buf[1]);
printf(%c\n", buf[2]);
printf(%c\n", buf[3]);
printf(%c\n", buf[4]);
printf(%c\n", buf[5]);
printf(%c\n", buf[6]);
printf(%c\n", buf[7]);
printf(%c\n", buf[8]);
fclose(f);
free(buf);
system("pause");
return 0;
}
When I print the bytes by printf, the output is not like the above.
This is what it shows:
ëPNG→►v#,
Can someone describe what happened and what can I do to modify it?
You need to print each value with the correct format specifier. Here we want numerical representations, not character ones.
From the documentation on printf:
%c writes a single character
%d converts a signed integer into decimal representation
%x converts an unsigned integer into hexadecimal representation
%02X prints a hexadecimal with a minimum width of two characters, padding with leading zeroes, using ABCDEF (instead of abcdef).
See also implicit conversions.
An example:
#include <stdio.h>
#define SIZE 8
int main(int argc, char **argv) {
unsigned char magic[SIZE];
FILE *file = fopen(argv[1], "rb");
if (!file || fread(magic, 1, SIZE, file) != SIZE) {
fprintf(stderr, "Failure to read file magic.\n");
return 1;
}
/* Decimal */
for (size_t i = 0; i < SIZE; i++)
printf("%d ", magic[i]);
printf("\n");
/* Hexadecimal */
for (size_t i = 0; i < SIZE; i++)
printf("%02X ", magic[i]);
printf("\n");
fclose(file);
}
Output:
137 80 78 71 13 10 26 10
89 50 4E 47 0D 0A 1A 0A
I have a file with these contents:
Code Name Income Allow Pens Ins Depend Charity Taxable Tax Net
------------------------------------------------------------------------------------------------------------------------
008 John 100000 4000 5000 1000 3200 1000 85800 20280 79720
001 Doe 50000 4000 0 500 1600 0 43900 7725 42275
I want to print a record if the input code is same as the code in the file.
This is my code:
fscanf(fp, " %3d%s%lf%lf%lf%lf%lf%lf%lf%lf%lf", &code_t, buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t);
printf("\n");
printf(" 03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
`
but it doesn't work.
So I'm thinking about using fscanf to read only the code and then print the line which contains that code. But how can I read the code without other contents like Name, Income, ... and how to read if the code has leading 0 like that?
First you cannot read the file with its header using the scanf you give because Code is incompatible with the first %d, so you need to bypass the first 2 lines
Warning a % is missing in your printf to print the code, so the code is considered as the string by %s with an unspecified behavior (a crash generally)
But how can I read the code without other contents like Name, Income
Of course if you use your scanf you also read the other fields, is it a real problem ? You can also read line per line as a strings (fgets or getline) and look at the beginning if you have the expected code and in that case manage the rest of the string to extract the fields if needed etc
An other way if the content of the file is very formatted is to change the file pointer using fseek to only read the codes up to the expected one (see the proposal at the end of my answer).
how to read if the code has leading 0 like that?
I do not understand scanf read it well, this is not octal because there is 008. If the presence of the 0 at the left are important do not manage the code as a number but as a string both in the file and when the code to search is given
A code from yours reading well your input file :
#include <stdio.h>
int bypassLine(FILE * fp)
{
int c;
for (;;) {
c = fgetc(fp);
if (c == EOF)
return 0;
if (c == '\n')
return 1;
}
}
int main()
{
FILE * fp = stdin;
int code_t;
char buffer[64];
double inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t;
if (!bypassLine(fp) || !bypassLine(fp))
puts("too short file");
else {
while (fscanf(fp, " %3d%s%lf%lf%lf%lf%lf%lf%lf%lf%lf", &code_t, buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t) == 11) {
printf(" %03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
}
}
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra f.c
pi#raspberrypi:/tmp $ cat f
Code Name Income Allow Pens Ins Depend Charity Taxable Tax Net
------------------------------------------------------------------------------------------------------------------------
008 John 100000 4000 5000 1000 3200 1000 85800 20280 79720
001 Doe 50000 4000 0 500 1600 0 43900 7725 42275
pi#raspberrypi:/tmp $ ./a.out < f
008 John 100000 4000 5000 1000 3200 1000 85800 20280 79720
001 Doe 50000 4000 0 500 1600 0 43900 7725 42275
pi#raspberrypi:/tmp $
Note there is no protection against an overflow when reading the string in scanf with just %s, if is better to use %63s because I sized buffer 64
A little change to search for the code, still using your scanf, giving the name of the file and the expected code in argument :
#include <stdio.h>
int bypassLine(FILE * fp)
{
int c;
for (;;) {
c = fgetc(fp);
if (c == EOF)
return 0;
if (c == '\n')
return 1;
}
}
int main(int argc, char ** argv)
{
if (argc != 3)
printf("usage : %s <file> <code>\n", *argv);
else {
FILE * fp;
int code_t, expected;
char buffer[64];
double inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t;
if ((fp = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "cannot open '%f'\n", argv[1]);
return -1;
}
if (!bypassLine(fp) || !bypassLine(fp)) {
fprintf(stderr, "too short file '%s'\n", argv[1]);
fclose(fp);
return -1;
}
if (sscanf(argv[2], "%d%c", &expected, buffer) != 1) {
fprintf(stderr, "invalid code '%s'\n", argv[2]);
}
else {
while (fscanf(fp, " %3d%63s%lf%lf%lf%lf%lf%lf%lf%lf%lf", &code_t, buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t) == 11) {
if (code_t == expected) {
printf(" %03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
fclose(fp);
return 0;
}
}
fprintf(stderr, "code %d not found in '%s'\n", expected, argv[1]);
}
fclose(fp);
return -1;
}
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra f.c
pi#raspberrypi:/tmp $ ./a.out ./f 2
code 2 not found in './f'
pi#raspberrypi:/tmp $ ./a.out ./f 8
008 John 100000 4000 5000 1000 3200 1000 85800 20280 79720
pi#raspberrypi:/tmp $
An other way using fseek to move directly from code to code in the file :
#include <stdio.h>
int bypassLine(FILE * fp)
{
int c;
for (;;) {
c = fgetc(fp);
if (c == EOF)
return 0;
if (c == '\n')
return 1;
}
}
int main(int argc, char ** argv)
{
if (argc != 3)
printf("usage : %s <file> <code>\n", *argv);
else {
FILE * fp;
int code_t, expected;
char buffer[64];
double inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t;
if ((fp = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "cannot open '%f'\n", argv[1]);
return -1;
}
if (!bypassLine(fp) || !bypassLine(fp)) {
fprintf(stderr, "too short file '%s'\n", argv[1]);
fclose(fp);
return -1;
}
if (sscanf(argv[2], "%d%c", &expected, buffer) != 1) {
fprintf(stderr, "invalid code '%s'\n", argv[2]);
}
else {
long offset = ftell(fp);
while (fscanf(fp, " %03d", &code_t) == 1) {
if (code_t == expected) {
/* extract the other fields */
if (fscanf(fp, "%63s%lf%lf%lf%lf%lf%lf%lf%lf%lf", buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t) == 10) {
printf(" %03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
fclose(fp);
return 0;
}
else {
fprintf(stderr, "code %d found but cannot read next fields\n", code_t);
fclose(fp);
return -1;
}
}
/* the lines are supposed having all the times 114 characters newline included */
offset += 114;
if (fseek(fp, offset, SEEK_SET) == -1) {
fprintf(stderr, "error when going at offset %d of '%s'\n", offset, argv[1]);
fclose(fp);
return -1;
}
}
fprintf(stderr, "code %d not found in '%s'\n", expected, argv[1]);
}
fclose(fp);
return -1;
}
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra f.c
pi#raspberrypi:/tmp $ cat f
Code Name Income Allow Pens Ins Depend Charity Taxable Tax Net
------------------------------------------------------------------------------------------------------------------------
008 John 100000 4000 5000 1000 3200 1000 85800 20280 79720
001 Doe 50000 4000 0 500 1600 0 43900 7725 42275
pi#raspberrypi:/tmp $ ./a.out ./f 8
008 John 100000 4000 5000 1000 3200 1000 85800 20280 79720
pi#raspberrypi:/tmp $ ./a.out ./f 1
001 Doe 50000 4000 0 500 1600 0 43900 7725 42275
pi#raspberrypi:/tmp $ ./a.out ./f 11
code 11 not found in './f'
I want to print a record if the input code is same as the code in the file.
If your goal is simply to print the records (aka lines) where "Code" matches some value given by the user, your approach seems a bit too complex as there is no need for scanning all the fields.
Simply use fgets to read the line, then check the Code value and do the print if it matches.
Something like:
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
int main(int argc, char* argv[]){
if (argc != 2)
{
printf("Wrong usage...\n");
return 1;
}
int code_to_print = atoi(argv[1]);
int code_read;
FILE* fp = fopen("db.txt", "r");
if (!fp)
{
printf("File error...\n");
return 1;
}
char buf[1024];
while (fgets(buf, sizeof buf, fp))
{
if (sscanf(buf, "%d", &code_read) == 1 && code_read == code_to_print)
{
printf("%s", buf);
}
}
fclose(fp);
}
Use the program like:
./prog 8
.. how to read if the code has leading 0 like that?
If the leading zeros are important then you can't scan using %d as that will "remove" the zeros. Instead you need to scan the Code as a word. Like:
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
int main(int argc, char* argv[]){
if (argc != 2)
{
printf("Wrong usage...\n");
return 1;
}
char code_read[4] = {0};
FILE* fp = fopen("db.txt", "r");
char buf[1024];
while (fgets(buf, sizeof buf, fp))
{
if (sscanf(buf, "%3s", code_read) == 1 && strcmp(code_read, argv[1]) == 0)
{
printf("%s", buf);
}
}
fclose(fp);
}
Use the program like:
./prog 008
I'm looking to sort a txt file that contains 1199 numbers in 10 columns per row, and I know that the last row will have 10 or fewer columns.
However, I'm getting 11 columns on the first row.
Thanks for the help.
#include <stdio.h>
#define t 1024
int main()
{
int i=0, c;
char p[t];
FILE *f;
f = fopen("CMB.txt", "r");
while ((c = getc(f)) != EOF)
{
fscanf(f, "%s", p);
if(i%10 == 0 && i > 0)
{
printf("\n");
}
printf("%s ", p);
if (c == '\n')
{
i++;
}
}
printf("\n %d", i+1);
fclose(f);
}
When I try your code it prints correctly the 10 columns you are expecting , perhaps you didn't recompile after modifying.
This is your code , I only removed the file reading and printing char.
#include <stdio.h>
int main()
{
int i=0;
//, c;
// char p[t];
// FILE *f;
// f = fopen("CMB.txt", "r");
while (i<30)
{
// fscanf(f, "%s", p);
if(i%10 == 0 && i > 0)
{
printf("\n");
}
printf("%d ", i);
// if (c == '\n')
// {
i++;
// }
}
// printf("\n %d", i+1);
// fclose(f);
}
Continuing from my earlier comment, when reading columns of data from a text file, it is generally better to read a line of input at a time with a line-oriented input function such as fgets or POSIX getline and then to parse the data from the buffer filled with sscanf (or walking a pointer down the buffer picking out what you need)
In your case when dealing with a fixed number of columns (10) where less than all values may be present, sscanf provides a way to determine exactly how many values are present in each row while allowing you to make use of the data if less than 10 are present.
sscanf (as with all scanf functions) returns the number of successful conversions took place based on the format string you provide (or returning EOF if the end of input is encountered before a conversion takes place). If you want to read 10-integer values from each line of data into an integer array names arr, you could use fgets to read the line into a buffer (say buf) and then separate the integer values head in buf with sscanf, e.g.
...
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
/* parse into integer values with sscanf saving return */
int rtn = sscanf (buf, "%d %d %d %d %d %d %d %d %d %d",
&arr[0], &arr[1], &arr[2], &arr[3], &arr[4],
&arr[5], &arr[6], &arr[7], &arr[8], &arr[9]);
...
Then you simply validate the return (rtn) using whatever if..else or switch... statements you need. Here we can simply sum the number of values read and output the row number for any row containing less than COL number of values for purposes of this example, e.g.
...
if (rtn > 0) /* did at least one conversion take place? */
n += rtn; /* increment count of values read */
if (rtn < COL) /* were less than COL values read? */
printf ("row[%zu]: %d values read.\n", row + 1, rtn);
row++; /* increment row count */
}
Putting it altogether, you could do something like the following:
#include <stdio.h>
#define COL 10
#define MAXC 1024
int main (int argc, char **argv) {
char buf[MAXC];
int arr[COL] = {0};
size_t n = 0, row = 0;
/* 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;
}
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
/* parse into integer values with sscanf saving return */
int rtn = sscanf (buf, "%d %d %d %d %d %d %d %d %d %d",
&arr[0], &arr[1], &arr[2], &arr[3], &arr[4],
&arr[5], &arr[6], &arr[7], &arr[8], &arr[9]);
if (rtn > 0) /* did at least one conversion take place? */
n += rtn; /* increment count of values read */
if (rtn < COL) /* were less than COL values read? */
printf ("row[%zu]: %d values read.\n", row + 1, rtn);
row++; /* increment row count */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
printf ("%zu values read from file.\n", n);
}
Example Input File
Reading a data file with 10-integers per-row for 119 rows and reading a final row with 9-integers as you describe in your question, you could use an input file like:
$ head -n4 dat/1199_10col.txt; echo "<snip>"; tail -n4 dat/1199_10col.txt
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
<snip>
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
1191 1192 1193 1194 1195 1196 1197 1198 1199
Example Use/Output
Running the code against the above file would yield the expected results of reading 1199 values, 10-integers per-row with 9-integers read from the final row with notice of the short row:
$ ./bin/read10col <dat/1199_10col.txt
row[120]: 9 values read.
1199 values read from file.
While there are many ways to do this, and arguably using fgets with strtol provides opportunity for finer grained error detection, this is probably one of the more simple and straight-forward approaches.
Look things over and let me know if you have further questions.
Why do you read a character before the first number? Why do you only increment i if this character is a newline?
If the file contains just numbers, you can simplify the code this way:
#include <stdio.h>
int main() {
int i, n;
FILE *f = fopen("CMB.txt", "r");
if (f != NULL) {
for (i = 0; fscanf(f, "%d", &n) == 1; i++) {
/* output the number followed by a tab, or by a newline every 10 numbers */
printf("%d%c", n, "\t\n"[i % 10 == 9]);
}
if (i % 10 != 0) {
printf("\n");
}
fclose(f);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * fp;
char *line;
int i;
size_t len = 0;
fp = fopen("CMB.txt", "r");
while (getline(&line, &len, fp) != -1)
{
fscanf(fp, "%s", line);
if(i%10 == 0 && i > 0)
{
printf("\n");
}
printf("%s\t ", line);
i++;
}
printf("\n\n%d", i);
fclose(fp);
return 0;
}
So now the numbers from the file are sorted in 10 columns, but the last line only have 8 columns when it should have 9. When I count (i) to see how many lines the while loop reads it only reads 1198, but i should read 1199. So I'm guessing it's skipping the first line? Appreciate the help :D
I know there are too many questions about this algorithm but I couldn't really find a good answer for compressing the bytes. I am kind of a newbie in C. I have the following code:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
//compress function here...
int main(int argc, char **argv) {
if(argc != 2){
fprintf(stderr, "Wrong argument number\n");
exit(1);
}
FILE *source = fopen(argv[1], "rb");
if(source == NULL){
fprintf(stderr, "Cannot open the file to be read\n");
exit(1);
}
FILE *destination;
char name = printf("%s.rle", argv[1]);
while((destination = fopen(&name, "wb")) == NULL){
fprintf(stderr, "Can't create the file to be written\n");
exit(1);
}
compress_file(source, destination);
int error;
error = fclose(source);
if(error != 0){
fprintf(stderr, "Error: fclose failed for source file\n");
}
error = fclose(destination);
if(error != 0){
fprintf(stderr, "Error: fclose failed for destination file\n");
}
}
If this is test.c and the executable is test. I need to make this work on terminal/command prompt as "./test file.txt". My file.txt includes something like (bytes):
20 21 20 20 8F 8F 21 21 64 60 70 20 21 90 90
and the desired output is:
01 20 01 21 02 20 02 8F 02 21 01 64 01 60 01 70 01 20 01 21 02 90
My code create a file and that file includes:
0b00 0000 0106 0000 0000 0000 0000 0000
0000 0000 0a
instead what I want. What do I miss?
Also I want my file to be named as file.txt.rle but it has no name.
EDIT:
char name[30];
sprintf(name, "%s.rle", argv[1]);
solved the problem for naming.
Also I want my file to be named as file.txt.rle but it has no name.
Well, this code
char name = printf("%s.rle", argv[1]);
while((destination = fopen(&name, "wb")) == NULL){
doesn't give you a string like "file.txt.rle". Instead try something like:
size_t len = strlen(argv[1]) + 4 + 1;
char name[len];
sprintf(name, "%s.rle", argv[1]);
while((destination = fopen(name, "wb")) == NULL){
instead what I want. What do I miss?
Well, you miss that you need to put data into str
This code
char str[BUF_SIZE];
fwrite(str, sizeof(str), 1, destination);
just writes an uninitialized variable to the file.
I'll not give you a complete solution but here is something that you can start with and then figure the rest out yourself.
void compress_file(FILE *source, FILE *destination){
char str[BUF_SIZE];
int index = 0;
int repeat_count = 0;
int previous_character = EOF;
int current_character;
while((current_character = fgetc(source)) != EOF){
if(current_character != previous_character) {
if (previous_character != EOF) {
// Save the values to str
str[index++] = repeat_count;
str[index++] = previous_character;
}
previous_character = current_character;
repeat_count = 1;
}
else{
repeat_count++;
}
}
if (repeat_count != 0)
{
str[index++] = repeat_count;
str[index++] = previous_character;
}
fwrite(str, index, 1, destination);
}
EXAMPLE 1:
Let's say a file.txt is:
ABBCCC
On linux it can be displayed hexadecimal like this:
# hexdump -C file.txt
00000000 41 42 42 43 43 43 |ABBCCC|
After running the program, you have:
hexdump -C file.txt.rle
00000000 01 41 02 42 03 43 |.A.B.C|
EXAMPLE 2:
Let's say that file.txt is like
# hexdump -C file.txt
00000000 20 21 20 20 8f 8f 21 21 64 60 70 20 21 90 90 | ! ..!!d`p !..|
the result will be
# hexdump -C file.txt.rle
00000000 01 20 01 21 02 20 02 8f 02 21 01 64 01 60 01 70 |. .!. ...!.d.`.p|
00000010 01 20 01 21 02 90 |. .!..|
As pointed in comments, you have two problems:
Usage of printf instead of sprintf,
Writting to file what you've counted.
Name Creation
char name = printf("%s.rle", argv[1]);
destination = fopen(&name, "wb");
The first line will store the number of characters in argv[1] plus 4 into name. Since, from man printf:
Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).
The second line is more problematic : you ask fopen to open a file giving it a pointer to char instead of a read string.
One correct way to do what you want is:
/* reserve memory to store file name
NOTE: 256 here might not large enough*/
char name[256];
/* fill name array with original name + '.rle'
The return of sprintf is tested to assert that its size was enough */
if (snprintf(name, sizeof name, "%s.rle", argv[1]) >= sizeof name)
{
fprintf(stderr, "name variable is not big enough to store destination filename");
}
Writting to file
The code
char str[BUF_SIZE];
fwrite(str, sizeof(str), 1, destination);
reserve a big array, and writes it to file, without initializing it. To do what you want, you can have this approach:
make a function that will only write two characters in file: the number of character found and the character itself
call this function each time needed (at character changing, but not when one of character is EOF...)
Let's look at :
void write_char_to_file(FILE *f, int count, char car)
{
/* char array to be stored in file */
char str[2];
/* number of repeating characters */
str[0] = count;
/* the character */
str[1] = car;
/* write it to file */
fwrite(str, sizeof str, 1, f);
}
This function has two potential problems:
It doesn't handle char overflow (what if count is over 256?),
It doesn't test the return of fwrite.
Then, when this function should be called, when the current character changes:
EOF A A B C C EOF
In this example, we have 4 characters changes, but we want only 3 writting in the file, so:
Character changing when previous is EOF must be ignored (else we would write something like 0 (char)EOF at file starting),
One writting must be added after while loop since, when the last reading gives EOF, we still have 2 C to write to file.
Let's look at the code:
while((current_character = fgetc(source)) != EOF) {
if(current_character != previous_character) {
/* ignore initial change */
if (previous_character != EOF) {
write_char_to_file(destination, repeat_count, previous_character);
}
previous_character = current_character;
repeat_count = 1;
} else {
repeat_count++;
}
}
/* write last change */
write_char_to_file(destination, repeat_count, previous_character);
This code have a problem too: what if the input file is empty? (first read gives EOF)
The complete code:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define BUF_SIZE 5096
void write_char_to_file(FILE *f, int count, char car)
{
/* char array to be stored in file */
char str[2];
/* number of repeating characters */
str[0] = count;
/* the character */
str[1] = car;
/* write it to file */
fwrite(str, sizeof str, 1, f);
}
void compress_file(FILE *source, FILE *destination)
{
int repeat_count = 0;
int previous_character = EOF;
int current_character;
while((current_character = fgetc(source)) != EOF) {
if(current_character != previous_character) {
if (previous_character != EOF) {
write_char_to_file(destination, repeat_count, previous_character);
}
previous_character = current_character;
repeat_count = 1;
} else {
repeat_count++;
}
}
write_char_to_file(destination, repeat_count, previous_character);
}
int main(int argc, char **argv) {
if(argc != 2) {
fprintf(stderr, "Wrong argument number\n");
exit(1);
}
FILE *source = fopen(argv[1], "rb");
if(source == NULL) {
fprintf(stderr, "Cannot open the file to be read\n");
exit(1);
}
FILE *destination;
/* reserve memory to store file name
NOTE: 256 here might not large enough*/
char name[256];
/* fill name array with original name + '.rle'
The return of sprintf is tested to assert that its size was enough */
if (snprintf(name, sizeof name, "%s.rle", argv[1]) >= sizeof name)
{
fprintf(stderr, "name variable is not big enough to store destination filename");
}
/* while is not needed here, if do the job */
if((destination = fopen(name, "wb")) == NULL) {
fprintf(stderr, "Can't create the file to be written\n");
exit(1);
}
compress_file(source, destination);
int error;
error = fclose(source);
if(error != 0) {
fprintf(stderr, "Error: fclose failed for source file\n");
}
error = fclose(destination);
if(error != 0) {
fprintf(stderr, "Error: fclose failed for destination file\n");
}
/* main must return a integer */
return 0;
}
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.