Task: A text input as command-line argument is to be written to a file.
Issue: Some encoding conversion is happening somewhere, so the file doesn't contain the exact text which was input. e.g. s%3A_blabla becomes s:_blabla . (Some HTML kind of thing)
Environment: Visual Studio 2019, C++17
How is the command-line argument being supplied: Using Smart Command Line Arguments extension. Also read 'Edit 1'.
How is the input being processed:
I've tried two methods:
static std::vector<const char*> args;
for (size_t i = 0; i < __argc; i++)
{
args.push_back(__argv[i]);
};
..........
std::string filePath = "random.txt";
std::string final_write(args[i+1]); //some value of i is chosen using a for loop
std::cout<<final_write<<std::endl;//this will print "s:_blabla"
std::ofstream out(filePath);
out << final_write;
Second method:
#include <atlstr.h>
........
LPWSTR *szArgList;
int argCount;
szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
for (int i = 0; i < argCount; i++)
{
CString pathCString(szArgList[i]);
std::cout<<pathCString<<std::endl; //Here also it prints "s:_blabla" for appropriate i
}
What I know:
I don't think cout is at fault, because in first case the file writing also give the same transformed output, and if I explicitly modify the string and add "%3A" to it, the cout is able to display that normally. I've not tried writing to file in the second case, as cout tells that something fishy has already happened during input.
It might be the extension, or an encoding issue somewhere else, I'm not sure. Any help?
Edit 1: As suggested, I checked the behaviour after removing the command line extension. I took input from Properties->Debugging section->Command Arguments, and I still face the same issue. Disabled the extension too.
I was tasked to read 3 rows of 5 comma separated values from a text file, sum up each column, and store the result in an array called bins. I am struggling to read the ints from the text file as they are comma separated. I first need to clarify how to read just the ints.
My next thought was to store the ints from the file into an array called "calc", and use the index of each element to sum up the values. I would then store these results into the "bins" array.
Here is some code I have tried to read the comma separated ints yet I cannot seem to get it to work.
int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;
int calc[15] = {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o};
ifstream myfile;
myfile.open("values.txt");
for(int i = 0; i <= 15; i++)
{
myfile >> calc[i];
myfile.close();
a = calc[0];
b = calc[1];
c = calc[2];
d = calc[3];
e = calc[4];
f = calc[5];
g = calc[6];
h = calc[7];
i = calc[8];
j = calc[9];
k = calc[10];
l = calc[11];
m = calc[12];
n = calc[13];
o = calc[14];
cout << calc[i] << endl;
}
I am really new to working with code and I dont quite understand how to work with values in this manner. It is a simple task yet I cannot seem how to implement it with code.
I am really new to working with code and I dont[sic] quite understand how to work with values in this manner.
OK, I have several tips for you:
① separate your tasks
You ran into a hitch parsing the input in the supplied format, dealing with the comma. Parsing the supplied input files is a totally different problem from the real work, which is summing the columns. Write them separately in the code.
In general you should isolate the "real work" in its own function and have it take parameters as input and returns results as a function return value. The input and output are written separately.
That gives you the added bonus of automating the testing by calling the "work" function with built-in test cases. In this case, it allows you to defer figuring out the parsing. You just pass in test data for now, to get the "work" part working, and then you can come back to parsing the input. Then, when you do need help, it will be specific to "parsing comma separated values" and have nothing to do with why you want them.
② To handle groups of values, you use the array.
This means subscripting or iterating, using loops (or library algorithms) to take what you want to do, written once, and apply it to each value in the array.
Given arrays input and sum, you can accumulate the current row (input) into the running sum with code like this:
for (size_t i = 0; i < COLS; ++i) {
sum[i] += input[i];
}
overall program sketch
open the file
repeat three times:
read a row of input
accumulate the sum with the new input
print the results
Note, as explained in the first topic, that read a row and accumulate the sum are separate functions and separate sub-tasks to figure out. This is called top-down decomposition of a problem.
It's best to use parameters for input and return for output of the function, but for this simple task I'll just use a global variable. Passing/returning is probably harder than the task you are learning! Note though that this is unrealistic in that in real code you would not want to use global variables like this. However, you might turn this into an "object", which you'll learn later.
#include <fstream>
constexpr size_t ROWS = 3;
constexpr size_t COLS = 5;
int input[COLS];
int sum[COLS];
std::ifstream infile;
int main()
{
infile.open("values.txt");
// todo: check for success and feedback to the user if failed
// skipped: zero out the sum array. Global variable start at 0,
// but more generally, you would need to initialize this.
for (size_t row= 0; row < ROWS; ++row) {
read_row();
sum_row();
}
print_results();
}
The sum_row function is what you saw earlier.
Note that with top-down decomposition, you can stub out parts that you will work on later. In particular, you can have read_row return hard-coded result at first, or read from a different format, so you can test the overall program. Then, go back and get that part working for real.
Top-Down Decomposition is critical for any kind of programming project.
Oops... most of your code is useless, and what remains is not really good.
Writing good programs is not a matter of adding C++ instructions one after the other. You must first design the overall structure of your program.
Here you have an input file containing lines of 5 comma separated values and want to compute an array (of size 5) containing the sum of the columns.
Let go from a high level
open the file
loop over the lines
loop 5 times:
read a field up to a comma (or end of the line)
decode that field into an int value
sum that value into an array
close the file
Ok, to be able to sum the values into an array, we will have to define the array before the loop and initialize its elements to 0.
Ok, C++ provide std::ifstream to read a file, std::getline to read a stream up to a delimiter (default being newline), std::istringstream to read the content of a string as an input stream and std::stoi to decode a string representing an int value.
Once this is done, but only after:
the program structure is clearly designed
the required tools from the standard library have been identified
it is possible to sit down in front of your keyboard and start coding.
BTW, this program will never require the declaration of 15 variables a to o nor an array of 15 integers: only int bins[5]...
It could be (tests omitted for brievety):
int bins[5] = {0}; // initializing the first value is enough, others will be 0
std::ifstream in("values.txt");
std::string line;
while (std::getline(in, line)) {
// std::cout << line << '\n'; // uncomment for debug
std::stringstream ss(line);
for(int& val: bins) { // directly loop over the bins array
std::string field;
std::getline(ss, field, ',');
val += std::atoi(field.c_str());
}
}
Of course, for a professional grade (or simply robust) program, every input operation should be followed by a test on the stream...
You can use the std::getline function within the string library to get each comma separated integer.
std::ifstream myfile("values.txt");
for(int i = 0; i < 15; i++)
{
std::string integer_as_string;
std::getline(myfile, integer_as_string, ',');
calc[i] = std::stoi(integer_as_string);
}
myfile.close();
Here we specify that the getline function will read a line of characters in the input until a , character is found. This string is assigned to the integer_as_string variable which will then be converted to an integer and gets assigned to the array.
Also note that i <= 15 will result in undefined behavior. You can further read it here: Wikipedia. And the myfile.close() function was set inside the for loop. This means that in every iteration, you will be closing the file. This is not needed. I think what your looking for is something like this.
std::ifstream myfile("values.txt");
for(int i = 0; i < 15; i++)
{
std::string integer_as_string;
std::getline(myfile, integer_as_string, ',');
calc[i] = std::stoi(integer_as_string);
std::cout << calc[i] << std::endl;
}
myfile.close();
a = calc[0];
b = calc[1];
c = calc[2];
d = calc[3];
e = calc[4];
f = calc[5];
g = calc[6];
h = calc[7];
i = calc[8];
j = calc[9];
k = calc[10];
l = calc[11];
m = calc[12];
n = calc[13];
o = calc[14];
References:
std::stoi
Why is "using namespace std;" considered bad practice?
First, your array have element with indices from 0 to 14, thus for(int i = 0; i <= 15; i++) should be for(int i = 0; i < 15; i++)
The loop itself might benefit from error-checking. What if file contains less than 15 values?
for(int i = 0; i <= 15; i++)
{
// you want check status of myfile here.
}
myfile >> calc[i] wouldn't work well with commas unless you add comma to a separator class for that stream. Albeit that can be done that's a little large change and one can use getline (see answers here for examples) instead to specify separator.
If you want named variables to refer to element of array, you can make them references and structurally bind them to array (or other tuple-like data structure, e.g. struct, etc.) provided you have access to C++17
int calc[15] = {};
auto& [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o] = calc;
a would become a reference to calc[0], b to calc[1] and so on.
I have the data in my vector. I am trying to write each vector value, say vector_name[0] into "examplezero.h" , vector_name[1] into "exampleone.h" and so on. The below code shows how I have created the files.
int co = 80;
string name ="example";
std::ofstream output_file[80];
for (int i = 0; i < co; i++)
{
output_file[i].open(name + std::to_string(i) + ".h");
output_file[i].close();
}
I am trying to iterate over my vector and trying to write to my files.
std::vector<string> rowname; //This has all the values
for (auto i = rowname.begin(); i != rowname.end(); ++i)
{
std::ostream_iterator<std::string> \
output_iterator(output_file[80], "\n");
std::copy(rowname.begin(), rowname.end(), output_iterator);
}
When I am trying to write to the files it is crashing. Can you let me know what's wrong? I know the basics of C++ and trying to learn the advanced concepts.
Thanks
Your program is likely crashing because you wrote this code:
std::ostream_iterator<std::string> \
output_iterator(output_file[80], "\n");
...and output_file[80] is one element past the end of the array. You declared it as:
std::ofstream output_file[80];
The first element of that array is output_file[0] and the last element of that array is output_file[79].
There are more things wrong
As #walnut pointed out, if your code is really as you posted it, then it appears to close each file immediately after opening it, without writing anything to the file.
for (int i = 0; i < co; i++)
{
output_file[i].open(name + std::to_string(i) + ".h");
output_file[i].close(); // Leaving so soon?
}
Writing to an ofstream that has been closed does not crash the program, but sets an error condition on the ofstream (badbit). So this will appear to be a silent failure to you.
To fix
To fix your problem you'll have to write to your file after you open it, but before you close it.
You'll also have to decide exactly which output_file you actually want to write to and provide the correct array index. It's not obviously clear from your sample code what your intent was. You'll have to decide which file(s) (of the 80 that you opened) you want to write each element of your rowname vector into.
The std::copy as you have written it will write all strings in the rowname vector to the same stream. If your intent was to write each element to its own file, then you'll have to set it up substantially differently.
Something more along the lines of:
#include <fstream>
#include <vector>
#include <string>
int main() {
std::vector<std::string> rowname = { "alpha", "bravo", "charlie" }; // example data
std::string name = "example"; // base filename
for (size_t i = 0; i < rowname.size(); ++i) {
std::ofstream output_file;
std::string filename = name + std::to_string(i) + ".h"; // e.g.: "example0.h"
output_file.open(filename);
output_file << rowname[i]; // write the string to the file
output_file.close(); // if you want
}
}
This writes the text alpha into example0.h, bravo into example1.h, and charlie into example2.h.
EDIT: Problem solved! Turns out Windows 7 wont let me read/ write to files without explicitly running as administrator. So if i run as admin it works fine, if i dont i get the weird results i explain below.
I've been trying to get a part of a larger program of mine to read a file.
Despite trying multiple methods(istream::getline, std::getline, using the >> operator etc) All of them return with either /0, blank or a random number/what ever i initialised the var with.
My first thought was that the file didn't exist or couldn't be opened, however the state flags .good, .bad and .eof all indicate no problems and the file im trying to read is certainly in the same directory as the debug .exe and contains data.
I'd most like to use istream::getline to read lines into a char array, however reading lines into a string array is possible too.
My current code looks like this:
void startup::load_settings(char filename[]) //master function for opening a file.
{
int i = 0; //count variable
int num = 0; //var containing all the lines we read.
char line[5];
ifstream settings_file (settings.inf);
if (settings_file.is_open());
{
while (settings_file.good())
{
settings_file.getline(line, 5);
cout << line;
}
}
return;
}
As said above, it compiles but just puts /0 into every element of the char array much like all the other methods i've tried.
Thanks for any help.
Firstly your code is not complete, what is settings.inf ?
Secondly most probably your reading everything fine, but the way you are printing is cumbersome
cout << line; where char line[5]; be sure that the last element of the array is \0.
You can do something like this.
line[4] = '\0' or you can manually print the values of each element in array in a loop.
Also you can try printing the character codes in hex for example. Because the values (character codes) in array might be not from the visible character range of ASCII symbols. You can do it like this for example :
cout << hex << (int)line[i]
I’m getting system error when I try to compile the code below on Visual C++ 2008 Express. What I’m trying to do is to initialize array of objects with data read from file. I think there is something wrong inside the while loop, because when I initialize these objects manually without the while loop it seems to work. Here is the code and text file:
#include <iostream>
#include <string>
#include "Book.h"
using namespace std;
int main()
{
const int arraySize = 3;
int indexOfArray = 0;
Book bookList[arraySize];
double tempPrice;//temporary stores price
string tempStr;//temporary stores author, title
fstream fileIn( "books.txt" );
while ( !fileIn.eof( ))
{
getline(fileIn,tempStr);
bookList[indexOfArray].setAuthor(tempStr);
getline(fileIn,tempStr);
bookList[indexOfArray].setTitle(tempStr);
fileIn >> tempPrice;
bookList[indexOfArray].setPrice(tempPrice);
if ( indexOfArray < arraySize ) //shifting array index while not exceeding array size
indexOfArray++;
}
fileIn.close();
return 0;
}
and the text file:
Author1
Book1
23.99
Author2
Book2
10.99
Autho3
Book3
14.56
It looks like you are trying to write to bookList[3] in the loop. You will loop through three times filling your array incrementing indexOfArray each time. This will leave indexOfArray at 3 -- your condition as it is written will allow indexOfAray to be incremented to 3. Then if you have a newline after the "14.56" in your data file you will loop one more time and attempt to pass an empty string to bookList[indexOfArray].setAuthor() leading to a segfault since indexOfArray is past the end of the array.
I would suggest ditching the hard-coded array and using a std::vector instead. At the start of each loop just use push_back() to add a new book to the end of the vector and then use back() to access the new element in the array.
There's another run-time error in your code: You don't read an entire line with the call to fileIn >> tempPrice;. The next call to getline() will read to the end of the line, so you'll get an empty string when you're expecting an author.
You're then off by one line in your text file, and you try to convert a title into a double. That make the fstream signal an error, and after that, you're in trouble.
Brett's right, a vector with push_back is a better solution here.
Brett also correctly pointed out that you could run into errors if your file has extra lines. You can fix that by checking if you successfully read from the file:
if(fileIn >> tempPrice)
{
bookList[indexOfArray].setPrice(tempPrice);
}
else
{
break;
}
if(!getline(fileIn,tempStr))
{
break;
}
The key must be in the contents of
#include "Book.h"
I copy-pasted your code, and replaced the #include with my assumption of what class Book might look like:
class Book
{
std::string auth;
std::string title;
double price;
public:
void setAuthor(std::string& str)
{
auth = str;
}
void setTitle(std::string& t)
{
title = t;
}
void setPrice(double d)
{
d = price;
}
};
and it compiled. Perhaps you could share your Book.h, or look there for any problems? Start with some simple definition from Book (like above) and begin readding code until you've found the lines that cause the problem. Its a crude method of figuring out the issue, but sometimes its the most direct way.