I'm pretty close to losing my head here ;)
I'm developing a service that uses gsoap. I would like to return a mime response.
I have everything working, but when reading binary files, all kind of files like jpeg, pdf, etc... contains the \0 char several times over the data (if opened with notepad can see a lot of NUL).
So any code for reading a raw file fails miserably once it finds the end-of-file char. I have tried to replace the \0 but the file becomes incorrect to display.
I have also tried several methods including the example that comes with gsoap.
So resuming,
fstream generic code doesn't work.
for (i = 0; i < MAX_FILE_SIZE; i++)
{ if ((c = fgetc(fd)) == EOF)
break;
image.__ptr[i] = c;
}
doesn't work also
QFile::ReadAll works but when converting QString to char* the array is trimmed in the first NUL.
So, which is the best aproach to read an entire binary file? Its crazy how sometimes C++ at the basic.
Thanks in advance.
I have tried this as retnick suggested below
UrlToPdf urlToPdf;
urlToPdf.getUrl(&input, &result);
QByteArray raw = urlToPdf.getPdf(QString(result.data.c_str()));
int size = raw.toBase64().size();
char* arraydata = new char[size];
strcpy(arraydata, raw.toBase64().data());
soap_set_mime(this, "MIME_boundary", NULL);
if(soap_set_mime_attachment(this, arraydata, size, SOAP_MIME_BASE64, "application/pdf", NULL, NULL, NULL))
{
soap_clr_mime(this);
soapMessage = this->error;
}
but no luck... the mime response is bigger than the actual file...
David G Ortega
to read binary files use fread()
Once you read it treat it as an array of bytes not as a string. No string functions allowed.
EDIT: The gSOAP documentation section 14.1 explains how to send MIME attachments. I only refer to the relevant function (please read it all).
int soap_set_mime_attachment(struct soap *soap, char *buf_ptr, size_t buf_size,
enum soap_mime_encoding encoding,
const char *type, const char *id,
const char *location, const char *description);
char *buf_ptr is your buffer.
size_t buf_size is the length of your buffer.
So just do your QFile::ReadAll.
this gives you back a QByteArray. The QByteArray has the method
QByteArray QByteArray::toBase64 () const
this will return a
QByteArray base64image = QByteArray::toBase64(rawImage);
so now just do
soap_set_mime(soap, "MIME_boundary", "<boundary.xml#just-testing.com>");
/* add a base64 encoded image (base64image points to base64 data) */
soap_set_mime_attachment(soap,
base64image.data(), base64image.size(),
SOAP_MIME_BASE64, "image/jpeg",
"<boundary.jpeg#just-testing.com>", NULL, NULL);
I have not tested this but should be close to finished.
QFile::ReadAll works but when converting QString to char* the array is trimmed in the first NUL.
Are you sure it's actually trimmed or you just can't print/view the array in the debugger [since C-style strings are 0 terminated]?
If the QString itself is not enough for your needs you may want to convert it to a std::vector or similar using the range constructor or range assign, you'll have lots less grief towards the how much data the container holds.
EDIT:
Here's some sample code for fstream reading from a binary file:
std::ifstream image( <image_file_name>, std::ios_base::in | std::ios_base::binary );
std::istream_iterator< char > image_begin( image ), image_end;
std::vector< char > vctImage( image_begin, image_end );
The std::ios_base::binary is the most important part of the thing (similar to fopen/fread ["rb"] & probably QFile has something similar)
Also posting some sample code usually helps in getting the right answer.
HIH
I have the solution for this... As renick suggested I tried his idea but it failed without undestanding it so much... From a logical point of view recnick was right... bat the truth is that any king of string manipulation using QT QByteArray, std or mem is going to stop when findind the first \0 char, Qt QString can do it without problems but when converting it to c string (char*) the data will be again trimmed with the first \0
I found that using QDataStream::readRawData reads the file into a char* given the size to read. So thats how I accomplished the deal...
QFile file("test.pdf");
file.open(QIODevice::ReadOnly);
int size = file.size();
char* buffer = new char[size];
QDataStream stream(&file);
stream.readRawData(buffer, size);
soap_set_mime(this, "MIME_boundary", NULL);
if(soap_set_mime_attachment(this, buffer, size, SOAP_MIME_BINARY, "application/pdf", NULL, NULL, NULL))
{
soap_clr_mime(this);
soapMessage = this->error;
}
Note that in the line
if(soap_set_mime_attachment(this, buffer, size, SOAP_MIME_BINARY, "application/pdf", NULL, NULL, NULL))
I'm still using the size var instead of doing sizeof(buffer) or any other aproach since this one is going to trimm again the data qhen finding the first \0...
Hope this helps...
David G Ortega
Related
Context: I am trying to read the content of a PNG picture in C++ to send it later to my Android app. To do so, I open the file in binary mode, read it's content by chuncks of 512 bytes, then send the data to the app. I'm on Windows.
Issue: I use an ifstream instance and the readsome() function as shown below, and it returns me 512, which is what I expected since I asked to read 512 bytes. However, it seems that I am far from really having 512 bytes in my buffer, which confuses me. While I debug my programm step by step, the number of char in the buffer seems random, but is never 512 as expected.
Code:
int currentByteRead = 0;
std::ifstream fl(imgPath.toStdString().c_str(), ios_base::binary);
fl.seekg( 0, std::ios::end );
int length = fl.tellg();
char *imgBytes = new char[512];
fl.seekg(0, std::ios::beg);
// Send the img content by blocks of 512 bytes
while(currentByteRead + 512 < length) {
int nbRead = fl.readsome(imgBytes, 512); // nbRead is always set to 512 here
if(fl.fail()) {
qDebug() << "Error when reading file content";
}
sendMessage(...);
currentByteRead += 512;
imgBytes = new char[512];
}
// Send the remaining data
int nbRemainingBytes = length - currentByteRead;
fl.readsome(imgBytes, nbRemainingBytes);
sendMessage(...);
fl.close();
currentByteRead += nbRemainingBytes;
The length I get at the beginning is the correct one, and it seems there is no error. But it is as if not all the data was copied into the buffer during the readsome() call.
Questions: Did I misunderstood something about the readsome() function ? Is there something related to Windows causing this behaviour ? Is there a more appropriate way to proceed ?
I finally found a way to do what I wanted, and as suggested by David Herring I will put here my answer.
My thoughts about the issue: If I use a std::ifstream::pos_type variable instead of an int, the correct number of bytes is read and put in the buffer. This was not the case when using an int, as if the chars were only written in the buffer until a given (random ?) point. I am not sure to understand why this behavior occurred. My guess was that I had issues with '\n' characters, but the randomness of the final content of the buffer is still unclear for me.
Correction: This is the working code I finally reached nonetheless. Starting with this, I was able to do what I had in mind.
std::ifstream ifs(imgPath.toStdString().c_str(), std::ios::binary|std::ios::ate);
std::ifstream::pos_type pos = ifs.tellg();
int length = ifs.tellg();
std::vector<char> result(pos);
ifs.seekg(0, std::ios::beg);
ifs.read(result.data(), pos);
ifs.close();
I hope this will help others. Thank you David for your suggestions.
I hate to ask this because I think this must be very trivial. But as someone who is used to high-level-languages this is a real problem.
I got a C++ program which uses PDFium to generate an Image to a PDF. And i have a C# program which communicates with the C++ program via Named Pipes. The PDF file (Which is saved as a byte-array) gets transmitted by the pipe. And here is my Problem.
On the 374th Position of the stream is a NUL byte (00) and im too stupid to somehow reach the data after it.
Here is my Code:
LPTSTR lpszPipename2 = TEXT("\\\\.\\pipe\\myNamedPipe2");
hPipe2=CreateFile(lpszPipename2, GENERIC_READ, 0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
if(ReadFile( hPipe2, chBuf, dwBytesToRead, &cbRead, NULL))
{
PDFData = chBuf;
}
dwBytes to read is the size of the file and cbRead shows the correct number. But PDFData only contains the first 373 bytes. I checked that the data beyond the 373th position is there with the Immediate Window i just don't know how to process it.
I gotta put the Data into a char-array.
As I already said, i think this is very trivial. But although i know where the problem comes from, i have simply no idea how to fix it.
Many Thanks and Regards
Michael
Edit: The C#-Code. Its everything but perfect. But i'm very sure this Problem is on the C++ side.
public void SendRawData(byte[] data)
{
while (clientse == null || clientse.stream == null)
{ }
if (clientse.stream.CanWrite)
{
clientse.stream.Write(data, 0, data.Length);
clientse.stream.Flush();
}
}
private void ListenForClients()
{
while (true)
{
clientHandle = CreateNamedPipe(this.pipeName, DUPLEX | FILE_FLAG_OVERLAPPED, 0, 255, BUFFER_SIZE, BUFFER_SIZE, 0, IntPtr.Zero);
//could not create named pipe
if (clientHandle.IsInvalid)
return;
int success = ConnectNamedPipe(clientHandle, IntPtr.Zero);
//could not connect client
if (success == 0)
return;
clientse = new Client();
clientse.handle = clientHandle;
clientse.stream = new FileStream(clientse.handle, FileAccess.ReadWrite, BUFFER_SIZE, true);
if (ClientType == 0)
{
Thread readThread = new Thread(new ThreadStart(Read));
readThread.Start();
}
}
}
"Solution":
Actually this never was a real problem. I just got my wires crossed. While chBuf seemed after copying it into PDFData or when i read its value is VS to only have those 373 bytes. All ~20 kilobytes were copied to that position.
I knew that, but i didn't understand how the PDFium sources should know that if the string terminates after 373 chars.
Well... the PDFium-sources know it cause i have to pass the length. Which was determined by
size_t len = PDFData.length();
and was therefore of course only 373 bytes.
The null character '\0' is used by C/C++ to terminate char* strings. So any library function (i.e. strlen(), strncpy(), etc) will use the null character as an implicit end-of-string indicator. Your code is obviously doing this somewhere. Instead, use something more like memcpy() or a std::vector<char> with an explicit data length.
Have a look at string:assign (http://www.cplusplus.com/reference/string/string/assign/)
String assignment operator from char * uses the C-style end-of-string convention. You need the "buffer" assign call:
string& assign (const char* s, size_t n);
This will include any NULs.
That being said, vector of bytes may indeed be a better choice.
Actually this never was a real problem. I just got my wires crossed. While chBuf seemed after copying it into PDFData or when i read its value is VS to only have those 373 bytes. All ~20 kilobytes were copied to that position. I knew that, but i didn't understand how the PDFium sources should know that if the string terminates after 373 chars.
Well... the PDFium-sources know it cause i have to pass the length. Which was determined by
size_t len = PDFData.length();
and was therefore of course only 373 bytes.
I'm sorry that i bothered you with that stuff
I'm reading data from a file and trying to display the raw data as 2 digit hex strings.
I'm using the Qt framework, specifically the QTextEdit.
I've tried a bunch of different approaches and have almost accomplished what I want it to do, however it has some unexpected errors I don't know anything about.
Currently this is my implementation:
1) Read in the data:
ifstream file (filePath, ios::in|ios::binary|ios::ate);
if (file.is_open())
{
size = file.tellg();
memblock = new char [size+1];
file.seekg(0, ios::beg);
file.read(memblock, size);
file.close();
}
2) Create a single QString that will be used (because QTextEdit requires a QString):
QString s;
3) Loop through the array appending each successive character to the QString s.
int count = 0;
for(i=0;i<size;i++)
{
count++;;
s.append(QString::number(memblock[i], 16).toUpper());
s.append("\t");
if (count == 16)
{
s.append("\n");
count -= 16;
}
}
Now this works fine, except when it reaches a character FF, it appears as FFFFFFFFFFFFFFFF
So my main questions are:
Why do only the 'FF' characters appear as 'FFFFFFFFFFFFFFFF' instead?
Is there a way to convert the char data to base 16 strings without using QString::number?
I want this implementation to be as fast as possible, so if something like sprintf could work, please let me know, as I would guess that might be faster that QString::number.
QString can't be used for binary data. You should use QByteArray instead. It can be easily created from char* buffer and can be easily converted to hex string using toHex.
QByteArray array(memblock, size);
textEdit->setText(QString(array.toHex()));
QString::number doesn't have an overload that takes a char, so your input is being promoted to an int; consequently you're seeing the effects of sign extension. You should be seeing similar behavior for any input greater than 0x7F.
Try casting the data prior to calling the function.
s.append(QString::number(static_cast<unsigned char>(memblock[i]), 16).toUpper());
I have this code that works fine:
FILE *fp;
fp = fopen(filename.c_str(), "rb");
char id[5];
fread(id,sizeof(char),4,fp);
now I've changed something in my architecture, and instead the filename as fullpath of the file I have a char pointer that contains the data of the file.. so I don't need to read (fopen, etc..) but only to read the char* buffer...
how can I do this?
thanks in advance
If I'm understanding your question correctly, you want to access a four character ID somewhere in the middle of your buffer. The easiest way to do this is just to copy the data into a new buffer and add a NULL terminator.
size_t index = 0;
// ...
char id[5];
memcpy(id, &myData[index], 4);
id[4] = '\0';
index += 4;
You can then read through your buffer sequentially by updating the index value every time you read something.
char id[5];
strncpy(id,bfr,4);
id[4]='\0';
Where bfr is the buffer with your file data.
Also strongly advise you read the chapter on pointers and strings in K&R: The C Programming Language.
I'm attempting to use TinyXML to read and save from memory, instead of only reading and saving files to disk.
It seems that the documnent's parse function can load a char *. But then I need to save the document to a char * when I'm done with it. Does anyone know about this?
Edit: The printing & streaming functions aren't what I'm looking for. They output in a viewable format, I need the actual xml content.
Edit: Printing is cool.
Here's some sample code I am using, adapted from the TiXMLPrinter documentation:
TiXmlDocument doc;
// populate document here ...
TiXmlPrinter printer;
printer.SetIndent( " " );
doc.Accept( &printer );
std::string xmltext = printer.CStr();
A simple and elegant solution in TinyXml for printing a TiXmlDocument to a std::string.
I have made this little example
// Create a TiXmlDocument
TiXmlDocument *pDoc =new TiXmlDocument("my_doc_name");
// Add some content to the document, you might fill in something else ;-)
TiXmlComment* comment = new TiXmlComment("hello world" );
pDoc->LinkEndChild( comment );
// Declare a printer
TiXmlPrinter printer;
// attach it to the document you want to convert in to a std::string
pDoc->Accept(&printer);
// Create a std::string and copy your document data in to the string
std::string str = printer.CStr();
I'm not familiar with TinyXML, but from the documentation it seems that by using operator << to a C++ stream (so you can use C++ string streams) or a TiXMLPrinter class you can get an STL string without using a file. See TinyXML documentation (look for the "Printing" section)
Don't quite get what you are saying; your question is not clear. I'm guessing you are wanting to load a file into memory so that you can pass it to the document parse function. In that case, the following code should work.
#include <stdio.h>
The following code reads a file into memory and stores it in a buffer
FILE* fd = fopen("filename.xml", "rb"); // Read-only mode
int fsize = fseek(fd, 0, SEEK_END); // Get file size
rewind(fd);
char* buffer = (char*)calloc(fsize + 1, sizeof(char));
fread(buffer, fsize, 1, fd);
fclose(fd);
The file is now in the variable "buffer" and can be passed to whatever function required you to provide a char* buffer of the file to it.