Width as a variable when using fscanf [duplicate] - c++

This question already has answers here:
How to prevent scanf causing a buffer overflow in C?
(6 answers)
Closed 6 years ago.
I am trying to read in a certain portion of a file and that amount of data is different per line but I know how how many bytes of info I want. Like this:
5bytes.byte1byte2byte3byte4byte5CKSum //where # of bytes varies for each line (and there is no period only there for readability)
Actual data:
05AABBCCDDEE11
03AABBCC22
04AABBCCDD33
So I want to have my width be a variable like this:
fscanf_s(in_file,"%variableX", &iData);
Is this possible, because right now I'm thinking I have to create a case statement?

Unfortunately, no, there's no modifier like '*' for printf that causes scanf to get its field width or precision from a variable. The closest you can come is dynamically creating the format string:
char format[8];
sprintf(format, "%%%dX", width);
fscanf(in_file, format, &iData);

If you really want to be able to adjust the fscanf format programmatically, you could try stack-allocating a string with enough space, and then generating the format like so:
e.g.
char formatString[100];
// writes "%max_size[0-9]", substituting max_size with the proper digits
sprintf(formatString, "%%%d[0-9]", MAX_SIZE);
fscanf(fp, formatString, buffer); // etc...

fscanf with %X will stop at a newline automatically, right? If the fields really are newline-terminated (as in your example), then can't you just call
fscanf(in_file, "%X", &iData);
and let fscanf figure out where the end is?

You might also consider using C++ streams.
#include <ifstream>
#include <iostream>
// open the file and create a file input stream
ifstream file("test.txt" , ios::in | ios::binary);
// loop through the whole file
while (ifs.good())
{
// extract one byte as the field width
unsigned char width;
file.read(&width, 1);
// extract width number of unformatted bytes
char * bytes = new char[width];
file.read(bytes, width);
// process the bytes
...
delete [] bytes;
// skip EOL characters if needed
// file.seekg(1, ios_base::cur)
}
file.close();
A simpler way if the newlines are included as you seem to indicate, would be to use getLine(). Check out http://www.cplusplus.com/reference/iostream/ifstream/ for more ways to use read(), get(), getLine() and lots of other great stream functions.

I think the simplest would be to use fread() like this:
fread(buffer, nbytes, sizeof(char), in_file);

Related

What is the best solution for writing numbers into file and than read them?

I have 640*480 numbers. I need to write them into a file. I will need to read them later. What is the best solution? Numbers are between 0 - 255.
For me the best solution is to write them binary(8 bits). I wrote the numbers into txt file and now it looks like 1011111010111110 ..... So there are no questions where the number starts and ends.
How am I supposed to read them from the file?
Using c++
It's not good idea to write bit values like 1 and 0 to text file. The file size will bigger in 8 times. 1 byte = 8 bits. You have to store bytes, 0-255 - is byte. So your file will have size 640*480 bytes instead of 640*480*8. Every symbol in text file has size of 1 byte minimum. If you want to get bits, use binary operators of programming language that you use. To read bytes much easier. Use binary file for saving your data.
Presumably you have some sort of data structure representing your image, which somewhere inside holds the actual data:
class pixmap
{
public:
// stuff...
private:
std::unique_ptr<std::uint8_t[]> data;
};
So you can add a new constructor which takes a filename and reads bytes from that file:
pixmap(const std::string& filename)
{
constexpr int SIZE = 640 * 480;
// Open an input file stream and set it to throw exceptions:
std::ifstream file;
file.exceptions(std::ios_base::badbit | std::ios_base::failbit);
file.open(filename.c_str());
// Create a unique ptr to hold the data: this will be cleaned up
// automatically if file reading throws
std::unique_ptr<std::uint8_t[]> temp(new std::uint8_t[SIZE]);
// Read SIZE bytes from the file
file.read(reinterpret_cast<char*>(temp.get()), SIZE);
// If we get to here, the read worked, so we move the temp data we've just read
// into where we'd like it
data = std::move(temp); // or std::swap(data, temp) if you prefer
}
I realise I've assumed some implementation details here (you might not be using a std::unique_ptr to store the underlying image data, though you probably should be) but hopefully this is enough to get you started.
You can print the number between 0-255 as the char value in the file.
See the below code. in this example I am printing integer 70 as char.
So this result in print as 'F' on the console.
Similarly you can read it as char and then convert this char to integer.
#include <stdio.h>
int main()
{
int i = 70;
char dig = (char)i;
printf("%c", dig);
return 0;
}
This way you can restrict the file size.

Reading text file in blocks-c++

string lineValue;
ifstream myFile("file.txt");
if (myFile.is_open()) {
//getline(myFile, lineValue);
//cout<<lineValue;
while (getline(myFile, lineValue)) {
cout << lineValue << '\n';
}
myFile.close();
}
else cout << "Unable to open file";
The txt file formate is like this
0 1
1 2
2 3
3 4
4 5
5 5
6 6
7 7
8 8
9 9
Above code is reading data from a text file line by line, but the text file size is quite large (10GB).
So how to read data from the file in chunks/blocks with less I/O and efficiently ?
If you are thinking of reading in large chunks of data then you will be using a technique called buffering. However, ifstream already provides buffering so my first step would be to see if you can get ifstream doing the job for you.
I would set a much larger buffer than the default in you're ifstream. Something like
const int BUFSIZE = 65536;
std::unique_ptr<char> buffer(new char[BUFSIZE]);
std::ifstream is;
is.rdbuf()->pubsetbuf(buffer.get(), BUFSIZE);
is.open(filename.c_str());
const int LINESIZE = 256;
char line[LINESIZE];
if (is) {
for (;;) {
is.getline(line, LINESIZE);
// check for errors and do other work here, (and end loop at some point!)
}
}
is.close();
Make sure your buffer lives as long as the ifstream object that uses it.
If you find the speed of this is still insufficient, then you can try reading chunks of data with ifstream::read. There is no guarantee it will be faster, you'll have to time and compare the options. You use ifstream::read something like this.
const int BUFSIZE = 65536;
std::unique_ptr<char> buffer(new char[BUFSIZE]);
is.read(buffer.get(), BUFSIZE);
You'll have to take care writing the code to call ifstream.read taking care to deal with the fact that a 'line' of input may get split across consecutive blocks (or even across more than two blocks depending upon your data and buffer size). That's why you want to modify ifstream's buffer as you're first option.
If and only if the text lines are the same length, you could simply read the file in using std::istream::read();.
The size of the block to read would be:
block_size = text_line_length * number_of_text_lines;
If you are brave enough to handle more complexity or your text lines are not equal lengths, you could read an arbitrary length of characters into a vector and process the text from the vector.
The complexities come into play when a text line overflows a block. Think of handling the case where only part of the sentence is available at the end of the block.

C++ fwrite corrupts binary file

I'm trying to open an exe file and place input taken from the user and replace existing data (overwriting it) of the same length at specific locations. I can do this with my code, but I'm seeing data corruption in other parts of my file. This is my first time with C++, I've tried looking at everything I could to help myself, but I'm at a loss. Only thing I can think is that its related to a null string char at the end of 'char test1[100];' (If I read the documentation right). But doesnt help my issue of resolving the issue. See linked image for example from Hex Viewer of Output and Original
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *key;
key=fopen ("Testfile.exe","r+b");
char test1[100];
char test2[100];
printf("Test data to input:");
fgets(test1, sizeof test1, stdin);
printf("Second test data to input:");
fgets(test2, sizeof test2, stdin);
fseek (key,24523,SEEK_SET); //file offset location to begin write
fwrite (test1,1,sizeof(test1),key);
fseek (key,24582,SEEK_SET); //file offset location to begin write
fwrite (test2,1,sizeof(test2),key);
fseek (key,24889,SEEK_SET); //file offset location to begin write
fwrite (test2,1,sizeof(test2),key);
fclose(key);
printf ("Finished");
return(0);
}
After my initial edits, I was still fighting with a Null Terminator being written at the end of my string (and thus affecting operation of the edited exe file). After a bit more reading this is my final solution that works as intended without any weird data being written. I used scanf ("%10s") to ensure only my string was being used and to get rid of any Null Terminator. Does anyone see anything majorly wrong here or improvements to be made? Eventually I'd like to implement string length checking to ensure proper length was input by user. Thanks for everyone's help.
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *key;
key=fopen ("test.exe","r+b");
char test1[10];
char test2[32];
printf("Input Test1 data:");
scanf ("%10s",test1); //only read 10 Chars
printf("Input test2 data:");
scanf ("%32s",test2); //only read 32 Chars
fseek (key,24523,SEEK_SET); //file offset location to begin write
fputs (test1,key);
fseek (key,24582,SEEK_SET); //file offset location to begin write
fputs (test2,key);
fseek (key,24889,SEEK_SET); //file offset location to begin write
fputs (test2,key);
fclose(key);
printf ("Finished");
return(0);
}
It looks like you're to write a string into the exe file but actually you're writing a string padded with garbage values up to a length of 100 bytes.
If you just want to write the string, replace fwrite with fputs.
sizeof(array) gives the allocated size of the static array (100 in this case) , not the string length. string length is done via strlen() which doesn't include the terminating NULL character.
You have two problems.
First: you're writing 100 byte buffers which have not been initialized except via fgets()... everything not put in there by fgets() is whatever happened to be in memory (on the stack in this case).
Second: you're writing 100 bytes with each write however your seek does not advance to at least 100 bytes later, meaning the second write() in this snippet partially overwrites the first.

Quickly convert raw data to hex string in c++

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());

Best way to compare input values to read values from files

I am relatively new to c++ programming and I have hit one of my first major snags in all of this..
I am trying to figure out how to read a value/character from a generic ".txt" file that is on notepad. With that comparison I want to determine whether or not to read that entire line, but I can't seem to just read the single one or two digit number, I got it to read the whole line using { 'buffername'.getline(variable, size) } but when I try to change the 'size' to a specific number it gives me a comparison error saying that its invalid to switch to 'int' or 'char' (depending on how I declare the variable).
Any help is appreciated.
Thanks
int length = 2;
char * buffer;
ifstream is;
is.open ("test.txt", ios::binary );
// allocate memory:
buffer = new char [length];
// read 2 char
is.read (buffer,length);
//Compare the character and decide
delete[] buffer;
return 0;
You'll want to use an ifstream to get the value (ref 1).
Something like the following should work. Here I use a word of type std::string, but you can replace that with other types to read them (ie: int, double, etc...).
std::ifstream f("somefile.txt");
std::string word;
std::string line;
if(f >> word){
if(<the comparison>){
line = f.getline();
}
}
Here's an extended example of how to use the ifstream
First of all, for performance reasons it is a bad idea to read 1 byte at a time.
I suggest this alternative:
You would be better off reading in the whole line, and then using character array.
char variable[1000];
read your line in from the file into variable.
if (variable[1]=='c') { printf("Byte 2 (remember 0 offset) is compared for the letter c";}
getting a 2 digit #
number=((variable[3]-48)*10)+(variable[4]-48);
You have to subtract 48 because in ASCII the number 0 is 48.