Error by reading binary file with c++ - c++

I try to read in a binary file containing char and int & double after a header:
// open file
int pos = 0,n;
char str1,str2;
//string str;
ifstream fid(pfad.c_str(),std::ios::binary);
if (fid.good() != 1) {
printf(" ++ Error: The elegant bunch file %s doesn't exist.\n",pfad.c_str());
return 1;
}
// cut the header
while (pos<5) {
if (fid.eof()) {
printf(" ++ Error: elegant bunch file is strange\n");
return 1;
}
fid >> str1;
switch (pos) {
case 0: str2 = '&'; break;
case 1: str2 = 'd'; break;
case 2: str2 = 'a'; break;
case 3: str2 = 't'; break;
case 4: str2 = 'a'; break;
}
if (str1 == str2){
pos ++;
} else {
pos = 0;
}
}
// Read out the data
fid.seekg(19,ios_base::cur);
std::cout << fid.tellg() << std::endl;
fid >> n;
std::cout << fid.tellg() << std::cout;
printf("\n\n%i\n\n",n);
printf("\nOK\n");
return 0;
My reading char with fid >> str1 works just fine. If I try to do this with a int it produces somehow a strange behaviour. The output then gets
813
-10x6c4f0484
0
Whereby the first number is the position in the file and the second one should be the same, but it looks like a pointer to me. Can anybody maybe try to clarify me confusion?
Thanks already in advance.

std::operator>>(std::istream&, int&) tries to parse an integer from a stream of characters, it doesn't read binary data. You'll need to use the std::istream::read(char*, std::streamsize) function.

Related

Splitting a String and Reading Every Part

I'm trying to complete a program that reads from a file and calculates the GPA. Basically there are 16 sets of data with 3 types in every set - name, grades, and extra points.
Example text:
Bugs Bunny
A B+ B A- B B C+ A B A-
100
The problem I am getting is at the middle part of the string, when taking in the grades. I am trying to read the entire line of grades, then read each grade itself, like "A" then "B+". Basically read "A", the value is 3, add it to an accumulator, then move to the next letter grade until the newline character is reached.
I thought of using .get but that's for taking in values. I don't really understand how to process the grades from the string. I know a loop is used, however.
struct infoTaker
{
string theirName;
string theirGrade;
double theirDonation;
int totalValue;
};
int main( )
{
double donation;
char letter;
ifstream file;
string fullName, actualGrade, substring;
file.open("F://Yes/thing.txt");
for ( int i = 0; i < 16; i ++){
getline( file, fullName ); // getting the names
infoTaker person;
person.theirName = fullName;
cout << person.theirName << endl; // end of names section
getline(file, actualGrade); // gettting the entire line
person.theirGrade = actualGrade; // the string of grades
cout << letter << endl; // Don't know what to do here
file >> donation;
file.ignore( 3 , '\n');
person.theirDonation = donation;
cout << person.theirGrade << endl;
cout << person.theirDonation << endl;
double convertDoodahs = person.theirDonation / 2.0;
}
}
This is one way to do it by adding the contents you read in a file, or you can also just read that certain line of grades. Im guessing this will be more useful because you can then later retrieve the name and other info.
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
int main(){
std::vector<std::string> vec;
std::string temp;
std::string grades;
std::ifstream input("test.txt");
//add them to vector, and access them later
while(getline(input, temp)) vec.push_back(temp);
//read the grades and seperate them
std::stringstream ss(vec[1]);
while(ss >> grades){
std::cout << grades << "\n";
}
}
sample txt file
Bugs Bunny
A B C D+
100
output
A
B
C
D+
#include<iostream>
#include<string>
using namespace std;
int convert(char a,char b='\0')
{
int result = 0;
if(b == '\0')
{
switch(a)
{
case 'A':
result = 9;
break;
case 'B':
result = 9;
break;
case 'C':
result = 9;
break;
}
}else
{
switch(a)
{
case 'A':
if(b=='+')
result = 10;
else
{
result = 8;
}
break;
case 'B':
if(b=='+')
result = 10;
else
{
result = 8;
}
break;
case 'C':
if(b=='+')
result = 10;
else
{
result = 8;
}
break;
}
}
return result;
}
int getSum(string g)
{
int ans = 0;
int l = g.length();
for(int i=0;i<l;)
{
char a = g[i++],b='\0';
if(g[i]=='+'||g[i]=='-')
{
b = g[i++];
}
ans+=convert(a,b);
i++;
}
return ans;
}
int main()
{
string g = "A B+ B A- B B C+ A B A-";
int sum = getSum(g);
}
try this...

get string after getline()

I want to hold a string with spaces therefore I used getline() but after it I want to get another string(no spaces) if there is a -e for example and the string after it in s2, but since in my code I lose the dash when using getline() I can't seem to achieve what I'm trying to do. any suggestions would be really helpful.
//example input: -f name -b blah blah -e email
//looking for output:
//name
//blah blah
//email
string s,s1,s2;
char check_character;
while (cin.peek() != '\n')
{
if (cin.get() == '-')
{
check_character = cin.get();
switch(check_character)
{
case 'f':
cin >> s;
break;
case 'b':
if(cin.peek() != '\n')
getline(cin, s1, '-');
else if(cin.peek() =='\n')
getline(cin, s1);
break;
case 'e':
cin>> s2;
break;
}
}
}
cout << s << endl << s1 << endl << s2 << endl;
return 0;
}
A better option would be to do a single call to getline() then parse the "command" string. There are many options of achieving this, from a simple split() on "-" or find('-')
getline() extracts characters from is and stores them into str until the delimitation character delim is found or the newline character, '\n'.
If the delimiter is found, it is extracted and discarded (i.e. it is not stored and the next input operation will begin after it).
I'm going to make a couple assumptions here:
You never expect a '\n' except at the end of the input string, even after "-b" (which your code will currently read in)
You expect to only accept 1 of each type of argument (cause your current code will stomp any previous entries)
A regex_search will handle this nicely with the regex:
(?:\s*-f\s+(\w+)|\s*-b\s+([^-]+)|\s*-e\s+(\w+))*
Live Example
You'll need to start by reading from cin into a variable, for example string input. This could be done like:
getline(cin, input)
Once you have your input you can simply do:
if(smatch m; regex_search(input, m, regex{ "(?:\\s*-f\\s+(\\w+)|\\s*-b\\s+([^-]+)|\\s*-e\\s+(\\w+))*" })) {
if(m[1].length() > 0U) {
cout << "-f " << m[1] << endl;
}
if(m[2].length() > 0U) {
cout << "-b " << m[2] << endl;
}
if(m[3].length() > 0U) {
cout << "-e " << m[3] << endl;
}
}
Live Example
If you go the approach of reading in the entire line, and you do not want to use Boost program options, or getopts, you could parse the line yourself (as has been suggested). Here would be one way of doing it, as an alternative of parsing on the fly in your code:
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
using std::cout;
using std::endl;
using std::get;
using std::literals::string_literals::operator""s;
using std::make_tuple;
using std::string;
using std::tuple;
using std::vector;
static auto chunkLine(string const& line)
{
auto result = vector<string>{};
auto i = string::size_type{};
while (i != string::npos && i < line.size())
{
auto pos = line.find(" -", i);
auto count = pos == string::npos ? pos : (pos - i);
result.push_back(line.substr(i, count));
i = pos + (pos != string::npos ? 1 : 0);
}
return result;
}
static auto parseChunks(vector<string> const& chunks)
{
auto result = vector<tuple<string, string>>{};
for (auto const& chunk : chunks)
{
auto pos = chunk.find(" ");
if (pos != string::npos && chunk[0] == '-')
{
auto kv = make_tuple(chunk.substr(1, pos-1), chunk.substr(pos+1));
result.push_back(kv);
}
}
return result;
}
int main()
{
auto line = "-f name -b blah blah -e email"s;
auto keyValueTuples = parseChunks(chunkLine(line));
for (auto const& kv : keyValueTuples)
{
cout << get<1>(kv) << endl;
}
}
The way you parse the arguments could certainly be improved, but this answer is not about it. I think what you are looking for is to simply put the - char back into the stream after std::getline removed it. In this case you could just use .putback() method
if (std::cin.peek() != '\n')
{
std::getline(std::cin, s1, '-');
std::cin.putback('-');
}
I think you should put cin.ignore before typing getline as in your code:
`string s,s1,s2;
char check_character;
while (cin.peek() != '\n')
{
if (cin.get() == '-')
{
check_character = cin.get();
switch(check_character)
{
case 'f':
cin >> s;
break;
case 'b':
if(cin.peek() != '\n')
cin.ignore
getline(cin, s1, '-');
else if(cin.peek() =='\n')
cin.ignore
getline(cin, s1);
break;
case 'e':
cin>> s2;
break;
}
}
}
cout << s << endl << s1 << endl << s2 << endl;
return 0; `

Debug error when trying to read from file and output altered string

So I'm testing out a small program that reads a file containing a few lines of one or two characters each and, based on what characters are in the line, outputs directional arrows (Think DDR output style: "< ^ v >")
Just reading and outputting the lines as they appear in the text file works fine, but I can't edit the way they appear without getting a super generic debug error with no code or anything to tell me what the problem is. Is this just something I'm not allowed to do, or should I replace my string with an array?
Code below:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void output(string raw);
int main()
{
string content_line;
ifstream file;
file.open("level1.txt", ios::out);
if (file.is_open())
{
while (getline(file, content_line))
{
output(content_line);
}
file.close();
}
return 0;
}
void output(string raw)
{
string row = " ";
for (int i = 0; i <= raw.size(); i++)
{
switch (raw.at(i))
{
case 'l': row.at(0) = '<';
break;
case 'u': row.at(2) = '^';
break;
case 'd': row.at(4) = 'v';
break;
case 'r': row.at(6) = '>';
break;
}
}
cout << row << '\n';
}
The text file as it appears right now is just:
u
d
l
r
ud
lr
ul
dr

Read from file (ifstream) and write to file (ofstream) - problems with encoding

Note: I'm rather a c++ rookie.
I've the following problem:
I read from a file and want to replace string parts with other string parts. Simple task, should be easy to do. I did some experiments with setmode, but without success. I googled also imbue, but it didn't help me either.
There might be minor code issues, but please focus on the problem. The test data for conversion is given below:
int main(int argc, char** argv) {
stringstream strs;
//Choose output file
ofstream ofile;
string filename;
cout << "Ausgabepfad eingeben" << endl;
cin >> filename;
ofile.open(&filename[0]);
//Choose input file
ifstream ifile;
cout << "Quellpfad eingeben" << endl;
cin >> filename;
ifile.open(&filename[0]);
//Choose decoding - doesnt work, I know - That was for experiments
int mode = 0;
cout << "Decoding wählen\n1 für _O_TEXT\n2 für _O_BINARY\n3 für _O_U16TEXT\n4 für _O_U8TEXT\n5 für _O_WTEXT" << endl;
cin >> mode;
/* switch(mode){
case 1:
_setmode (_fileno(ifstream), _O_TEXT);
break;
case 2:
_setmode (_fileno(ifstream), _O_BINARY);
break;
case 3:
_setmode (_fileno(ifstream), _O_U16TEXT);
break;
case 4:
_setmode (_fileno(ifstream), _O_U8TEXT);
break;
case 5:
_setmode (_fileno(ifstream), _O_WTEXT);
break;
default:
cerr << "ungültige Codierung gewählt"
}
*/
//Choose search string and replacement string
ifile.seekg(0); //not necessary, I know
string searchstr = "";
cout << "Suchstring eingeben" << endl;
cin >> searchstr;
string fillstr;
cout << "Ersetzungsstring eingeben" << endl;
cin >> fillstr;
cout << fillstr;
int marker = 0;
if(searchstr.length()<1){
return 0;
}
//actual program
while(!ifile.eof()){
int counter = 0;
ifile.seekg(marker);
char current = ifile.get();
if(current==searchstr[0]){ //if the first search letter matches, seet, if the others do.
marker++;
counter++;
for(int i = 1; i < searchstr.length(); i++){
ifile.seekg(marker);
if(ifile.get()==searchstr[i]){
counter++;
marker++;
}else{
marker-i;
break;
}
}
if(counter == searchstr.length()){
ofile << fillstr;
cout << endl;
}
}else{
ofile << ifile.get();
cout << ifile.get();
}
marker++;
}
ifile.close();
ofile.close();
cout << endl;
return 1;
}
The following string
|-
should be replaced by
\n
The text (sample) is:
|[[Allgemeines Deutsches Kommersbuch:1|1]]
|1
|[[Abend wird's, des Tages (Körner)]]
|[[Die Eichen (Körner)]]
|-
|[[Allgemeines Deutsches Kommersbuch:2|2]]
|2
The program runs, but it doesn't work. The output is lots of numbers without any sense.
The output is lots of numbers without any sense.
The call ifile.get() returns a value of type int_type. Thus << outputs all these integer values.
Replace
ofile << ifile.get();
(doing formatted output of integers) with
ofile << char( ifile.get() );
or (preferred, using unformatted input and output)
ofile.put( ifile.get() );
It's almost always a better idea to express the problem in terms of standard streams, iterators and algorithms:
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
// lets get the logic right in terms of streams
void replace_all(std::ostream& dest, std::istream& source,
const std::string& search_for, const std::string& replace_with)
{
std::string line;
while (std::getline(source, line)) {
auto ipos = line.begin();
while (ipos != line.end())
{
ipos = std::search(ipos, line.end(), search_for.begin(),
search_for.end());
if (ipos != line.end())
{
auto start_index = std::distance(line.begin(), ipos);
line.replace(ipos, std::next(ipos, search_for.length()),
replace_with);
ipos = std::next(line.begin() + start_index + replace_with.length());
}
}
dest.write(line.data(), line.size());
dest.put('\n');
}
}
// now test
int main()
{
std::string search_for = "|-";
std::string replace_with = "";
std::istringstream source_stream("|[[Allgemeines Deutsches Kommersbuch:1|1]]\n"
"|1\n"
"|[[Abend wird's, des Tages (Körner)]]\n"
"|[[Die Eichen (Körner)]]\n"
"|-\n"
"|[[Allgemeines Deutsches Kommersbuch:2|2]]\n"
"|2\n");
std::ostringstream dest_stream;
replace_all(dest_stream, source_stream, search_for, replace_with);
std::cout << dest_stream.str();
return 0;
}
// todo: write a new main which asks for filenames, builds ifstreams and ofstreams, and calls replace_all()
expected output:
|[[Allgemeines Deutsches Kommersbuch:2|2]]
|2
Jewels-MacBook-Pro:play richardh$ ./replace.cpp
|[[Allgemeines Deutsches Kommersbuch:1|1]]
|1
|[[Abend wird's, des Tages (Körner)]]
|[[Die Eichen (Körner)]]
|[[Allgemeines Deutsches Kommersbuch:2|2]]
|2

how to read a long string from a file into an array or something similar?

So I have searched for quite a while and found nothing that solves my problem currently I have a mess of a code from my previous problem, can someone attempt to tidy this so it works or simply remake it, I have 4 different text files to read from depending on what it is so the switch statement was my best idea and I intend to keep that bit. Just to add this code does not work.. .and can't work out why not.
ifstream QuestionFile;
int i = 0;
switch (x){
case 1:
QuestionFile.open("Topic1 Questions.txt", ios::app);
break;
case 2:
QuestionFile.open("Topic2 Questions.txt", ios::app);
break;
case 3:
QuestionFile.open("Topic3 Questions.txt", ios::app);
break;
case 4:
QuestionFile.open("Topic4 Questions.txt", ios::app);
break;
}
stringstream buffer;
buffer << QuestionFile.rdbuf();
string test = buffer.str();
size_t pos1 = 0;
size_t pos2;
if (!QuestionFile)
{
cout << "Cannot load file" << endl;
}
else
{
if (QuestionFile.peek() != ifstream::traits_type::eof()) {
while (!QuestionFile.eof())
{
pos2 = test.find("|", pos1);
questions[i] = test.substr(pos1, (pos2 - pos1));
cout << questions[i];
i++;
}
QuestionFile.close();
}
}
Okay so solved this myself in the end was actually really simple... so just in case anyone else comes across this and doesn't find a simple answer I've done it like this instead.
ifstream QuestionFile;
int i = 0;
//switch statement for checking which text file I personally wanted to use as it
//depending on which the user was allowed to use.
switch (x){
case 1:
QuestionFile.open("Topic1 Questions.txt", ios::app);
break;
case 2:
QuestionFile.open("Topic2 Questions.txt", ios::app);
break;
case 3:
QuestionFile.open("Topic3 Questions.txt", ios::app);
break;
case 4:
QuestionFile.open("Topic4 Questions.txt", ios::app);
break;
}
if (!QuestionFile)
{
cout << "Cannot load file" << endl;
}
else
{
if (QuestionFile.peek() != ifstream::traits_type::eof()) {
while (!QuestionFile.eof())
{
//This simply gets which ever line you want and puts it into that part of the array
//and does this till the end of the array.
getline(QuestionFile, questions[i]);
i++;
}
QuestionFile.close();
}
}
Since you'd like to keep the switch statement, try this:
const char *filename;
size_t pos;
size_t oldPos;
stringstream buffer;
string test;
filename = NULL;
switch(x)
{
case 0:
filename = "Topic1 Questions.txt";
break;
case 1:
filename = "Topic2 Questions.txt";
break;
case 2:
filename = "Topic3 Questions.txt";
break;
case 3:
filename = "Topic4 Questions.txt";
break;
}
QuestionFile.open(filename, ios::app); // try opening the file
if(QuestionFile.is_open()) // if open succeeds...
{
buffer << QuestionFile.rdbuf();
test = buffer.str(); // read entire file into 'test'
pos = 0; // start at beginning
do
{
oldPos = pos;
pos = test.find("|", oldPos); // find position of next line
questions[i] = test.substr(oldPos, (pos - oldPos)); // put in array
i++;
}
while(pos != oldPos); // as long as pos2 changes.
QuestionFile.close();
}
else
{
cout << "Cannot load file" << endl;
}
Note: I have not allocated an array for you, so you still need to do this yourself.