I've got a file that can be of any size and is a series of char values without any spaces between (except a blank space is treated as a blank cell of a grid).
xxxxxxx
xx xx
xxyyyxx
After some great help I've gone with the method to use a vector<vector<char> > however I cannot seem to populate it.
void readCourse(istream& fin) {
// using 3 and 7 to match example shown above
vector<vector<char> > data(3, vector<char>(7));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 7; j++) {
fin.get(data[i][j]); // I believe the problem exists here
} // Does the .get() method work here?
} // Or does it need to be .push_back()?
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 7; j++) {
cout << data[i][j];
}
}
}
Is my method for populating my 2D vector valid? If not, can you please point me in the right direction?
I'd keep it simple and efficient with a single vector<char>:
vector<char> readCourse(istream& fin) {
vector<char> course(3*(7+2)); // 3x7 plus newlines
fin.read(course.data(), course.size());
course.resize(fin.gcount());
auto end = remove(course.begin(), course.end(), '\n');
end = remove(course.begin(), end, '\r');
course.erase(end, course.end()); // purge all \n and \r
return course;
}
That's a single input operation to get all the data, followed by removing the characters you don't need. You can then access the result in a 2D way like this:
course.at(x + y*7) // assuming width 7
That may seem a bit inconvenient, but it is efficient and compact--the overhead is always three pointers and a single heap allocation, instead of being proportional to the number of rows.
Solution I ended up using after ADT implementation:
void readCourse(std::istream& fin) {
std::vector<std::string> level
std::string line;
while(std::getline(fin, line) {
level.push_back(line);
}
for (std::size_t i = 0; i < 3; i++) {
for (std::size_t j = 0; j < 7; j++) {
std::cout << data[i][j];
}
std::cout << std::endl;
}
}
Related
Basically, I'm reading a file and trying to store the data in a 2D, for the differentiation between rows and columns I use the logic below:
int rows=0,column=0;
char arr[50][50];
while(my_file.eof()==0){
my_file.get(ch);
if(ch=='\n'){
rows++;
}
arr[rows][column]=ch;
column++;
}
for(int j=0;j<rows;j++){
for(int k=0;k<column;k++){
cout<<arr[j][k];}
}
But the when I run It shows the following output: https://i.stack.imgur.com/XzhST.png
And the text file data is:
I am going to school
hi!
Hello
guide me a bit...
Hmm, a 2D char array can indeed be used to store an number of lines, but you should control that you never try to store more than 50 characters for a single line, and that you never try to ouput more characters for a line than what it initially contained.
Here is a minimal fix of your code:
int rows = 0, column = 0;
char arr[50][50] = { {0 } }; // ensure the array is initialized with '\0' chars
for (;;) {
my_file.get(ch);
if (!my_file) break; // eof shall be tested AFTER a read operation
if (ch == '\n') {
rows++;
if (rows == 50) break; // no more than 50 lines
column = 0; // reset column index for next line
}
else if (column < 50) { // no more than 50 columns
arr[rows][column] = ch;
column++;
}
}
for (int j = 0; j < rows; j++) {
for (int k = 0; k < 50; k++) {
if (arr[j][k] == 0) break; // stop on end of line
std::cout << arr[j][k];
}
std::cout << '\n'; // and display the end of line
}
And as you have been said this is rather C-ish... I assume it is only for learning how 2D arrays can work.
As pointed out in comments, you'd be much better off using a std::vectorstd::string to store the strings.
But, this looks like a homework assignment to read then print each byte separately, so let's have a look... I'll add one of the ways this is usually done at the end of this post.
Your output looks like this:
It looks like you are displaying characters beyond the bondary of the strings, or that your strings are not null terminated... Turns out it's both.
Your code:
int rows = 0, column = 0;
char arr[50][50]; // <-- your array is not initialized, while that is not
// a big issue, filling the array with zeroes is easy:
// char arr[50][50] = {};
while (my_file.eof() == 0) {
my_file.get(ch);
if (ch == '\n') {
rows++; // <-- you pass to the next string, but do not put a
// null character to properly terminate your strings
// while this could have been avoided by initializing
// the array, it's best to do it explicitely.
// replace above line contents by:
arr[row][column] = '\0';
if (++row >= 50) // consider using named constants for the size of your array.
break; // No use keeping on reading strings if there is no
// more room to store them
}
arr[rows][column] = ch; // <-- I suspect a bunch un undefined stuff will
// start happening when column >= 50
column++;
// Try replacing above code with:
if (column < 50) // consider using named constants for the size of your array.
arr[rows][column++] = ch;
}
// make sure the last string is null terminated.
if (row < 50 && column < 50)
arr[row][column] = '\0';
// note that strings that are 50 bytes long are NOT null terminated.
// that's important to keep in mind, and only workss because we'll print
// byte by byte.
// your original print routine prints out all characters in the array, even
// stuff that was not in the original file...
for (int j = 0; j < rows; ++j){
for (int k=0 ; k < column; ++k){ // <-- you need to check for a null
// terminating character here...
// also, column is the length of the last
// string in the array. This is not a very
// useful value for displaying any other
// strings, is it?
// try this:
for (int k = 0; k < 50 && arr[j][k] != '\0'; ++k)
cout << arr[j][k];
}
cout << '\n'; // insert a newline after each string.
}
As you can tell, this is overly complex for doing a very common operation... Here's a more concise way of doing the same thing:
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
int main()
{
std::vector<std::string> arr;
std::ifstream ifs("testfile.txt");
while (ifs && !ifs.eof())
{
std::string str;
std::getline(ifs, str);
arr.push_back(str);
}
for (size_t i = 0; i < arr.size(); ++i)
std::cout << arr[i] << '\n';
return 0;
}
Because you haven't compile the array yet
char arr[50][50];
for (int r = 0; r < 50; r++){
for (int c = 0; c < 50; c++){
arr[r][c] = ' ';}
}
I want to create a string array and then after writing lines into it I want to change one exact character into int. I already know that all the characters are going to be numbers. As my goal is to change the one character at a time, options like atoi, stoi etc. are perhaps off? The closest I got is that:
#include <iostream>
int main()
{
int n=0,suma=0,i=0;
int multiplier[11]={1,3,7,9,1,3,7,9,1,3,1};
std::cin>>n;
std::string str[n];
for (int i = 0; i < n; ++i)
{
std::cin>>str[i];
}
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 11; ++j)
{
i = str[i][j] - '0';
std::cout << i;
}
}
}
Although this is the output I get
"1-48"
I know that the string is going to be 11 characters long. Any ideas?
EDIT: It was a single typo that caused my confuse :p Yet still I'm looking forward to read and learn from your suggestions such as using different way to read n (from user input) strings. :)
In your loop:
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 11; ++j)
{
i = str[i][j] - '0';
std::cout << i;
}
}
you are modifying outer loop variable i (looks like for the purpose of printing a value).
Given an unfortunate input, you would go out-of-bounds fast.
I'm writing a code that reads a text file to create a matrix dinamically. I will then get the information from this matrix to write it in another text file.
I put in a 'for' loop that will create a matrix for each chunk of text that it reads, here it is:
for (cont = 0; cont < nPares; cont++){
ResultFile >> FlowOri;
ResultFile >> FlowDest;
ResultFile >> nPaths;
ResultFile >> largestPath;
double** iPathMatrix = (double**) new double[nPaths];
for (i = 0; i < nPaths; i++) {
iPathMatrix[i] = (double*) new double[largestPath + 2];
}
for (i = 0; i < nPaths; i++) {
for (j = 0; j < largestPath + 2; j++) {
ResultFile >> iPathMatrix[i][j];
}
}
for (i = 0; i < nPaths; i++) {
for (j = 0; j < largestPath + 2; j++) {
cout << iPathMatrix[i][j] << " ";
}
cout << "\n";
}
free(iPathMatrix);
}
'ResultFile' is an ifstream.
That 'cout' near the end was put there to check if it was creating the matrices as intended, which it is.
As you can see, I'm creating the matrix and then freeing the memory at the end of the loop to create it again, since it will have the same name. I can probably figure a way to work with this, but it'd be way easier if I could create a different matrix with each loop, perhaps naming each with the 'FlowOri' and 'FlowDest' variables, if that's possible, and after the loop stops, access them and write in my output file.
Is there a way to do it? How would I reference it afterwards?
As you can see, I'm creating the matrix and then freeing the memory at
the end of the loop to create it again,
No you aren't (at least not correctly). Each new[] must be paired with a delete[], so the correct code is
for (i = 0; i < nPaths; i++) {
delete[] iPathMatrix[i];
}
delete[] iPathMatrix;
Now since you are programming C++, you could avoid this by using vectors.
std::vector<std::vector<double>> iPathMatrix(nPaths, std::vector<double>(largestPath + 2));
Now you don't need to allocate or free anything, but the rest of your code is unchanged.
Now as for your actual question. If I understand it correctly you want to associate a matrix with the value of the flowOri variable. That's easy to do, you should use a std::map.
You haven't said what type flowOri is, I'm going to assume it's a std::string but you should be able to get this to work whatever type it is.
#include <vector>
#include <map>
#include <string>
// lets give a shorter name for the vector type
using MatrixRowType = std::vector<double>;
using MatrixType = std::vector<MatrixRowType>;
// and this is the map type that holds the matrices
using MatrixMapType = std::map<std::string, MatrixType>;
...
MatrixMapType matrixMap;
for (cont = 0; cont < nPares; cont++){
// read stuff
ResultFile >> FlowOri;
...
// read the matrix
MatrixType iPathMatrix(nPaths, MatrixRowType(largestPath + 2));
...
// save the matrix in the map keyed by FlowOri
matrixMap[FlowOri] = iPathMatrix;
}
Now later when you want to retrieve a matirx you just write matrixMap[something] where something is a variable with the name of the matrix you want to retrieve.
Variable names only have a meaning for the programmer and disappear from the executable unless you use a debug mode, so C++ does not allow to create dynamically named variables. Anyway even with languages that allow it, it is a terrible design.
But you can always create an array (or better a vector) of any object type. So if you want to store everything in the loop and process later the whole data, you could use a 3 level vector. With the magic of references, little has to be changed from your code:
std::vector<std::vector<std::vector<double>>> matrixes(nPares);
for (auto& iPathMatrix : matrixes) { // iPathMatrix is a reference inside matrixes
ResultFile >> FlowOri;
ResultFile >> FlowDest;
ResultFile >> nPaths;
ResultFile >> largestPath;
iPathMatrix = std::vector<std::vector<double>>(nPaths);
for (i = 0; i < nPaths; i++) {
iPathMatrix[i] = std::vector<double>(largestPath + 2];
}
for (i = 0; i < nPaths; i++) {
for (j = 0; j < largestPath + 2; j++) {
ResultFile >> iPathMatrix[i][j];
}
}
}
// control:
for (int i = 0; i < nPares; i++) {
for (int j = 0; j < matrixes[i].size(); j++) {
for (int k = 0; k < matrixes[i][j].size(); k++) {
std::cout << matrixes[i][j][k] << " ";
}
std::cout << '\n';
}
std::cout << '\n';
}
matrixes now contain all of your data
I'm trying to step through a given string with a for loop, replacing one character per iteration with a character from a vector[char].
Problem is that the replace inserts the entire vector-k instead of the character at place k and I cannot figure out what I've done wrong.
Any and all help is appreciated.
(alphabet is a const string a-z, FirstWord is the given string).
vector<char> VectorAlphabet;
for (int i=0; i<alphabet.length(); ++i)
{
VectorAlphabet.push_back(alphabet.at(i));
}
for (int i = 0; i < FirstWord.length(); ++i )
{
for (int k = 0; k < VectorAlphabet.size(); ++k)
{
string TempWord = FirstWord;
TempWord.replace(i, 1, &VectorAlphabet[k]);
if (CheckForValidWord(TempWord, WordSet))
{
if(CheckForDuplicateChain(TempWord, DuplicateWordSet))
{
DuplicateWordSet.insert(TempWord);
stack<string> TempStack = WordStack;
TempStack.push(TempWord);
WordQueue.push(TempStack);
}
}
}
}
e.g TempWord = tempword, then after TempWord.replace() on the first iteration it is abcde...zempWord. and not aempword. On the second to last iteration of the second for loop it is yzempword.
What have I missed?
Problem solved, thanks to Dieter Lücking.
Looking closer at the string.replace reference, I see that I tried to use a replace which takes strings as the input, and then the vector[char] is interpreted as a c-string, starting from the k-position.
By using the fill-version of replace the vector position is correctly used as a char instead.
New code is:
for (int i = 0; i < FirstWord.length(); ++i )
{
for (int k = 0; k < VectorAlphabet.size(); ++k)
{
string TempWord = WordStack.top();
// Change:
TempWord.replace(i, 1, 1, VectorAlphabet[k]);
if (CheckForValidWord(TempWord, WordSet))
{
if(CheckForDuplicateChain(TempWord, DuplicateWordSet))
{
DuplicateWordSet.insert(TempWord);
stack<string> TempStack = WordStack;
TempStack.push(TempWord);
WordQueue.push(TempStack);
}
}
}
}
I'm trying to read text from a file into a char's matrix
I do it this way:
char** crearMundo()
{
ifstream input("C:\\Users\\JhonAlx\\Desktop\\file.txt");
input >> filas;
input >> columnas;
filas += 2;
columnas += 2;
char** laberinto = crearMatriz(filas, columnas);
//Initial fill
for(int i = 0; i < filas; i++)
{
for(int j = 0; j < columnas; j++)
{
laberinto[i][j] = ' ';
}
}
//Next two loops will fill only borders
for(int i = 0; i < filas; i++)
{
laberinto[0][i] = '?';
laberinto[filas - 1][i] = '?';
}
for(int i = 0; i < columnas; i++)
{
laberinto[i][0] = '?'; //VS throws error in this line
laberinto[i][columnas - 1] = '?';
}
//Fill actual content of file, omitting borders
for(int i = 1; i < filas - 1; i++)
{
for(int j = 1; j < columnas - 1; j++)
{
input >> laberinto[i][j];
}
}
return laberinto;
}
This morning when I programmed it, it was fine, but now it throws this error:
Access violation reading location 0xFDFDFDFD
Debugging with VS2012 and looking with Locals explorer, I get this text on the ifstream variable:
input{_Filebuffer={_Set_eback=0xcccccccc <
Error reading characters of
string.>
_Set_egptr=0xcccccccc <
Error reading characters of string.>
...}
} std::basic_ifstream >
Any help will be apreciated.
In the first and last set of loops (the doubly-nested ones), your index limits are set up with respect to filas then columnas. However, the middle two (that set the borders) are different and probably wrong. Note the indices used by your 4 successive loops:
laberinto[0..filas-1][0..columnas-1] -- fill with ' ' (ok: filas, then columnas)
laberinto[0,filas-1][0..filas-1] -- first borders (bad: filas, then filas)
laberinto[0..columnas-1][0,columnas-1] -- second borders(bad: columnas, then columnas)
laberinto[0..filas-2][0..columnas-2] -- read from file(ok: filas, then columnas)
The likely cause of your error is overrunning your array bounds due to using the wrong index in the border loops; the likely fix is to correct that.
Alternative method - read the file into a string, and then use c_str() to get the char array.
Example:
std::ifstream in("content.txt");
std::string contents((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
contents.c_str() // The char array