My question is about binary file I/O. Suppose the following code is run:
#include <iostream>
#inclide <fstream>
int main(){
fstream out;
out.open("binfile.bin",ios::binary|ios::out);
if(!out.good()){
cout<<"ain't good"<<endl;
return EXIT_FAILURE;
}
out.seekp(3);
out<<char(74);
out.seekp(7);
out<<char(73);
out.close();
}
binfile.bin contains 00 00 00 4A 00 00 00 49, as expected. Can I somehow change that default value that is placed into the file if I don't specify what to output? I would like to replace 00 with something like 30, so that binfile.bin would contain 30 30 30 4A 30 30 30 49, is that doable? Of course I can loop through the file at the end and replace all 00s with 30s, but I'd like to avoid that.
You could write a wrapper for seekp and use that. Pseudocode:
my_seekp(fstream& f, int pos)
{
seek to end
endpos = position at end
if (pos > endpos)
{
int n = endpos - pos;
for (int i = 0; i < n; ++i)
f.put(char(30));
}
else
seek to pos
}
Instead of seeking and writing value as char using <<, what if you allocate a char buf[4] array, set your desired 'fill' value for all the elements, then just update the first/last element with the value you want and call out.write(buf, 4)?
Related
So I want to build a simple calculator program with hexadecimals as their input. But the input would also contain both the operation and the numbers to operate. The first two hexadecimals would be the math operations which are 01 = addition, 02 = subtraction, 03 = multiplication, and 04 = division, and the following four or more hexadecimals would be two hexadecimals (a max value of FF) to operate on.
An example input would be 010306 where it would be the operation 03 + 06 since 01 is addition and 03 and 06 being the numbers to operate on. I can manage implementing the calculator part but I can't seem to figure out how to chop up strings by 2 and convert them to hexadecimals. I kind of know a way but I do now know the correct syntax. Here is my thought code:
#include <iostream>
#include <iterator>
#include <string>
using namespace std;
int main() {
string input = "010306";
int exampleHex[input.length()/2];
for(int index = 0; index < exampleHex.length(); index+2){
exampleHex[index] = stoi(input[index] + stoi(input[index+1]), 0, 16);
}
/*
* or somethiing like this to somehow get
* int exampleHex[] = {01, 03, 06}
*/
return 0;
}
For the splitting, doing something like this should produce the result you want, so long as it's always a consistent 2 (or whatever) characters.
std::string nums = "010306";
int splitLength = 2;
std::vector<std::string> substrings;
for (int i = 0; i < nums.length(); i += splitLength)
{
substrings.push_back(nums.substr(i, splitLength));
}
Giving the resulting vector { "01", "03", "06" }
I don't know what happened to the post but it must have happened on my first edit.
I got this info to go in and work when it was in separate files. But I need it on one file.
I have looked in my text book and a bunch of other places but I can not find how to only get the text or characters out of the file.
I have got all the info into a single array but it looks like I will need to pull out each group piece by piece and put it where I want it but that looks slow, tedious and very susceptible to errors.
Johnson 85 83 77 91 76
Aniston 80 90 95 93 48
Cooper 78 81 11 90 73
Gupta 92 83 30 69 87
Blair 23 45 96 38 59
Clark 60 85 45 39 67
Kennedy 77 31 52 74 83
Bronson 93 94 89 77 97
Sunny 79 85 28 93 82
Smith 85 72 49 75 63
If this looks familiar it is the same assignment as my previous post, now I just need to figure out how to parse this info and use it again.
Chances are, you'll need to take your input as string value and check through it to find the beginning of the numerical characters.
only after having separated the alphabetical part of the input string from it's numerical part, you start creating your target arrays.
this might help: How can I check if a string has special characters in C++ effectively?
/e: wording
There are multiple ways of doing it. You could read it into a string and process it manually based on spaces. Or you could use stringstream to extract numerical values into array/vector. That however, still requires you to remove the name before you do it.
Here is a little code that reads the file content into an unordered_map which is essentially a dictionary as defined in other languages.
void read_file(const std::string& path) {
std::ifstream in(path); // file stream to read file
std::unordered_map<std::string, std::vector<double>> map;
/*
* map structure to hold data, you do not have to use this.
* I am using it only for demonstration purposes.
* map takes string (name) as KEY and vector<double> as VALUE
* so given a NAME you can get the corresponding grades VECTOR
* i.e.: map["Johnson"] --> [85, 83, 77, 91, 76]
*/
std::string line;
while (std::getline(in, line)) { // read entire line
if (line == "") continue; // ignore empty lines
int last_alpha_idx = 0; // name ends when last alphabetic is encountered
for (size_t i = 0; i < line.size(); i++)
if (std::isalpha(line[i])) last_alpha_idx = i; // get index of last alpha
std::string name = line.substr(0, last_alpha_idx + 1); // name is from index 0 to last_alpha_idx inclusive (hence +1)
std::string numbers = line.substr(last_alpha_idx + 1); // array values the rest of the line after the name
std::stringstream ss(numbers); // this is an easy way to convert whitespace delimated string to array of numbers
double value;
while (ss >> value) // each iteration stops after whitespace is encountered
map[name].push_back(value);
}
}
You could read it into an array, the code will not change dramatically. I chose string as KEY and vector<double> as VALUE to form KEY/VALUE pairs for the dictionary (map).
As you can see in the code, it looks for the last alphabetic character in each line and takes its index to extract the name from the read line. Then it takes the rest of the string (just the numbers) and puts them into a stringstream which will extract each number individually in its inner loop.
Note: the code above supports having full names (e.g. "Johnson Smith 85 83 77 91 76").
I've got a small program on my Arduino, in which I use the HashMap lib from Wiring framework.
Anyways, the HashMap is of key type char *, and value type char * as well.
I read some keys and values from a file and I try to add them to the map. I read them as Strings, and then I use the .c_str() method to convert them into char *.
When I then print the map, I get very weird values, which I think might be garbage from the memory?
Anyone knows how can I fix that, and why that happens? I was thinking it might have to do something with the temporary values in the memory returned by .c_str(), but I'm not sure. Any help?
Here is the code: (st is just a string and I split it into two, where the : is).
String msgC = st.substring(0, st.indexOf(":") - 1);
const char* messageCode = msgC.c_str();
String msg = st.substring(st.indexOf(":") + 1);
const char* message = msg.c_str();
hashMap[messageCode] = message;
When I print the 'message' and 'messageCode' into the console, they are fine.
Output when I print the hashmap:
Key: rently wiping, hig& Value: B0 D8 0 BE A0 9B 86:Wipers currently wiping, hig&
Key: Value: ⸮pers currently wiping, high speed.
Key: Value: ⸮
EDIT:
When I use String types of the hashMap key and values, I get more strange behavior:
if (dataFile) {
String st;
while (dataFile.available() > 0) {
char s = char(dataFile.read());
if (s!='\n'){
st = st+s;
}
else{
String msgC = st.substring(0, st.indexOf(":") - 1);
String msg = st.substring(st.indexOf(":") + 1);
Serial.print(msgC + " " + msg);
hashMap[msgC] = msg;
st = "";
}
}
Now the first three pairs of msgC and msg are read and stored fine. But the rest get split mid sentence or are not loaded at all. The output from the Serial.print:
1D0 834 B0 D8 0 A0 A0 9B 8 Wipers are off.
1D0 834 B0 D8 0 A8 A0 9B 8 Wipers on, single wipe.
1D0 834 B0 D8 0 A1 A0 9B 8 Wipers are on, not wiping.
(Up until here it's ok)
currently wiping, low speed.
currently wiping, low speed.
tly wiping, medium speed.
tly wiping, medium speed.
urrently wiping, high speed.
urrently wiping, high speed.
Weird stuff. And this did not happen when I used char * in the HashMap, and I did not change anything else in the code. How come that changing the type of HashMap keys and values could affect this reading from a file?
You are adding only pointer to data into hashmap:
String msg = st.substring(st.indexOf(":") + 1);
const char* message = msg.c_str();
hashMap[messageCode] = message;
Once msg is destroyed, the memory pointed by message becomes free for usage, so it is Undefined Behavior.
Your hash map should actually contains String values instead of char*.
Have an ASCII text file with some integer numbers in it, each separated by a space, and sometimes the numbers go on to a new line. For example:
// my_file.txt
23 45 973 49
44 1032 33 99
43 8 4 90824
I want to read 100 of these numbers into an array of "ints". Thus far, I have the following code:
int x[100];
fstream file_in("my_file.txt", ios::in);
for (int i = 0; i < 100; i++)
{
file_in >> x[i];
}
However, I now want to do a couple of other things that I am not sure about.
What if I want to just read the entire file in, without having to go through the loop? If this was binary data, I know that I can just write file_in.read((char*)&x[0], 100 * sizeof(int)). But how can I do this with an ASCII file?
What if I want to skip the first 3 numbers in the file, and start reading from the fourth? Again, if this was binary data, I could just use file_in.seekg(3 * sizeof(char)). But I don't know how to skip in an ASCII file.
No raw loops!
Reading the entire file:
#include <fstream>
#include <iterator>
#include <vector>
int main()
{
std::ifstream f("data.txt");
std::vector<int> v(std::istream_iterator<int>(f), {});
Skipping over the first three:
v.erase(v.begin(), v.begin() + 3);
I am working on a program that is supposed to read in a file (each line in the file represents a hand of 13 cards) and evaluate each bridge hand.
I will ask my specific questions at the end, but because there is a good bit to this program I am going to include all the instructions so you get an idea of what is required.
Here is the text file that will be read in:
2C QD TC AD 6C 3D TD 3H 5H 7H AS JH KH
3C 4C 2D AC QC 7S 7C TD 9C 4D KS 8D 6C
2C 3C KC JC 4C 8C 7C QC AC 5C 9C 6C TC
5H 3S 4D KC 9S 3D 4S 8H JC TC 8S 2S 4C
2S 5D 6S 8S 9D 3C 2H TH
2H 6D %S 8S 7S 4D 3H 4S KS QH JH 5C 9S
2C QD TC AD 6C 3D TD 3C 5H 7H AS JH KD QS
2C QD TC AD 6C 3D TD 2C 5D 7H AS JH KD
2H 6D TS 8Z 7S 4D 3H 4S KS QD JH 5C 9S
With each pair representing a card (the value and the suit).
Legal values include:
2-9
T(10), A(Ace), K(King), Q(Queen), and J(Jack)
And suits:
C(Clubs), S(Spades), D(Diamonds), and H(Hearts)
Once the file is read in, each hand must be sorted first by suit and then by the rank within the suit (aces are high). When the sorting is complete, each hand must be evaluated using the following rules:
Aces = 4
Kings = 3
Queens = 2
Jacks = 1
voids (no cards in a suit) = 3
singletons (one card in a suit) = 2
doubletons (two cards in a suit) = 1
long suits (more than 5 cards in a suit) = 1 count for each card over 5 in number
After being evaluated, each hand should be displayed in the following format:
Example Input:
2C QD TC AD 6C 3D TD 3H 5H 7H AS JH KH
Example Output:
Clubs 10 6 2
Diamonds A Q 10 3
Hearts K J 7 5 3
Spades A
Points = 16
Here are a few specifics about what the program must include:
1. A data structure to hold cards in an ordered manner.
2. A function to read in the hand.
3. A function to evaluate the hand (with support functions).
4. A function to display the hand.
Here is what little code I've been able to come up with. In case it's not clear, the comments are steps I think will need to be done in order for the program to work properly. Right now all it does is open the file, and yes I will be removing the "File is Open" message, I just wanted to be sure the file was actually being open.
//#include <program3.h>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
//Create Array
//char bridgeHands[];
//Open file, if it doesn't exist, exit the program
ifstream bridgeFile;
bridgeFile.open("prog3.dat");
if(!bridgeFile) {
cerr << "Open Failure" << endl;
exit(1);
}
else {
//Read file into array
//Sort array
//Evaluate hands
//Display hands
cout << "File is open" << endl;
}
return 0;
}
I guess my specific question at the moment is this. How do I need to go about creating and loading the array? I have never worked with loading an array from input that is in pairs. Also, how does this work with the data structure?
I'm sure you can tell by now that I'm extremely new at this and am learning as I go (pretty much everything I know how to do in C++ is written in that code), so any help is greatly appreciated. Thank you for your time.
There are a lot of open questions in my mind. First and
foremost: should each hand be on a separate line, or is it just
the next 13 cards, regardless of line breaks. This changes the
way you read hands. In either case, your input file has errors
which must be detected: in the first case, the fifth and seventh
lines have an incorrect format, and in the second, the number of
cards isn't a multiple of 13, so there must be an error
somewhere.
Anyway, the proper way to approach this is to define types
(classes) for the cards and the hand, and defined a user defined
operator>> for each, with the operator>> for hands using the
one for cards. For example:
std::istream& operator>>( std::istream& source, Card& object)
{
char value;
char suit;
source >> std::ws; // skip leading whitespace.
source.get(value); // these do _not_ skip whitespace
source.get(suit);
if ( source ) { // no errors on input...
// Map the characters to the internal representation and write
// them into `object`. This operator may have to be a friend
// to do this.
//
// If one of the characters isn't legal:
// source.setstate( std::ios_base::failbit );
}
return source;
}
For the hand, if your input is line oriented, the best solution
is probably to use std::getline to read the line, then
std::istringstream to parse it. Make sure you check that
there is nothing but white space left once you've read the 13
cards. (If the input ignores line endings, then just reading 13
cards should be sufficient.) Regardless of the strategy, be
sure the check for errors after each read, before using the
values you've read. So you're loop (either on the
std::istringstream or the original source, depending) might
look something like:
int i = 0;
while ( i != 13 && source >> dest[i] ) {
++ i;
}
if ( i == 13 ) {
// Input of 13 cards succeeded...
}
Finally: You're input contains errors (probably intentionally,
to ensure that you test them correctly). Which means the
simplest form of the outer loop won't work correctly (since it
will stop at the first error). If we suppose line oriented
input globally, but the >> operator for hand ignores line
endings and just looks for the next 13 Card:
std::string line;
int lineNumber = 0;
while ( std::getline( source, line ) ) {
++ lineNumber;
std::istringstream parser( line );
if ( parser >> hand >> std::ws && parser.get() == EOF) {
// Line is good, hand contains the instance to be evaluated
} else {
std::cerr << "Input format error in line " << lineNumber << std::endl;
}
}
Concering the condition in the if: we read a hand, then skip
white space; if that succeeds, we verify that we have reached
end of file (otherwise, there's extra garbage at the end of the
line). You can give a more detailed error message if you
separate these different operations out, although to indicate
which card is in error, you'll have to input the 13 cards
directly at this level, rather than using the >> for Hand.
One other suggestion: I would choose an internal representation
which made processing simple, with mapping functions for input
and output. This is probably two enums: one for values (with
the values in the order of their ranking), one for suits (also
in the order of their ranking). This will make the sorting
and the counting significantly easier, and mapping functions are
very easy: for small sets like these, nothing more than an array
with the legal representations: on input, a linear search, and
on output, just index. (Note that this may result in the value
2 having the numeric value 0 internally, since it will be
the first value of the enum.)
Use the ifstream::getline() function for reading and parsing the file. At this link you also find a nice example how you might read the file directly into a std::array:
#include <iostream>
#include <sstream>
#include <vector>
#include <array>
int main()
{
std::istringstream input("abc|def|gh");
std::vector<std::array<char, 4>> v;
// note: the following loop terminates when std::ios_base::operator bool()
// on the stream returned from getline() returns false
for (std::array<char, 4> a; input.getline(&a[0], 4, '|'); ) {
v.push_back(a);
}
for (auto& a : v) {
std::cout << &a[0] << '\n';
}
}
But take a close look whether this is suitable for your case. As an alternative you could omit the last parameter of getline so you really get the lines one by one. Then you'd have to parse these lines using std::string::find() and std::string::substr().