I'm trying to read a BMP image (greyscales) with C, save values into an array, and convert this array to a string with values separated with a comma.
My program worked well under Windows 7 64-bit, but I had to move to Windows XP 32-bit because of library compatibility problems.
I have 1,750 images to read, and I want to store all of them in a single string.
When I launch my program it goes fine until the 509:th image, then I get a Segmentation Fault caused by fread(). Here's my code:
int i=0,j,k,num,len,length,l;
unsigned char *Buffer;
FILE *fp;
char *string,*finalstring;
char *query;
char tmp2[5],tmp[3];
query = (char *)malloc(sizeof(char)*200000000);
string = (char *)malloc(sizeof(char)*101376);
Buffer = (unsigned char *)malloc(sizeof(unsigned char)*26368);
BITMAPFILEHEADER bMapFileHeader;
BITMAPINFOHEADER bMapInfoHeader;
length = 0;
for (k =1;k<1751;k++)
{
strcpy(link,"imagepath");
//here just indexing the images from 0000 to 1750
sprintf(tmp2,"%.4d",k);
strcat(link,tmp2);
strcat(link,".bmp");
fp = fopen(link, "rb");
num = fread(&bMapFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
num = fread(&bMapInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);
//seek beginning of data in bitmap
fseek(fp,54,SEEK_SET);
//read in bitmap file to data
fread(Buffer,26368,1,fp);
l=0;
for(i=1024;i<26368;i++)
{
itoa(Buffer[i],tmp,10);
len = strlen(tmp);
memcpy(string+l,tmp,len);
memcpy(string+l+len,",",1);
l = l+len+1;
}
memcpy(query,"",1);
memcpy(string,"",1);
printf("%i\n",k);
}
Thanks
Make it tmp[4]; for three digits and the terminating 0.
Also: where is the fclose? I suspect that you're running out of file handles.
Check, whether fp != 0.
Where did you get 101376 from? Each of your bytes take up at most 5 characters as a decimal number with comma (e.g. -127,), 5*26368 is 131840.
Get rid of the casts in malloc calls. And #include <stdlib.h>.
What's the output of this program, in both the 64-bit and 32-bit systems you're using?
#include <stdio.h>
int main(void) {
printf("sizeof (int) is %d\n", (int)(sizeof (int)));
printf("sizeof (int*) is %d\n", (int)(sizeof (int*)));
return 0;
}
Run your program in the debugger.
Set a breakpoint at the call to
fread -- make it conditional on
k==507 (this will stop it when you
expect the fread to be successful).
When the program hits the
breakpoint, examine the variables
and check what is about to be passed
to fread. The first one or two times
you hit the breakpoint, the values
will be good.
Then on the 509th time, you will
probably see bogus values being passed
to fread. Figure out where those
bogus values are coming from --
possibly set a conditional
breakpoint on the variable being set
to whatever the bogus value is.
Related
I hope this is not a turkey of a question. My conditional to detect EOF does not seem to work. The code continues through the EOF and processes within the statement. When I recreate the text, it appears appropriately, however the entire bmp with garbage code also prints out telling me the end of text flag never was encoded. I have placed a printf statement in the next else conditional, however it never enters that to print. I can not see what the problem is, if it is right in front of me, or something more ominous. Thanks as always!
/*******************************************************************************
* This code is to take a text document and using steganography techniques, hide
* the text within a bmp. It will take each character of the text, parse it into
* four 2 bit pieces and inject those bits into the two least significant bits
* of each pixel color (BGR) byte as well as the line padding.
******************************************************************************/
#include <stdio.h>
/*******************************************************************************
* getIntFromArray (borrowed from class notes). Takes unsigned character array
* and assembles/returns an int value using bit shifting with OR.
******************************************************************************/
int getIntFromArray(unsigned char bytes[])
{
int n =
bytes[0] |
bytes[1] << 8 |
bytes[2] << 16 |
bytes[3] << 24;
return n;
}
/*******************************************************************************
* bitWise. Take unsigned char pointer and character, parses the character
* using bitwise manipulation and injects 2 bits into the 2 least significant
* bits of each pixel color byte as well as padding.
******************************************************************************/
void bitWise(unsigned char* bytes, char character)
{
int i;
char tmpChar;
for(i = 0; i < 4; ++i)
{
tmpChar = character;
tmpChar &= 3;
bytes[i] &= 252;
bytes[i] |= tmpChar;
character = character >> 2;
}
}
int flag = 0;
int main(int argc, char **argv)
{
char *infilename = argv[1];
char *outfilename = argv[2];
unsigned char header[54];
FILE *in = fopen(infilename, "rb");/*Command line input.*/
FILE *out = fopen(outfilename, "wb");/*Command line input.*/
int pixelWidth;
int pixelHeight;
int i;
int j;
fread(header, 1, 54, in);/* read header into array */
pixelWidth = getIntFromArray(&header[18]);
pixelHeight = getIntFromArray(&header[22]);
fwrite(header, 1, sizeof(header), out);/* write header to output file */
for(i = 0; i < pixelHeight; ++i)/*Loop to read pixel data from bmp.*/
{
for(j = 0; j < pixelWidth; ++j)
{
unsigned char bytes[4];
unsigned char character = 0;
fread(&bytes, 1, 4, in);/*Reads sequentially pixel and padding bytes.*/
if(flag == 0)/*Breakout flag, initially set to 0.*/
{
character = getchar();/*Takes in characters from stdin.*/
if(character != EOF)/*Breakout if EOF.*/
{
bitWise(bytes, character);
}
else
{
bitWise(bytes, 0);/*Sets end of hidden text with 4 bytes LSB to 0.*/
flag = 1;
}
}
fwrite(&bytes, 1, 4, out);
}
}
fclose(in);
fclose(out);
return 0;
}
You are assigning signed int to an unsigned int. The result will be not what you expect it to be. It will be a value with all bits set to 1. (EOF has value -1 so it is signed).
Long story short it should be int. Simple int character would serves the purpose.
Also another thing getchar() returns int.
int getchar(void);
There are few other things to do :-
fread return value should be checked.
size_t fread(void * restrict ptr,size_t size, size_t nmemb,FILE * restrict stream);
The fread function returns the number of elements successfully
read, which may be less than nmemb if a read error or end-of-file
is encountered. If size or nmemb is zero, fread returns zero and
the contents of the array and the state of the stream
remain unchanged
Another thing is to check the return value of fopen(). In case of failure the return value would be NULL.
There are two serious but common problems here which lead me to believe you're not reading a reputable book, or you're having serious problems with it, as the reputable book would cover those problems in early chapters.
Perhaps we should look at some other options, since whatever you're using now clearly isn't working for you. You could've been reading your book all of this time you were struggling with trial and error, and your book should have guided you nicely past these common issues.
The bottom line is: you need to respect return values.
Don't try to convert return values before you check them. In unsigned char character; character = getchar(); you're converting from what the books and getchar manual says is an int to an unsigned char, before you try to check it against EOF. That conversion may result in loss of data. Do you wonder which data you've lost?
If you're struggling to understand K&R2E or a manual, you should write a question about that which you don't understand, rather than moving on, confused, to write code which relies upon guesswork. Any guesswork is dangerous in languages such as C.
You should also check the return value of fread, and I'd expect to see the size parameter passed 54 and the count parameter passed 1 in your case. That way, you can treat occasions when fread only reads fifty-three bytes (or fifty-two, or fifty-one) as though it's the end of input, rather than treating that unexpectedly short input as though it's the expected size. Like the getchar manual, you can find out all about fread from the fread manual.
Ohh, and nowadays I get a whole lot of "but the Youtube videos" responses. Youtube isn't a good substitute for a reputable book. Anybody can jump on there and "just wing it", and the video you watch will likely be equally flawed as your own guesses. A reputable book, on the other hand, has had thousands of hours put into planning, peer reviewing, testing (on students, since these books are usually written by professors who also have classes to teach) and refactoring (based on the testing, to better deal with "hiccups" that occur).
The choice should be obvious, and the proof is in the eating of the pudding. If you've tried the "youtube" or "try it and see" methods, they're not working for you; what you see before you now is the result of that. Try something else. Good luck!
I'm trying to write a number (randomly generated) to a file using a loop for some number of iterations. I have:
while (i++ < number) {
n = randno();
write(openFd, (char) n, sizeof(int));
}
The code compiles and runs. I can print 'n' to the screen, a file has been opened (code not shown), but there does not seem to be any data in the file when I try to open it using gedit. I want a file with one very large number of a certain length. Where am I going wrong? Any help is appreciated.
By the way, what is a true random function? Thank you.
The prototype for write() is:
ssize_t write(int fd, const void *buf, size_t count);
Notice the second arg is a pointer value, so it is a little bit surprising that passing that a random number there (which is what you have done1) does not seg fault.
Anyway, if what you want to do is do is write the number as text, you need to put it into a string first:
int n = randno();
char buffer[16]; // Will hold any int value + `\0`
sprintf(buffer, "%d", n);
You can now use buffer with write:
write(openFd, buffer, strlen(buffer));
1. n is an int; casting it with (char) simply truncates it. Perhaps you meant (char*)&n, which will work to write out the number, but in binary, not text! I presume that's not what you want since you were examining the file in gedit.
You could also use fprintf if you have a FILE*
fprintf(fp, "%d",n);
By casting n to a char type you have created something that will randomly write the memory pointed to from a number between 0 to 255. Expanding what you are doing, all you would need is.
while (i++ < number) {
int n;
n = randno();
write(openFd, &n, sizeof(int));
}
The & operator will pass a pointer to N instead of the n value...
A true random function is something that output real random numbers that are not computable.
/dev/random on linux as opposed to /dev/urandom.
For your file-write I would use ''fprintf''
I have been trying to make this program to convert a tga image for color into black and white. But i have no clue how to go about it. I am verry new to C and have yet to get the hang of the syntax and even proper usage of ubuntu.
I think my problem is somthing with tha tga files header cant be read. Because the result i get when trying this program on a tga file is an unopenable picture with no height. "height = 0".
Is there some good links for one to read up on C?
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct pixel {
uint8_t r, g, b, a;
};
static uint8_t *load_image(char *filename, int *sizex, int *sizey)
{
uint8_t *image;
char buf[512];
char *bufptr;
int ret;
FILE *fp = fopen(filename, "r");
bufptr = fgets(buf, 512, fp);
ret = fscanf(fp, "%d %d\n", sizex, sizey);
bufptr = fgets(buf, 512, fp);
image = malloc(*sizex * *sizey * 4);
int i;
uint8_t *ptr = image;
for (i=0; i<*sizex * *sizey; ++i) {
ret = fread(ptr, 1, 3, fp);
ptr += 4;
}
fclose(fp);
return image;
}
static int save_image(const char *filename, uint8_t *image, int sizex, int sizey)
{
FILE *fp = fopen(filename, "w");
fprintf(fp, "P6\n%d %d\n255\n", sizex, sizey);
int i;
uint8_t *ptr = image;
for (i=0; i<sizex * sizey; ++i) {
fwrite(ptr, 1, 3, fp);
ptr += 4;
}
fclose(fp);
return 1;
}
void convert_grayscale(uint8_t *input, uint8_t *output, int sizex, int sizey)
{
// Y = 0.299 * R + 0.587 * G + 0.114 * B
int i;
for (i = 0; i < sizex * sizey; ++i)
{
struct pixel *pin = (struct pixel*) &input[i*4];
struct pixel *pout = (struct pixel*) &output[i*4];
float luma = 0.299 * pin->r + 0.587 * pin->g + 0.114 * pin->b;
if (luma > 255)
luma = 255;
uint8_t intluma = (int) luma;
pout->r = intluma;
pout->g = intluma;
pout->b = intluma;
pout->a = 255;
}
}
int main()
{
uint8_t *inputimg, *outputimg;
int sizex, sizey;
inputimg = load_image("image.tga", &sizex, &sizey);
outputimg = malloc(sizex * sizey * 4);
convert_grayscale(inputimg, outputimg, sizex, sizey);
save_image("output.tga", outputimg, sizex, sizey);
}
(Personal note: A longer answer after reading Why Stackoverflow sucks. That should be Required Reading for everyone who gets Moderator privileges.)
The problem is your load_image code seems designed to read PPM (ASCII-based) images:
Each PPM image consists of the following:
1. A "magic number" for identifying the file type. A ppm image's magic number is the two characters "P6".
2. Whitespace (blanks, TABs, CRs, LFs).
3. A width, formatted as ASCII characters in decimal.
4. Whitespace.
5. A height, again in ASCII decimal.
6. Whitespace.
7. The maximum color value (Maxval), again in ASCII decimal. Must be less than 65536 and more than zero.
8. A single whitespace character (usually a newline).
9. A raster of Height rows [...]
-- your first fgets reads, then discards, the "magic number" line, followed by reading the width and height, and then discarding the "maxval" line.
It ought to work for PPM images (and you could rename this routine load_ppm_image) were it not for a single important issue: after all that ASCII stuff, you switch to fread, and so here is Warning #1.
Before opening your file, decide whether you are going to read exclusively ASCII text, or might need to read binary data.
The problem is that 'text mode' "w" converts certain characters when reading and writing into others. That's built-in behavior in all common C libraries; it attempts to fix the end-of-line characters mess that a previous generation of programmers left us with. Now, reading text files in text mode got a bit simpler, but reading binary data is impossible. You can't be sure you got exactly what was in the file.
Let's get on with Warning #2: not all file formats are the same.
The above routine works (mostly) for PPM images, but it will fail on TGA because its header is organized differently. The TGA header is described rather well here (a random pick of Google results).
The specification describes bytes, so first thing to do is change your fopen line to
FILE *fp = fopen(filename, "rb");
and, by the way, a good practice is to test if it was successful:
if (fp == NULL)
{
printf ("Opening the file '%s' failed\n", filename);
return NULL;
}
Then you can use fgetc or fread to read one or more bytes. Here comes Warning #3: use fread with care.
fread reads multiple bytes in the order in which they are stored into the file, and so you would think it may read an item such as width and height -- each a 2-byte integer value -- in one 'read' operation. But fread does not know the order of the bytes in your system (nor in the file itself), and so it could be it reads "lo-hi", as in the specification I pointed to, while in your computer the order of bytes in an integer is "hi-lo". To clarify: if the file contains this
80 00
and you read, then store, this with fread (&width,1,2, fp), these 2 bytes get stored into computer memory in that same order. The bytes are in Big-Endian order; the "large" byte is at the end. But if your computer happens to be a Little-Endian order system, you would not get the value 0x0080 = 128 but 0x8000 = 32768 instead!
The way to circumvent this is to read one byte at a time:
width = fgetc(fp) + (fgetc(fp)<<8);
will always read the data in the correct order: low first, then high. Only the sum gets stored (in the order for your system, but that's now irrelevant!).
With the above, I think I'm out of warnings. Using the TGA specifications as a guide, you can now open the file, read the header one byte at a time until you have all information that's needed, and continue to fread your raw image data into memory. You can safely use fread to read your image bytes three at a time because they will appear in the same order into memory as they were read (they are not integers or larger, so the "memory order" is not an issue).
A good approach to ensure you are reading the correct information is this:
Read one byte at a time, to prevent endianness issues.
Add a comment in your code detailing what it is
Print out the value
Check with the specifications if the value is allowed.
To get you started, after the fopen line (and the required check if it worked):
int idLength = fgetc(fp); /* length of id string after header */
printf ("id length: %u bytes\n", idLength);
int colorMapType = fgetc(fp); /* 0 = RGB */
printf ("color map type: %u\n", colorMapType);
if (colorMapType != 0)
{
printf ("unexpected color map type!\n");
return NULL;
}
int imageType = fgetc(fp); /* 0 = None, 1 = Indexed, 2 = RGB, 3 = Greyscale */
.. and so on. When the entire header has been read and you didn't encounter surprises, you are ready to set up things to read the actual image data. No changes needed there, your existing code should work just fine.
Post-edit: I see I used
int colorMapType = fgetc(fp);
where the 'color map type' is in fact a byte, not an integer. That is to allow a belt-and-suspenders approach. If you encounter the end of the file while you are reading the header, the code that fgetc returns is EOF. EOF cannot be stored into a char, because it is an integer value: 0xFFFFFFFF (more accurately: (int)-1). If you store it into a char, you cannot distinguish it from the perfectly okay value 0x000000FF (the value 255).
The belt-and-suspender approach is to check each and every single byte:
if (colorMapType == EOF)
{
printf ("encountered unexpected end of file!\n");
return NULL;
}
Overkill if you are working with a known file, and you know it's a valid TGA (you can view and edit it with bitmap editors), but if you ever plan to work on files of which you don't know if they are valid, you might need this.
I wrote this code to test to combine two files:
long getFileSize(char *filename)
{
FILE* fp=fopen(filename,"rb");
fseek(fp,0,SEEK_END);
long size=ftell(fp);
fclose(fp);
return size;
}
long lengthA = getFileSize(argv[1]);
long lengthB = getFileSize(argv[2]);
printf("sizeof %s is:%d\n",argv[1],lengthA);
printf("sizeof %s is %d\n",argv[2],lengthB);
void *pa;
void *pb;
FILE* fp=fopen(argv[1],"rb");
fread(pa,1,lengthA,fp);
fclose(fp);
FILE* fpn=fopen(argv[2],"rb");
fread(pb,1,lengthB,fpn);
fclose(fpn);
printf("pointerA is:%p;pointerB is:%p\n",pa,pb);
FILE *ff=fopen("test.pack","wb");
fwrite(pa,1,lengthA,ff);
fwrite(pb,1,lengthB,ff);
fclose(ff);
long lengthFinal = getFileSize("test.pack");
printf("Final size:%i\n",lengthFinal);
however I don't know if the data is equal to the returned value from getFileSize,the console print clearly says something wrong with it,but I can't figure it out:
sizeof a.zip is:465235
sizeof b.zip is 107814
pointerA is:0x80484ec;pointerB is:0x804aff4
Final size:255270
since I know the length of each file,I can then use fseek to restore them right? that's the idea I was thinking.
*pa and *pb need to point to some memory where the file content shall be read to.
So, do a malloc for these two buffers with lengthA*sizeof(char) and lengthB*sizeof(char) and pass these allocated buffers to fread:
pa = malloc(lengthA*sizeof(char));
pb = malloc(lengthB*sizeof(char));
...
fread(pa,sizeof(char),lengthA,fp);
...
fread(pb,sizeof(char),lengthB,fpn);
Furthermore, fread returns the number of items actually read. Also check this!
Excerpt from man fread:
fread() and fwrite() return the number of items successfully read or written (i.e., not the number of characters). If an error occurs, or the end-of-file is reached, the return value is a short item count (or zero).
Note that there's no real reason to load both source files into memory at once. Also, it's potentially very memory-inefficient to do so, since you're really reading all of the files in, and then all you do is write the contents out again.
A better algorithm, in my opinion, would be:
let C = a reasonable buffer size, say 128 KB
let B = a static buffer of C bytes
let R = the output file, opened for binary write
for each input file F:
open F for binary read
repeat
let N be the number of bytes read, up to a maximum of C
if N > 0
write N first bytes of B into R
until N = 0
close F
close R
This does away with the need to allocate buffers dynamically, you could just do char C[B] and have #define B (128 << 10).
The above assumes that reading from a file which has no more bytes to deliver returns 0 bytes.
Also note that by doing away with the need to load the entire file, you also no longer need to open each input file an extra time just to seek to the end in order to compute the file's size.
pa and pb are not pointing to valid memory.
char* pa = malloc(lengthA * sizeof(char));
char* pb = malloc(lengthB * sizeof(char));
Remember to free() when no longer required.
Check all return values from functions fopen(), fread(), fwrite(), etc.
I find fwrite fails when I am trying to write somewhat big data as in the following code.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int size = atoi(argv[1]);
printf("%d\n", size);
FILE* fp = fopen("test", "wb");
char* c = "";
int i = fwrite(c, size, 1, fp);
fclose(fp);
printf("%d\n", i);
return 0;
}
The code is compiled into binary tw
When I try ./tw 10000 it works well. But when I try something like ./tw 12000 it fails.(fwrite() returns 0 instead of 1)
What's the reason of that? In what way can I avoid this?
EDIT: When I do fwrite(c, 1, size, fp) it returns 8192 instead of larger size I give.
2nd EDIT: When I write a loop that runs for size times, and fwrite(c, 1, 1, fp) each time, it work perfectly OK.
It seems when size is too large(as in the first EDIT) it only writes about 8192 bytes.
I guess something has limited fwrite write up to fixed size bytes at a time.
3rd EDIT: The above is not clear.
The following fails for space - w_result != 0 when space is large, where space is determined by me and w_result is object written in total.
w_result = 0;
char* empty = malloc(BLOCKSIZE * size(char));
w_result = fwrite(empty, BLOCKSIZE, space, fp);
printf("%d lost\n", space - w_result);
While this works OK.
w_result = 0;
char* empty = malloc(BLOCKSIZE * sizeof(char));
for(i = 0; i < space; i ++)
w_result += fwrite(empty, BLOCKSIZE, 1, fp);
printf("%d lost\n", space - w_result);
(every variable has been declared.)
I corrected some errors the answers memtioned. But the first one should work according to you.
With fwrite(c, size, 1, fp); you state that fwrite should write 1 item that is size big , big out of the buffer c.
c is just a pointer to an empty string. It has a size of 1. When you tell fwrite to go look for more data than 1 byte in c , you get undefined behavior. You cannot fwrite more than 1 byte from c.
(undefined behavior means anything could happen, it could appear to work fine when you try with a size of 10000 and not with a size of 12000. The implementation dependent reason for that is likely that there is some memory available, perhaps the stack, starting at c and 10000 bytes forward, but at e.g. 11000 there is no memory and you get a segfault)
You are reading memory that doesn't belong to your program (and writing it to a file).
Test your program using valgrind to see the errors.
From that snippet of code, it looks like you're trying to write what's at c, which is just a single NULL byte, to the file pointer, and you're doing so "size" times. The fact that it doesn't crash with 10000 is coincidental. What are you trying to do?
As has been stated by others the code is performing an invalid memory read via c.
A possible solution would be to dynamically allocate a buffer that is size bytes in size, initialise it, and fwrite() it to the file, remembering to deallocate the buffer afterwards.
Remember to check return values from functions (fopen() for example).