I have a text file, with many lines that goes like this:
1,1
2
7,7
11,11
13,13
0,0
I would like to take every integer and assign it to a variable, using the text file system that Qt provides. I've thought about reading every line, then using QString::split(), but I think that there are easier methods to do this.
Use QFile::readAll, pass it to QString in constructor, split it to QStringList, iterate through it with toInt function.
Edited to suit better your purpose, this is simple console test app (i would assume, that line with only number 2 is a mistake and every line should have at least two numbers).
main.cpp:
QFile f("file.txt");
f.open(QIODevice::ReadOnly);
foreach (QString i,QString(f.readAll()).split(QRegExp("[\r\n]"),QString::SkipEmptyParts)){
QPoint pos;
pos.setX(i.section(",",0,0).toInt());
pos.setY(i.section(",",1,1).toInt());
// draw something here, pos holds your coords in x as first valur and in y second (pos.x(), pos.y() )
qDebug()<<pos;
}
f.close();
your coords will hold QPoint pos, it will have one line of coords at a time, so you can draw points or do whatever you want with them. file.txt should be in a dir with a binary file or you can change as it suit you.
Related
I'm writing a simple application using QT that should write the mouse position inside certain widget to a file (the writing is on user double click).
The situation is as follows:
The user performs double click inside the widget.
In the Debug window i can see the current mouse position.
The mouse position is written to the file as expected.
The issue when the mouse position (X or Y) is less then 100. In case that the position is less then 100 the number that is written to the file is always 3 digits number.
For example: Mouse position in the debug window is: 34, 251 and in the file the position is 344, 251. So i can't predict if the actual X position was 34 or 344 because both of them are valid values.
This is the part that responsible on writing the data to the file.
QByteArray temp1;
char buf[2];
::sprintf(buf, "%d", X); // X is the mouse x position
temp1.append(buf);
temp1.append(",");
::sprintf(buf, "%d", Y); // Y is the mouse y position
temp1.append(buf);
...
if (tempFile.open(QIODevice::ReadWrite)) {
QTextStream stream(&tempFile);
stream << temp1;
}
tempFile.close();
This code works good only for positions larger then 100 for some reason.
Thanks
Problem 1
The buffer is too small.
char buf[2];
There needs to be place for multiple digits and a terminating NUL byte, since your are using sprintf here:
::sprintf(buf, "%d", X);
Writing over the end of the array results in undefined behavior.
So you need to increase the size of the array to fix it.
Problem 2
the file is opened in ReadWrite mode
the X and Y value are written
the file is closed
The next time the values are written the same operations are called. ReadWrite mode does not delete the existing contents of the file. For example if you write once
128,1024
and then the next time you write the position: 60,30 it would look like this:
60,3024
One possible solution for this problem would be to apply Truncate mode.
if (tempFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
I can get the current position in row and col via QTextCursor::blockNumber() and QTextCursor::positionInBlock(). My question is that how to move the cursor to the specific position with row and col. like
setPosition(x,y) // The current cursor would move to row x and col y.
Is it possible to do that?
Easy solution:
Just move the cursor there:
textEdit.moveCursor(QTextCursor::Start); //set to start
for( <... y-times ...> )
{
textEdit.moveCursor(QTextCursor::Down); //move down
}
for( < ... x-times ...>)
{
textEdit.moveCursor(QTextCursor::Right); //move right
}
moveCursor is also the goto-way if you need to "select" text for changing it. A similar approach without the loops is also at the end.
A bit more explanation and maybe better solution:
In theory a text has no "lines" like shown in GUIs but the endline-character (\n or \r\n depending on operating system and framework) is only just another character. So for a cursor mostly everything is just one "text" without lines.
There are wrapper functions to deal with this but I get to them later. First you cannot access those directly via the QTextEdit interface but you have to manipulate the cursor directly.
QTextCursor curs = textEdit.textCursor(); //copies current cursor
//... cursor operations
textEdit.setTextCursor(curs);
Now for the "operations":
If you know at what position you want to go in the string you have setPosition() here. This "position" is not in respect to vertical lines though, but the whole text.
This is what a multi-line string looks internally:
"Hello, World!\nAnotherLine"
This would show
Hello, World!
AnotherLine
setPosition() wants the position of the internal string.
To move to another line you would have to calculate the position by looking for the first \n in the text and add your x-offset. If you want the 3rd line look for the first 2 \n etc.
Luckily there is also the function setVerticalMovement which seems to wrap this and maybe is what you want to do. It moves the cursor vertically.
So you could do:
curs.setPosition(x); //beginning at first line
curs.setVerticalMovement(y); //move down to the line you want.
After that call setTextCursor like shown above with the cursor.
Note:
The order is important though. setPosition sets the position in the whole text. So setPosition(5) while maybe in the 3rd line will not set it to the 5th character in the line you are but of the whole text. So move the x-cordinate first then y.
You need to be aware of the lengths of the lines though.
some longer line
short
another longer line
If you now specify line 2 and column 7 it will be "out-of-bounds". I am not sure how verticalMovement here behaves. I assume the cursor will be at the end of the line.
When you use the QTextCursor class directly you can also use the move operations without the loops because they have an extra parameter to repeat the operations.
curs.movePosition(QTextCursor::Start);
curs.movePosition(QTextCursor::Down,<modeMode>,y); //go down y-times
curs.movePosition(QTextCursor::Right,<moveMode>,x); //go right x-times
I think the best way is via QTextCursor.
For example, if your QTextEdit is called textEdit:
QTextCursor textCursor = ui->textEdit->textCursor();
textCursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, x);
textCursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, y);
ui->textEdit->setTextCursor(textCursor);
Where x and y are the required position.
If the document layout uses 1 block per line (e.g. it's a QPlainTextEdit or the document contains text with very basic formatting), then you can set the position directly:
void setCursorLineAndColumn (QTextCursor &cursor, int line, int col,
QTextCursor::MoveMode mode)
{
QTextBlock b = cursor.document()->findBlockByLineNumber(line);
cursor.setPosition(b.position() + col, mode);
// you could make it a one-liner if you really want to, i guess.
}
The major advantage over the Down + Right methods is it is much simpler to select text between two locations:
QTextCursor cursor = textEdit->textCursor();
setCursorLineAndColumn(cursor, startLine, startCol, QTextCursor::MoveAnchor);
setCursorLineAndColumn(cursor, endLine, endCol, QTextCursor::KeepAnchor);
textEdit->setTextCursor(cursor);
There's also a performance advantage, if relevant.
NOTE: I changed the title from .png to .bmp due to a comment suggesting bitmaps instead.
I'm making this simple 2d grid based CMD-game, and I want to make .png levels and turn them into level data for my game.
So basically all I want to know is, how would I iterate through the pixels of a bmp to parse it to some level data.
This is how I did it with a .txt
int x = 0;
int y = 0;
std::ifstream file(filename);
std::string str;
while (std::getline(file, str))
{
x++;
for (char& c : str) {
y++;
updateTile(coordinate(x), coordinate(y), c);
}
}
I couldn't find any helpful threads so I posted this new one, hope I'm not breaking any rules
I don't know if you still want to read png-files, but if you do, check this decoder:
http://lodev.org/lodepng/
It loads a png-file into a vector where 4 chars (bytes) give one pixel(RGBA format). So by loading 4 chars at once, you will get one pixel.
I haven't used it before, but it looks easy to use.
Edit: Changed the title to better reflect the current issue.
Right i know now were the source of the issue lies, it's with the text splitting part of the function. I remember now what i did, i changed the splitting text because the tutorial for me was returning an error.
for(const char *c=text;*c;c++)
{
if(*c=='\n') {
string line;
for(const char *n=start_line;n<c;n++) line.append(1,*n);
lines.push_back(line);
start_line=c+1;
}
}
if(start_line)
{
string line;
for(const char *n=start_line; n < c;n++) line.append(1,*n);
lines.push_back(line);
}
The 'c' was returning undeclared, and there's no mention for any other c, so i guess it's referring to the pointer in the for loop above. Though bring the "if (start_line)" into the first code block, kept returning me each character in the text, instead of just the whole thing.
So i changed the code to the following:
for(const char *c=text;*c;c++)
{
if(*c=='\n')
{
string line;
for(const char *n=start_line;n<c;n++) line.append(1,*n);
lines.push_back(line);
start_line=c+1;
if(start_line)
{
string line;
for(const char *n=start_line;n<c;n++) line.append(1,*n);
lines.push_back(line);
}
}
else if (*c == *start_line)
{
lines.push_back(text);
}
}
I pretty sure that the "else if (*c == *start_line)" comparsion is what's causing me the issue. Unsure though what to replace it with. I guess though because i'm not using any newlines or don't plan to i can just go with:
for(const char *c=text;*c;c++)
{
lines.push_back(text);
break;
}
But it would still be nice to know were i was going wrong. *Note: That the above code works fine now, no issue with that and the doubling effect. So i'm sure that it was my text splitting code.
Here's an idea for you: in your text rendering method, add a static counter and use it to set the color of each string rendered. Since you don't seem to have that many strings per frame, you can use the 8 bits of one color component (e.g. red) for the counter and set the 2 other components to 255. If you had more than 255 strings, you could still encode the counter value over 2 or 3 color components.
With this little debug aid, you will be able to see in which order each piece of text is rendered. You can use pixie and/or zoomin to see the pixel values "live". Otherwise, just take a screenshot and examine the result.
It looks like the erroneously drawn text in that capture is "50b" which I doubt is a string that would normally appear in your game. It looks like you're drawing something that's normally an empty string, but sometimes picks up junk values - in other words, undefined behavior.
I can't be sure, of course, because I simply don't have enough information to find your problem. Your glClear looks fine to me, so you can be assured that the extra text is being drawn in the same frame as your intended text.
I have a data file which contains data in row/colum form. I would like a way to read this data in to a 2D array in C or C++ (whichever is easier) but I don't know how many rows or columns the file might have before I start reading it in.
At the top of the file is a commented line giving a series of numbers relating to what each column holds. Each row is holding the data for each number at a point in time, so an example data file (a small one - the ones i'm using are much bigger!) could be like:
# 1 4 6 28
21.2 492.1 58201.5 586.2
182.4 1284.2 12059. 28195.2
.....
I am currently using Python to read in the data using numpy.loadtxt which conveniently splits the data in row/column form whatever the data array size, but this is getting quite slow. I want to be able to do this reliably in C or C++.
I can see some options:
Add a header tag with the dimensions from my extraction program
# 1 4 6 28
# xdim, ydim
21.2 492.1 58201.5 586.2
182.4 1284.2 12059. 28195.2
.....
but this requires rewriting my extraction programs and programs which use the extracted data, which is quite intensive.
Store the data in a database file eg. MySQL, SQLite etc. Then the data could be extracted on demand. This might be a requirement further along in the development process so it might be good to look into anyway.
Use Python to read in the data and wrap C code for the analysis. This might be easiest in the short run.
Use wc on linux to find the number of lines and number of words in the header to find the dimensions.
echo $((`cat FILE | wc -l` - 1)) # get number of rows (-1 for header line)
echo $((`cat FILE | head -n 1 | wc -w` - 1)) # get number of columns (-1 for '#' character)
Use C/C++ code
This question is mostly related to point 5 - if there is an easy and reliable way to do this in C/C++. Otherwise any other suggestions would be welcome
Thanks
Create table as vector of vectors:
std::vector<std::vector<double> > table;
Inside infinite (while(true)) loop:
Read line:
std::string line;
std::getline(ifs, line);
If something went wrong (probably EOF), exit the loop:
if(!ifs)
break;
Skip that line if it's a comment:
if(line[0] == '#')
continue;
Read row contents into vector:
std::vector<double> row;
std::copy(std::istream_iterator<double>(ifs),
std::istream_iterator<double>(),
std::back_inserter(row));
Add row to table;
table.push_back(row);
At the time you're out of the loop, "table" contains the data:
table.size() is the number of rows
table[i] is row i
table[i].size() is the number of cols. in row i
table[i][j] is the element at the j-th col. of row i
How about:
Load the file.
Count the number of rows and columns.
Close the file.
Allocate the memory needed.
Load the file again.
Fill the array with data.
Every .obj (3D model file) loader I've seen uses this method. :)
Figured out a way to do this. Thanks go mostly to Manuel as it was the most informative answer.
std::vector< std::vector<double> > readIn2dData(const char* filename)
{
/* Function takes a char* filename argument and returns a
* 2d dynamic array containing the data
*/
std::vector< std::vector<double> > table;
std::fstream ifs;
/* open file */
ifs.open(filename);
while (true)
{
std::string line;
double buf;
getline(ifs, line);
std::stringstream ss(line, std::ios_base::out|std::ios_base::in|std::ios_base::binary);
if (!ifs)
// mainly catch EOF
break;
if (line[0] == '#' || line.empty())
// catch empty lines or comment lines
continue;
std::vector<double> row;
while (ss >> buf)
row.push_back(buf);
table.push_back(row);
}
ifs.close();
return table;
}
Basically create a vector of vectors. The only difficulty was splitting by whitespace which is taken care of with the stringstream object. This may not be the most effective way of doing it but it certainly works in the short term!
Also I'm looking for a replacement for the deprecated atof function, but nevermind. Just needs some memory leak checking (it shouldn't have any since most of the objects are std objects) and I'm done.
Thanks for all your help
Do you need a square or a ragged matrix? If the latter, create a structure like this:
std:vector < std::vector <double> > data;
Now read each line at a time into a:
vector <double> d;
and add the vector to the ragged matrix:
data.push_back( d );
All data structures involved are dynamic, and will grow as required.
I've seen your answer, and while it's not bad, I don't think it's ideal either. At least as I understand your original question, the first comment basically specifies how many columns you'll have in each of the remaining rows. e.g. the one you've given ("1 4 6 28") contains four numbers, which can be interpreted as saying each succeeding line will contain 4 numbers.
Assuming that's correct, I'd use that data to optimize reading the data. In particular, after that, (again, as I understand it) the file just contains row after row of numbers. That being the case, I'd put all the numbers together into a single vector, and use the number of columns from the header to index into the rest:
class matrix {
std::vector<double> data;
int columns;
public:
// a matrix is 2D, with fixed number of columns, and arbitrary number of rows.
matrix(int cols) : columns(cols) {}
// just read raw data from stream into vector:
std::istream &read(std::istream &stream) {
std::copy(std::istream_iterator<double>(stream),
std::istream_iterator<double>(),
std::back_inserter(data));
return stream;
}
// Do 2D addressing by converting rows/columns to a linear address
// If you want to check subscripts, use vector.at(x) instead of vector[x].
double operator()(size_t row, size_t col) {
return data[row*columns+col];
}
};
This is all pretty straightfoward -- the matrix knows how many columns it has, so you can do x,y indexing into the matrix, even though it stores all its data in a single vector. Reading the data from the stream just means copying that data from the stream into the vector. To deal with the header, and simplify creating a matrix from the data in a stream, we can use a simple function like this:
matrix read_data(std::string name) {
// read one line from the stream.
std::ifstream in(name.c_str());
std::string line;
std::getline(in, line);
// break that up into space-separated groups:
std::istringstream temp(line);
std::vector<std::string> counter;
std::copy(std::istream_iterator<std::string>(temp),
std::istream_iterator<std::string>(),
std::back_inserter(counter));
// the number of columns is the number of groups, -1 for the leading '#'.
matrix m(counter.size()-1);
// Read the remaining data into the matrix.
m.read(in);
return m;
}
As it's written right now, this depends on your compiler implementing the "Named Return Value Optimization" (NRVO). Without that, the compiler will copy the entire matrix (probably a couple of times) when it's returned from the function. With the optimization, the compiler pre-allocates space for a matrix, and has read_data() generate the matrix in place.