getline item returning empty variable - c++

I'm trying to read in a text file and separate the lines into cities and locations. Everything is working for most of the lines, but for I'm getting a
terminate called after throwing and instance of 'std::invalid_argument'
what(): stoi
Aborted (core dumped)
After some investigating, I figured out that it's hanging up on the 2 in Lima, Peru. I could be that the getline function is giving it something it can't handle, but there are instances of exactly the same number in exactly the same position earlier in the document.
...
Hobart, Tasmania: 42 52 S 147 19 E
Hong Kong, China: 22 20 N 114 11 E
Iquique, Chile: 20 10 S 70 7 W
Irkutsk, Russia: 52 30 N 104 20 E
Jakarta, Indonesia: 6 16 S 106 48 E
Johannesburg, South Africa: 26 12 S 28 4 E
Kingston, Jamaica: 17 59 N 76 49 W
Kinshasa, Congo: 4 18 S 15 17 E
Kuala Lumpur, Malaysia: 3 8 N 101 42 E
La Paz, Bolivia: 16 27 S 68 22 W
Leeds, England: 53 45 N 1 30 W
Lima, Peru: 12 0 S 77 2 W
Lisbon, Portugal: 38 44 N 9 9 W
Liverpool, England: 53 25 N 3 0 W
London, England: 51 32 N 0 5 W
Lyons, France: 45 45 N 4 50 E
Madrid, Spain: 40 26 N 3 42 W
...
Here's the section of the code that I think is throwing the error. I can post more if needed, but I think this is the relevant part.
while(is_more_stuff_there(file_to_read))
{
getline(file_to_read, line);
// parse city
index = line.find(':');
city_name = line.substr(0 , line.find(':'));
istringstream position_stream(line.substr(index + 2 , line.find(':')));
cout << city_name << endl;
// initialize an array to store the parsed values from the position_string
string position_array[6];
string item;
int i = 0;
// fill the array, split by spaces
while (getline(position_stream, item, ' '))
{
position_array[i] = item;
i++;
cout << item << endl;
}
cout << position_array[4] << endl;
// initialize the position variables
lat_min = stoi(position_array[0]);
lat_sec = stoi(position_array[1]);
long_min = stoi(position_array[3]);
long_sec = stoi(position_array[4]);
// determine positivity of lats and longs
if (position_array[2] == "S") { lat_min *= -1; lat_sec *= -1; }
if (position_array[5] == "E") { long_min *= -1; long_sec *= -1; }
vertex city(city_name, lat_min, lat_sec, long_min, long_sec);
g.add_vertex(city);
}

There is a non-printing character in your text file, just before the 2 in question. you could find what exactly it is by using od -x (if you're on a unix box). Or simply remove the line and retype it.

One problem that I can see with your code is that the second parameter passed to the substr function seems wrong. It should be the length of the sub-string to extract but that need not coincide with the index of the :. You can simply leave the second parameter out to get the entire remaining sub-string.
std::istringstream position_stream(line.substr(index + 2));
If you only add 1 to index, your code will also parse inputs where there is no space after the colon.
Although not fundamentally wrong, the code could be simplified by using the C++ style extraction operators. You can read in your four fields directly from the stream.
int lat_min, lat_sec, long_min, long_sec;
std::string ns, we;
position_stream >> lat_min >> lat_sec >> ns >> long_min >> long_sec >> we;
Then continue processing them with whatever logic is required.

Related

2nd Variation on Caesar Cipher (Having problems with splitting final string into parts)

Prompt:
In this country soldiers are poor but they need a certain level of secrecy for their communications so, though they do not know Caesar cypher, they reinvent it in the following way.
They use ASCII, without really knowing it, but code only letters a-z and A-Z. Other characters are kept such as.
They change the "rotate" each new message. This "rotate" is a prefix for their message once the message is coded. The prefix is built of 2 letters, the second one being shifted from the first one by the "rotate", the first one is the first letter, after being downcased, of the uncoded message.
For example if the "rotate" is 2, if the first letter of the uncoded message is 'J' the prefix should be 'jl'.
To lessen risk they cut the coded message and the prefix in five pieces since they have only five runners and each runner has only one piece.
If possible the message will be evenly split between the five runners; if not possible, parts 1, 2, 3, 4 will be longer and part 5 shorter. The fifth part can have length equal to the other ones or shorter. If there are many options of how to split, choose the option where the fifth part has the longest length, provided that the previous conditions are fulfilled. If the last part is the empty string don't put this empty string in the resulting array.
For example, if the coded message has a length of 17 the five parts will have lengths of 4, 4, 4, 4, 1. The parts 1, 2, 3, 4 are evenly split and the last part of length 1 is shorter. If the length is 16 the parts will be of lengths 4, 4, 4, 4, 0. Parts 1, 2, 3, 4 are evenly split and the fifth runner will stay at home since his part is the empty string and is not kept.
Could you ease them in programming their coding?
Example with shift = 1 :
message : "I should have known that you would have a perfect answer for me!!!"
code : => ["ijJ tipvme ibw", "f lopxo uibu z", "pv xpvme ibwf ", "b qfsgfdu botx", "fs gps nf!!!"]
By the way, maybe could you give them a hand to decode?
//Ends here
Issues faced:
Can't figure out how to divide the encoded string according to the given conditions. I understand the math behind how the division needs to be done, but can't convert it into code. I know that the num variable that I used needs to be decremented by 4 and the count variable should be incremented by 4 till the condition (num/4 > count) because the condition is such that if the string can be split in multiple ways, then we should do it such that the 5th part is of the longest length.
My code:
static vector<string> encodeStr(const string &s, int shift)
{
char pre = tolower(s[0]);
pre += shift;
string newS = "";
newS += tolower(s[0]);
newS += pre;
vector<string> ans;
for (int i = 0; i < (int)s.size(); i++)
{
if ((s[i] >= 65 && s[i] <= 90) || (s[i] >= 97 && s[i] <= 122))
{
char c = s[i];
c += shift;
newS += c;
}
else
newS.push_back(s[i]);
}
if (newS.size() % 4 == 0)
{
int parts = newS.size() / 4;
int start = 0;
while (start < (int)newS.size())
{
ans.push_back(newS.substr(start, parts));
start += parts;
}
}
else if (newS.size() % 5 == 0)
{
int parts = newS.size() / 5;
int start = 0;
while (start < (int)newS.length())
{
ans.push_back(newS.substr(start, parts));
start += parts;
}
}
else if (newS.length() % 5 != 0 && newS.length() % 4 != 0)
{
int num = newS.length();
int count = 0;
int start = 0;
while (num % 4 != 0)
{
num--;
count++;
}
while (num / 4 > count)
{
num = num - 4;
count = count + 4;
}
int x = newS.length() - count;
int parts = x / 4;
while (start < (int)newS.length() - count)
{
ans.push_back(newS.substr(start, parts));
start += parts;
}
ans.push_back(newS.substr((int)newS.size() - count, count));
}
return ans;
}
static string decode(vector<string> &s)
{
string s1 = "";
char check = ' ' - 1;
for (int i = 0; i < (int)s.size(); i++)
{
s1 += s[i];
}
char a = s1[1];
char b = s1[0];
int shift = a - b;
s1.erase(0, 2);
transform(s1.begin(), s1.end(), s1.begin(), [&](auto x)
{
if ((x >= 65 && x <= 90) || (x >= 97 && x <= 122))
return x -= shift;
else
return x;
});
for (int i = 0; i < (int)s1.size(); i++)
{
if (s1[i] == check)
{
s1[i]++;
}
}
return s1;
}
Code Output
First, we need to extract the important requirements from the story-text. An evaluation of the text leads to:
Caesar cypher
Based on ASCII
only upper and lowercase alpha letters shall be encoded ('A'-'Z', 'a'-'z')
The key (shift-information) shall be encoded and transmitted by along the message as 2 letter prefix. Taking the first letter of the text, unencrypted, as part 1 of the encrypted key and shifting this letter by the key and transmit it as part 2.
If possible the message will be evenly split between the five runners; if not possible, parts 1, 2, 3, 4 will be longer and part 5 shorter. The fifth part can have length equal to the other ones or shorter.
The 2-letter encrypted key shall be a prefix for parts of the split message.
For the following design, we can derive 3 major blocks:
We need a Caesar Cypher encryption/decryption algorithm
The key encryption/decryption must be implemented
The original message must be split according to requirements.
Let us start with the design for the Caesar Cypher encryption/decryption algorithm. We will take advantage of the ASCII code, where all characters have a defined associated numerical value. Please see the table below for the printable characters:
Hex Dec Bin Hex Dec Bin Hex Dec Bin
20 32 00100000 # 40 64 01000000 ` 60 96 01100000
! 21 33 00100001 A 41 65 01000001 a 61 97 01100001
" 22 34 00100010 B 42 66 01000010 b 62 98 01100010
# 23 35 00100011 C 43 67 01000011 c 63 99 01100011
$ 24 36 00100100 D 44 68 01000100 d 64 100 01100100
% 25 37 00100101 E 45 69 01000101 e 65 101 01100101
& 26 38 00100110 F 46 70 01000110 f 66 102 01100110
' 27 39 00100111 G 47 71 01000111 g 67 103 01100111
( 28 40 00101000 H 48 72 01001000 h 68 104 01101000
) 29 41 00101001 I 49 73 01001001 i 69 105 01101001
* 2a 42 00101010 J 4a 74 01001010 j 6a 106 01101010
+ 2b 43 00101011 K 4b 75 01001011 k 6b 107 01101011
, 2c 44 00101100 L 4c 76 01001100 l 6c 108 01101100
- 2d 45 00101101 M 4d 77 01001101 m 6d 109 01101101
. 2e 46 00101110 N 4e 78 01001110 n 6e 110 01101110
/ 2f 47 00101111 O 4f 79 01001111 o 6f 111 01101111
0 30 48 00110000 P 50 80 01010000 p 70 112 01110000
1 31 49 00110001 Q 51 81 01010001 q 71 113 01110001
2 32 50 00110010 R 52 82 01010010 r 72 114 01110010
3 33 51 00110011 S 53 83 01010011 s 73 115 01110011
4 34 52 00110100 T 54 84 01010100 t 74 116 01110100
5 35 53 00110101 U 55 85 01010101 u 75 117 01110101
6 36 54 00110110 V 56 86 01010110 v 76 118 01110110
7 37 55 00110111 W 57 87 01010111 w 77 119 01110111
8 38 56 00111000 X 58 88 01011000 x 78 120 01111000
9 39 57 00111001 Y 59 89 01011001 y 79 121 01111001
: 3a 58 00111010 Z 5a 90 01011010 z 7a 122 01111010
; 3b 59 00111011 [ 5b 91 01011011 { 7b 123 01111011
< 3c 60 00111100 \ 5c 92 01011100 | 7c 124 01111100
= 3d 61 00111101 ] 5d 93 01011101 } 7d 125 01111101
> 3e 62 00111110 ^ 5e 94 01011110 ~ 7e 126 01111110
? 3f 63 00111111 _ 5f 95 01011111 Del 7f 127 01111111
We observe that upper- and lowercase numbers only differ in one bit, which is equal to a distance of 32. We will use this property later.
Then, now, let us come to the core algorithm. Shifting letters.
The biggest problems are potential overflows. So, we need to deal with that.
Then we need to understand what encryption and decryption means. If encryption will shift everthing one to the right, decryption will shift it back to left again.
So, with "def" and key=1, the encrpyted string will be "efg".
And decrpytion with key=1, will shift it to left again. Result: "def"
We can observe that, for decryption, we simply need to shift by -1, so the negative of the key.
Important result: Encryption and decryption can be done with the same routine. We just need to invert the keys.
Let us look now at the overflow problematic. For the moment we will start with uppercase characters only. Characters have an associated code as shown in above ASCII table. For example, the letter 'A' is encoded with 65, 'B' with 66 and so on. Because we do not want to calculate with such big numbers, we normalize them. We simply subtract 'A' from each character. Then
'A' - 'A' = 0
'B' - 'A' = 1
'C' - 'A' = 2
'D' - 'A' = 3
You see the pattern. If we want to encrypt now the letter 'C' with key 3, we can do the following.
'C' - 'A' + 3 = 5 Then we add again 'A' to get back the letter and we will get 5 + 'A' = 'F'
That is the whole magic.
But what to do with an overflow, beyond 'Z'. This can be handled by a simple modulo division. Let us look at 'Z' + 1. We do 'Z' - 'A' = 25, then +1 = 26 and now, modulo 26 = 0. At the end again plus 'A' will be 'A'.
And so on and so on. The resulting formula is: (c - 'A' + key) % 26 +'A'
Next, what with negative keys? This is also simple. Assume an 'A' and key=-1.
Result will be a 'Z'. But this is the same as shifting positions 25 to the right. So, we can simply convert a negative key to a positive shift. The simple statement will be:
if (key < 0) key = (26 + (key % 26)) % 26;
With the above formular, there is even no need to check for a negative values. It will work for positive and negative values.
So, key = (26 + (key % 26)) % 26; will always work, for encrpytion and decrytion, for positive and negative keys.
Some extended information: Please have a look at any ASCII table and remeber, what we said above. We found out already, that any uppercase and lowercase character differ by 32. Or, if you look again to the binary representation:
char dec bin char dec bin
'A' 65 0100 0001 'a' 97 0110 0001
'B' 66 0100 0010 'b' 98 0110 0010
'C' 67 0100 0011 'b' 99 0110 0011
. . .
So, if you already know that a character is alpha, then the only difference between upper- and lowercase is bit number 5. If we want to know, if char is lowercase, we can get this by masking this bit. c & 0b0010 0000. Which is equal to c & 32 or c & 0x20.
If we want to operater on either uppercase or lowercase characters, then we can mask the "case" away. With c & 0b00011111 or c & 31 or c & 0x1F we will get always equivalents for uppercase charcters, already normalized to start with value 1.
char dez bin Masking char dez bin Masking
'A' 65 0100 0001 & 0x1b = 1 'a' 97 0110 0001 & 0x1b = 1
'B' 66 0100 0010 & 0x1b = 2 'b' 98 0110 0010 & 0x1b = 2
'C' 67 0100 0011 & 0x1b = 3 'b' 99 0110 0011 & 0x1b = 3
. . .
So, if we use an alpha character, mask it, and subtract 1, then we get as a result 0..25 for any upper- or lowercase character.
Again, I would like tor repeat the key handling. Positive keys will encrypt a string, negative keys will decrypt a string. But, as said above, negative keys can be transformed into positive ones. Example:
Shifting by -1 is same as shifting by +25
Shifting by -2 is same as shifting by +24
Shifting by -3 is same as shifting by +23
Shifting by -4 is same as shifting by +22
So,it is very obvious that we can calculate an always positive key by: 26 + key. For negative keys, this will give us the above offsets.
And for positve keys, we would have an overflow over 26, which we can elimiate by a modulo 26 division:
'A'--> 0 + 26 = 26 26 % 26 = 0
'B'--> 1 + 26 = 27 27 % 26 = 1
'C'--> 2 + 26 = 28 28 % 26 = 2
'D'--> 3 + 26 = 29 29 % 26 = 3
--> (c + key) % 26 will eliminate overflows and result in the correct new en/decryptd character.
And, if we combine this with the above wisdom for negative keys, we can write: ((26+(key%26))%26) which will work for all positive and negative keys.
If we now implement all above gathered wisdom in code, we can come up with bascically one C++ statement for the whole encryption and decryption, using std::transform:
std::string caesar(const std::string& in, int key) {
std::string res(in.size(), ' ');
std::transform(in.begin(), in.end(), res.begin(), [&](char c) {return std::isalpha(c) ? (char)((((c & 31) - 1 + ((26 + (key % 26)) % 26)) % 26 + 65) | (c & 32)) : c; });
return res;
}
This will do, what we described above:
(c & 31) - 1 will normalize a character. Meaning, convert to uppercase and to a range of 0-25
((26 + (key % 26)) % 26)) % 26 will do the key shift.
+ 65 will convert the nomalized value (0-25) back to a letter ('A'-'Z')
| (c & 32)) : c This will restore the lower case, if the letter was lower case before.
Now, we derived a complete algorithm and function for encryption and decryption using Caeser Cypher.
.
Next is splitting up the message in 5 parts.
The requirement was:
If possible the message will be evenly split between the five runners; if not possible, parts 1, 2, 3, 4 will be longer and part 5 shorter. The fifth part can have length equal to the other ones or shorter.
This can again be achieved with integer and modulo division. Basically, we will do an integer division to get the number of letters for each of the 5 chunks. Then we use a modulo division, to get the rest.
It is clear, but I will repeat it. If we do an integer division by 5, then the rest can be max 4. And this rest can then be distributed and added 1 by one to other chunks. Let us make an example using 23.
23 % 5 = 4 So, initially each chunk will be 4 letters long
Chunk 1: 4
Chunk 2: 4
Chunk 3: 4
Chunk 4: 4
Chunk 5: 4
------------
Sum: 20 // The rest, 3 is missing
Rest can be calculated with:
23 % 5 = 3 // So, we have a rest or remainder of 3. This we will distribute now:
Remainder = 3
Chunk 1: 4 + 1 = 5 3 - 1 = 2
Chunk 2: 4 + 1 = 5 2 - 1 = 1
Chunk 3: 4 + 1 = 5 1 - 1 = 0 Now everything was distributed
Chunk 4: 4 4
Chunk 5: 4 4
-------------------
Sum: 23
We now know, how chunksizes can be calculated.
For splitting the original strings into substrings, we can use the corresponding std::strings substr function, which is described here. You see, that we need to calculate a "start position" and a "length" value. Let us write a short piece of code for that.
#include <iostream>
#include <array>
#include <string>
constexpr std::size_t NumberOfChunks = 5u;
struct SDefs {
struct SDef {
std::size_t startPosition{}; // For substr function, we need a start position
std::size_t count{}; // and a count aof characters
};
std::array<SDef, NumberOfChunks> sDefs{}; // We have an array of 5 for this Positions and Counts
void calculate(const std::string& s) { // Calculation function
const size_t chunk = s.size() / NumberOfChunks; // Calculate the basic chunksize of all chunks
size_t remainder = s.size() % NumberOfChunks; // Calculate the rest that needs to be distributed
for (std::size_t startPos{}; SDef & sdef : sDefs) { // Calculate all positions and counts in a loop
sdef.startPosition = startPos; // Set startposition
sdef.count = chunk + (remainder ? 1 : 0); // And the chunk size, including potential distributed remainder
startPos += sdef.count; // Next startposition
if (remainder) --remainder; // And next remainder, if any
}
}
SDef& operator[](const std::size_t i) { return sDefs[i]; } // Easier accessibility
};
// Test code
int main () {
SDefs sdefs{};
std::string test{ "12345678901234567890123" };
sdefs.calculate(test);
for (std::size_t i{}; i < NumberOfChunks; ++i)
std::cout << "Chunk " << i+1 << " Start position: " << sdefs[i].startPosition << "\tCount: " << sdefs[i].count << '\n';
}
.
Finally: The transmission of the key. For encrypting, we simply take the first character of the text, or, in our case the substring. and then apply the encryption/decryption function on that to get the second letter.
And because the requirement was to use lower case characters, we set the 5th bit for the characters.
For decryption, in order to get the key, we need to subtract the second letter from the first. Thats all. Then we can invert it and use our encryption/decryption function again.
By the way. This method is dangerous and easy to hack, because you have always repeating letters at the beginning of a chunk.
For the final result, we need to add a little bit of house keeping code.
Then, lets_put everything together and create some program:
#include <iostream>
#include <array>
#include <string>
#include <algorithm>
#include <cctype>
constexpr std::size_t NumberOfChunks = 5u; // Maybe modified to whatever you need
// ---------------------------------------------------------------------------------------------------------
// Chunk calculator
struct SDefs {
struct SDef {
std::size_t startPosition{}; // For substr function, we need a start position
std::size_t count{}; // and a count aof characters
};
std::array<SDef, NumberOfChunks> sDefs{}; // We have an array of 5 for this Positions and Counts
void calculate(const std::string& s) { // Calculation function
const size_t chunk = s.size() / NumberOfChunks; // Calculate the basic chunksize of all chunks
size_t remainder = s.size() % NumberOfChunks; // Calculate the rest that needs to be distributed
for (std::size_t startPos{}; SDef & sdef : sDefs) { // Calculate all positions and counts in a loop
sdef.startPosition = startPos; // Set startposition
sdef.count = chunk + (remainder ? 1 : 0); // And the chunk size, including potential distributed remainder
startPos += sdef.count; // Next startposition
if (remainder) --remainder; // And next remainder, if any
}
}
SDef& operator[](const std::size_t i) { return sDefs[i]; } // Easier accessibility
};
// ---------------------------------------------------------------------------------------------------------
// Caesar Cypher
std::string caesar(const std::string& in, int key) {
std::string res(in.size(), ' ');
std::transform(in.begin(), in.end(), res.begin(), [&](char c) {return std::isalpha(c) ? (char)((((c & 31) - 1 + ((26 + (key % 26)) % 26)) % 26 + 65) | (c & 32)) : c; });
return res;
}
// Get a prefix, based on a given key
std::string getKeyPrefix(const std::string& s, const int key) {
std::string prefix("AA");
if (auto i = std::find_if(s.begin(), s.end(), std::isalpha); i != s.end()) {
prefix[0] = *i |32;
prefix[1] = (char)((((*i & 31) - 1 + ((26 + (key % 26)) % 26)) % 26 + 65) | 32);
}
return prefix;
}
// ---------------------------------------------------------------------------------------------------------
std::string test{"This was a major hack. What a pity that nobody will read or value it."};
int main() {
std::cout << "\nPlease enter a key: ";
if (int key{}; std::cin >> key) {
// Here we will store our encrypter and later decypted messages
std::array<std::string, NumberOfChunks> messages{};
// Here we will calculate the substrings properties
SDefs sdef{};
sdef.calculate(test);
// Encryption
for (std::size_t i{}; std::string& message : messages) {
// Get substring
const std::string sub = test.substr(sdef[i].startPosition, sdef[i].count);
// Encrypt sub string text
message = getKeyPrefix(sub, key) + caesar(sub,key);
// Debug output
std::cout << "Encrypted Message chunk " << i++ << ":\t" << message << '\n';
}
// Decryption
std::cout << "\n\nDecrypted Message:\n\n";
for (std::string& message : messages) {
// get key, inverted
int dkey = message[0] - message[1];
// Get substring
std::string sub = message.substr(2);
// Derypt sub string text
message = caesar(sub, dkey);
// Debug output
std::cout << message;
}
std::cout << "\n\n";
}
else
std::cerr << "\n\n***Error: Invalid input\n\n";
}
Have fun.
Checksum: fkems hajk eks ἀρμιν μοντιγνι qod krtd ghja

How do can I get rid of the blank lines that are created c++?

Edit:
Thank you all for the quick and helpful replies. I got it working now. It was because I had to reset the counter.
I have come to ask for help as my professor is not giving me the help I need. I am new to c++ and I am trying to program a program that displays all the integers from 1 to 100 that are divisible by 6 or 7, but not both. and I have to display 5 numbers per row. I got it working except I have blank lines forming in certain areas. I don't know if it's because of how I set up the counter or what.
Here is what I got.
#include <iostream>
using namespace std;
int main()
{
int counter = 0; // Counter for creating new lines after 5 numbers
for (int numRange = 1; numRange <= 100; ++numRange) // Starts the loop of number 1 to 100
{
if (numRange % 6 == 0 || numRange % 7 == 0) // Makes the numbers divisible by 6 and 7
{
cout << numRange << " "; // Displays the output of the divisible numbers
counter++; // Starts the counter
}
if (counter % 5 == 0) // using the counter to create new lines after 5 numbers displayed
{
cout << endl; // Creates a new line
}
}
return 0;
}
This is what is outputted:
6 7 12 14 18
21 24 28 30 35
36 42 48 49 54
56 60 63 66 70
72 77 78 84 90
91 96 98
and this is what it's supposed to look like
6 7 12 14 18
21 24 28 30 35
36 48 49 54 56
60 63 66 70 72
77 78 90 91 96
98
The problem that you're seeing is due to the fact that you are checking for "5 outputs" on every loop, rather than only on ones where a number has been output! So, to fix this issue (there are others), put the counter % 5 == 0 test inside the preceding if block:
for (int numRange = 1; numRange <= 100; ++numRange) // Starts the loop of number 1 to 100
{
if (numRange % 6 == 0 || numRange % 7 == 0) // Makes the numbers divisible by 6 and 7
{
cout << numRange << " "; // Displays the output of the divisible numbers
counter++; // Increments the counter
if (counter % 5 == 0) // Only need this if we have done some output!
{
cout << endl; // Creates a new line
}
}
}
Another problem is that, in this requirement:
that are divisible by 6 or 7, but not both
your code doesn't check for the "but not both" part (but that's not the 'title' question, and I'm not going to do all your homework in one fell swoop).

Does fstream access the hardrive with read and writes during compile time if used within a class temlpate?

Consider the following code snippet of this class template...
template<class T>
class FileTemplate {
private:
std::vector<T> vals_;
std::string filenameAndPath_;
public:
inline FileTemplate( const std::string& filenameAndPath, const T& multiplier ) :
filenameAndPath_( filenameAndPath ) {
std::fstream file;
if ( !filenameAndPath_.empty() ) {
file.open( filenameAndPath_ );
T val = 0;
while ( file >> val ) {
vals_.push_back( val );
}
file.close();
for ( unsigned i = 0; i < vals_.size(); i++ ) {
vals_[i] *= multiplier;
}
file.open( filenameAndPath_ );
for ( unsigned i = 0; i < vals_.size(); i++ ) {
file << vals_[i] << " ";
}
file.close();
}
}
inline std::vector<T> getValues() const {
return vals_;
}
};
When used in main as such with the lower section commented out with the following pre-populated text file:
values.txt
1 2 3 4 5 6 7 8 9
int main() {
std::string filenameAndPath( "_build/values.txt" );
std::fstream file;
FileTemplate<unsigned> ft( filenameAndPath, 5 );
std::vector<unsigned> results = ft.getValues();
for ( auto r : results ) {
std::cout << r << " ";
}
std::cout << std::endl;
/*
FileTemplate<float> ft2( filenameAndPath, 2.5f );
std::vector<float> results2 = ft2.getValues();
for ( auto r : results2 ) {
std::cout << r << " ";
}
std::cout << std::endl;
*/
std::cout << "\nPress any key and enter to quit." << std::endl;
char q;
std::cin >> q;
return 0;
}
and I run this code through the debugger sure enough both the output to the screen and file are changed to
values.txt - overwritten are -
5 10 15 20 25 30 35 40 45
then lets say I don't change any code just stop the debugging or running of the application, and let's say I run this again 2 more times, the outputs respectively are:
values.txt - iterations 2 & 3
25 50 75 100 125 150 175 200 225 250
125 250 375 500 625 750 875 1000 1125 1250
Okay good so far; now lets reset our values in the text file back to default and lets uncomment the 2nd instantiation of this class template for the float with a multiplier value of 2.5f and then run this 3 times.
values.txt - reset to default
1 2 3 4 5 6 7 8 9
-iterations 1,2 & 3 with both unsigned & float the multipliers are <5,2.5> respectively. 5 for the unsigned and 2.5 for the float
- Iteration 1
cout:
5 10 15 20 25 30 35 40 45
12.5 25 37.5 50 62.5 75 87.5 100 112.5
values.txt:
12.5 25 37.5 50 62.5 75 87.5 100 112.5
- Iteration 2
cout:
60
150 12.5 62.5 93.75 125 156.25 187.5 218.75 250 281.25
values.txt:
150 12.5 62.5 93.75 125 156.25 187.5 218.75 250 281.25
- Iteration 3
cout:
750 60
1875 150 12.5 156.25 234.375 312.5 390.625 468.75 546.875 625 703.125
values.txt:
1875 150 12.5 156.25 234.375 312.5 390.625 468.75 546.875 625 703.125
A couple of questions come to mind: it is two fold regarding the same behavior of this program.
The first and primary question is: Are the file read and write calls being done at compile time considering this is a class template and the constructor is inline?
After running the debugger a couple of times; why is the output incrementing the number of values in the file? I started off with 9, but after an iteration or so there are 10, then 11.
This part just for fun if you want to answer:
The third and final question yes is opinion based but merely for educational purposes for I would like to see what the community thinks about this: What are the pros & cons to this type of programming? What are the potentials and the limits? Are their any practical real world applications & production benefits to this?
In terms of the other issues. The main issue is that you are not truncating the file when you do the second file.open statement, you need :
file.open( filenameAndPath_, std::fstream::trunc|std::fstream::out );
What is happening, is that, when you are reading unsigned int from a file containing floating points, it is only reading the first number (e.g. 12.5) up to the decimal place and then stopping (e.g. reading only 12)
, because there is no other text on the line that looks like an unsigned int. This means it only reads the number 12 and then multiplies it by 5 to get the 60, and writes it to the file.
Unfortunately because you don't truncate the file when writing the 60, it leaves the original text at the end which is interpreted as additional numbers in the next read loop. Hence, 12.5 appears in the file as 60 5
stream buffers
Extracts as many characters as possible from the stream and inserts them into the output sequence controlled by the stream buffer object pointed by sb (if any), until either the input sequence is exhausted or the function fails to insert into the object pointed by sb.
(http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/)

File Reading Function only Reads First line then quits

Problem Description
Basically, Inside of my "Roster.h" header file, i have an array of "Student" objects from the student class ( which includes the functions changeScore, SetID, setTotal,setLetterGrade). In the function that will be attached below, it is only reading the first line of data and then quitting at the while condition. I've been staring at this issue for hours now and could use a second (or third) pair of eyes. Any criticism is also appreciated, as i know that i am not the most effective programmer.It should be noted that "m_studentnum",is private data that is initialized to 0 in the constructor. Thanks in advance!
Code
void Roster::readStudentRecord(string file)
{
ifstream in;
string studentID;
string line;
int ola, cla, quiz, homework, exam, bonus, total, final = 0;
in.open(file.c_str());
getline(in, line);
while (in >> studentID) {
in >> cla >> ola >> quiz >> homework >> exam >> bonus >> total >> final;
m_students[m_studentNum].Student::setID(studentID);
m_students[m_studentNum].Student::changeScore(Student::CLA, cla);
m_students[m_studentNum].Student::changeScore(Student::OLA, ola);
m_students[m_studentNum].Student::changeScore(Student::QUIZ, quiz);
m_students[m_studentNum].Student::changeScore(Student::HOMEWORK, homework);
m_students[m_studentNum].Student::changeScore(Student::EXAM, exam);
m_students[m_studentNum].Student::changeScore(Student::BONUS, bonus);
total = cla + ola + quiz + homework + exam + bonus;
m_students[m_studentNum].Student::setTotal(total);
if (total >= 90) {
m_students[m_studentNum].Student::setLetterGrade('A');
}
else if (total >= 80 && total < 90) {
m_students[m_studentNum].Student::setLetterGrade('B');
}
else if (total >= 70 && total < 80) {
m_students[m_studentNum].Student::setLetterGrade('C');
}
else if (total >= 60 && total < 70) {
m_students[m_studentNum].Student::setLetterGrade('D');
}
else {
m_students[m_studentNum].Student::setLetterGrade('F');
}
m_studentNum++;
}
}
Data file
-note that i am doing a getline to get read in the headline for the data column's
ID CLA OLA Quiz Homework Exam Bonus Total FinalGrade
c088801 10 15 4 15 56 5
c088802 9 12 2 11 46 2
c088803 8 10 3 12 50 1
c088804 5 5 3 10 53 3
c088805 3 11 1 10 45 0
c088806 8 14 2 11 40 -1
c088807 4 12 2 12 48 -2
c088808 10 10 3 11 36 0
c088809 8 8 3 11 39 0
c088810 6 9 4 9 47 3
c088811 8 7 3 13 41 3
c088812 4 11 3 11 37 1
The "Total" and "FinalGrade" columns are empty, and you unconditionally try to read them.
When you attempt to do that, the input will contain the "ID" from the next line, and as that is not an integer, leading to the failbit flag being set for the stream which causes the loop condition to be false and the loop to end.
One possible solution is to read the while line into a string, put that string into an std::istringstream object, and read the non-empty columns as you do now. Then try to read the possibly empty columns from the input string stream.
Another solution, if those columns are supposed to be empty, is to simply not read them.

read file line by line and store different variables

My text file look like this:
1 52 Hayden Smith 18:16 15 M Berlin
2 54 Mark Puleo 18:25 15 M Berlin
3 97 Peter Warrington 18:26 29 M New haven
4 305 Matt Kasprzak 18:53 33 M Falls Church
5 272 Kevin Solar 19:17 16 M Sterling
6 394 Daniel Sullivan 19:35 26 M Sterling
7 42 Kevan DuPont 19:58 18 M Boylston
8 306 Chris Goethert 20:00 43 M Falls Church
9 262 James Sullivan 20:12 28 M Sterling
10 348 Bill Gaudere 20:17 54 M Hudson
11 13 Travis Wheeler 20:23 31 M Clinton
12 69 Eric Anderson 20:34 54 M Clinton
13 341 Alex Teixeira 20:46 0 M Clinton
14 112 James Long 20:50 38 M 0
15 279 Nate Richards 21:31 17 M Berlin
......................................................
There are eight columns, separated by 'tabs', except the first name and last name is separated by a space.
I must have eight different types of variables.
int a;
int b;
string c;
string d;
string e;
int f;
char g;
string h;
I need to read the file line by line and cout every line's a, b, c, d, e, f.
I also need those variables for later use.
So, I tried this:
std::ifstream infile("text.txt");
int a;
int b;
string c;
string d;
string e;
int f;
char g;
string h;
while(infile>>a>>b>>c>>d>>e>>f>>g>>h)
{
cout <<"C is: "<<c<<endl; // just to see if the loop is working.
}
I don't need arrays and vectors to store those variables, I have a linked structure. Right now, I just need a way to read the file and store those strings and integers into variables.
But it's not working, lol. Don't know why. I also thought about using getline, something like this:
while(getline(infield, s)):
But, isn't this essentially just giving me one big fat line, with all strings and integers smashed together.
Your approach works exactly as you want it to when testing it on my machine, except for the fact that it will stop at the third entry:
3 97 Peter Warrington 18:26 29 M New haven
This is because of the space in New haven, which will fail the while condition as it fails to be copied into the integer field, a on the next iteration. If you want to keep this structure, perhaps put underscores in place of spaces. Otherwise move to parsing it line by line, perhaps with the std::regex library.
For example, changing the location string to be seperated by underscores instead of spaces results in finding all 15 entries. To change the underscores back into spaces we can use std::replace, so the body of your while loop would look lile:
std::cout <<a<<" "<<b<<" "<<c<<" "<<d<<" "<<e<<" "<<f<<" "<<g<<" ";
std::replace( h.begin(), h.end(), '_', ' ' );
std::cout<<h<<"\n";
(make sure to include algorithm)
We now have all our entires printed!
To directly answer your original question, I'm guessing the file doesn't exist.
std::ifstream infile("text.txt");
if(!infile.is_open()) {
std::cout<<"Couldn't find file";
return 0;
}
// ..