Read Value of Attribute in HDFql - c++

I am using HDFql to create an HDF5 file. I am creating a group and putting some data into it and that works fine. I then add an attribute to the file (which also seems to work, since it shows up when I inspect the HDF5 file with an HDF editor), but I can't figure out how to read the value of the attribute. Here is a minimal example:
#include <iostream.h>
#include <HDFql.hpp>
int main (int argc, const char * argv[]) {
char script[1024];
//Create the HDF5 file and the group "test"
HDFql::execute("CREATE TRUNCATE FILE /tmp/test.h5");
HDFql::execute("USE FILE /tmp/test.h5");
HDFql::execute("CREATE GROUP test");
//Generate some arbitrary data and place it in test/data
int data_length = 1000;
int data[data_length];
for(int i=0; i<data_length; i++) {data[i] = i;}
sprintf(script, "CREATE DATASET test/data AS INT(%d) VALUES FROM MEMORY %d",
data_length, HDFql::variableTransientRegister(data));
HDFql::execute(script);
//Create an attribute called "channels" and give it an arbitrary value of 11
HDFql::execute("CREATE ATTRIBUTE test/data/channels AS INT VALUES(11)");
//Show the attribute
HDFql::execute("SHOW ATTRIBUTE test/data/channels");
//Try to move the cursor to the attribute
HDFql::cursorLast();
//If that worked, print the attribute contents
if(HDFql::cursorGetInt())
{
std::cout << "channels = " << *HDFql::cursorGetInt() << std::endl;
} else
{
std::cout << "Couldn't find attribute" << std::endl;
}
HDFql::execute("CLOSE FILE");
}
I would expect the output to the console to be channels = 11, instead I am getting channels = 1953719668. Curiously, if I call cursorGetChar instead, the return value is "t", and if I say
std::cout << "channels = " << HDFql::cursorGetChar() << std::endl;
the output becomes channels = test/data/channels.
So I think I have misunderstood how exactly HDFql cursors work. So my question is: what is wrong with my code? And why is my code wrong?
Many thanks!

When you do SHOW ATTRIBUTE test/data/channels you are basically testing for the existence of an attribute named channels stored in test/data. Since this attribute exists, function HDFql::execute returns HDFql::Success and the cursor is populated with the string test/data/channels. On the other hand, if the attribute didn't exist, function HDFql::execute would have returned HDFql::ErrorNotFound and the cursor would be empty.
To read the value stored in attribute test/data/channels do the following instead:
HDFql::execute("SELECT FROM test/data/channels");
HDFql::cursorFirst();
std::cout << "channels = " << *HDFql::cursorGetInt() << std::endl;

Related

Segmentation fault while reading feather file containing columns with datatype arrow::large_utf8()

I had written c++ code to read feather file and insert the data into a arrow::Table, but it gives segmentation fault if the file contains any column with datatype arrow::large_utf8. It gives segfault for this datatype only, there are no errors for utf8/int/float datatype.
I think there is something wrong with the feather API implementation for read/write because if I do the same for a parquet file, it works fine.
I found a link which somewhat is closely related to the same problem but it was with python - https://github.com/pandas-dev/pandas/issues/24767
Does anyone have any idea on why there is such behavior ?
arrow::Status write_to_feather(std::string path,std::shared_ptr <arrow::Table> table) {
arrow::fs::LocalFileSystem file_system;
auto output = file_system.OpenOutputStream(path).ValueOrDie();
ABORT_ON_FAILURE(arrow::ipc::feather::WriteTable(*table,output.get()));
return arrow::Status::OK();
}
void read_feather_to_table(std::string path,std::shared_ptr<arrow::Table> *feather_table){
arrow::fs::LocalFileSystem file_system;
std::shared_ptr <arrow::io::RandomAccessFile> input_file = file_system.OpenInputFile(path).ValueOrDie();
std::shared_ptr <arrow::ipc::feather::Reader> feather_reader = arrow::ipc::feather::Reader::Open(input_file).ValueOrDie();
arrow::Status temp_status = feather_reader -> Read(feather_table);
if(temp_status.ok()){
std::cout << "Read feather file Successfully." << std::endl;
std::cout << ((*feather_table).get()) -> ToString() << std::endl; // this line gives segfault
}
else{
std::cout << "Feather file reading process failed." << std::endl;
}
return;
}
When i had used write_to_feather to write a table which contains any column with large_utf8 datatype, my read_feather_to_table function gives a segfault while in all other cases both functions work fine. The segfault occurs when i try to print the table content as specified in the code above.

Bug writing into Excel file - C++

When I send a variable into Excel it changes its value. It just happens with excel. It also just happens when the variable is stored in a container. I guess it is more clear if you see the code:
#include<iostream>
#include<array>
#include<vector>
#include<fstream>
const int aSize{ 150000 };
std::array<double, aSize> anArray{};
int main(void)
{
double aValue{ 0.00000005467 };
std::vector<double> aVector;
for (int i = 0; i < aSize; ++i)
{
anArray[i] = aValue;
aVector.push_back(aValue);
}
std::ofstream fileOne, fileTwo, fileThree, fileFour, fileFive;
fileOne.open("array.xls");
fileTwo.open("array.txt");
fileThree.open("vector.xls");
fileFour.open("vector.txt");
fileFive.open("value.xls");
fileOne << anArray[0];
fileTwo << anArray[0];
fileThree << aVector[0];
fileFour << aVector[0];
fileFive << aValue;
std::cout << aValue << "\n" << anArray[0] << "\n" << aVector[0];
return 0;
}
All I do is populate a vector and an array. If I print the value of the variable I get the expected value. If I send it into a .txt I get the expected value. If I send just the value into Excel I get the expected value.
It all just breakes down when I send the value from the containers into Excel. Why can this be happening?
What seems likely to be the problem here is the way the Excel is interpreting the (formatted) numerical output from your c++ program. Even though the text may be correct (from the point of view of the cout function) it may not have the 'correct' decimal point character in it (i.e. a dot instead of a comma, or vice versa).
Solution: Make sure that Excel is set to use the same "locale" as the default c++ locale, or set the c++ locale to whatever Excel is using.
The MS-XLS file format is not a simple text file. You cannot simply put text into it and expect it to show up correctly. You would need more code and/or specialized libraries to interact with it.
See the suggestions here.

vtk value at x,y,z point

I have a vtk file which maps temperature in 3 dimensions. I would like to determine the temperature at a given x, y, z point. I will use the following code in order to load the vtk file (Reading .vtk file):
int main(int argc, char *argv[])
{
// simply set filename here (oh static joy)
std::string inputFilename = "setYourPathToVtkFileHere";
// Get all data from the file
vtkSmartPointer<vtkGenericDataObjectReader> reader =
vtkSmartPointer<vtkGenericDataObjectReader>::New();
reader->SetFileName(inputFilename.c_str());
reader->Update();
// All of the standard data types can be checked and obtained like this:
if (reader->IsFilePolyData())
{
std::cout << "output is a polydata" << std::endl;
vtkPolyData* output = reader->GetPolyDataOutput();
std::cout << "output has " << output->GetNumberOfPoints() << " points." << std::endl;
}
return EXIT_SUCCESS;
}
However, when searching through the extensive list of methods in the vtk library I cant find the appropriate function to extract a value at a specific location. Any suggestions?
The proper way to retrieve a scalar value at a given position depends on two questions:
How is your data laid out and
From which position do you want to retrieve an attribute
Concerning the data layout there are two main layouts:
Structured: The data resides in a uniform grid
Unstructured: The point samples are arbitrary
Concerning the position you can have two situations:
Query at a sample position: You ask for a point that is directly a sample in your dataset
Query at an arbitrary position: You ask for a point that is somewhere in your domain but does not neccessarily coincide with a sample of your data.
Independent of the data layout, to retrieve the data at a sample position (i.e. a sample of your original data set) you can use the vtkPointLocator class. Use the class as follows (untested):
// Build locator object
vtkSmartPointer<vtkPointLocator> locator = vtkPointLocator::New();
locator->SetDataSet(polyData);
locator->BuildLocator();
// Define query position
double pt[3] = {0.1, 0.2, 0.3};
// Get the ID of the point that is closest to the query position
vtkIdType id = locator->FindClosestPoint(pt);
// Retrieve the first attribute value from this point
double value = polyData->GetPointData()->GetScalars()->GetTuple(id, 0);
This will give you the point value of the closest sample of your data.
Note that this will not give you the explicit position of the point in the data set as this is implicitly encoded in the variable id. To retrieve the actual position of the closest point, you can write:
double *dataPt = polyData->GetPoint(id);
If you want to retrieve data at an arbitrary position of your domain you will need some way of interpolation.
Here, the data layout is important.
For structured data you can first convert your data to a vtkImage and then perform queries on it. If you want to retrieve an interpolated attribute using linear or cubic schemes you can add a vtkImageInterpolator in your filter chain and then retrieve a point using the GetScalarComponentAsDouble method.
For unstructured data you should first decide on an interpolation scheme. vtk has various filters to reconstruct continuous data from data samples. Options include Delaunay triangulation/tetrahedrization (vtkDelaunay2D, vtkDelaunay3D) , as well as the Shepard method (vtkShepardMethod). Either method will give you a new data set with possibilities to query arbitrary points. If you want to retrieve the scalar attributes for a (batch of) points without actually reconstructing a complete data set you can also look at the vtkProbeFilter.
You need to first extract the polyData from the reader. Then, store the points via vtkPolyData::getPoints into a vtksmartPointer<vtkPoints>.
To finish, create a std::vector of a custom struct and store them while iterating over your vtkPoints.
Here's some code to illustrate :
#include <vtkDataArray.h>
#include <vtkDataSet.h>
#include <vtkGenericDataObjectReader.h>
#include <vtkPointLocator.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkStructuredGrid.h>
#include <string>
struct Pnt {
double x_, y_, z_;
Pnt(double x, double y, double z) : x_(x), y_(y), z_(z) {}
};
int main ( int argc, char *argv[] )
{
// Ensure a filename was specified
if(argc != 2)
{
std::cerr << "Usage: " << argv[0] << " InputFilename" << endl;
return EXIT_FAILURE;
}
// simply set filename here (oh static joy)
std::string inputFilename = "setYourPathToVtkFileHere";
// Get all data from the file
vtkSmartPointer<vtkGenericDataObjectReader> reader = vtkSmartPointer<vtkGenericDataObjectReader>::New();
reader->SetFileName(inputFilename.c_str());
reader->Update();
vtkSmartPointer<vtkPolyData> polydata = reader->GetPolyDataOutput();
vtkSmartPointer<vtkPoints> vtk_points = polydata->GetPoints();
std::vector<Pnt> my_points;
for (int i = 0; i < vtk_points->GetNumberOfPoints(); i++){
const auto pnt = vtk_points->GetPoint(i);
my_points.emplace_back(pnt[0], pnt[1], pnt[2]);
}
return EXIT_SUCCESS;
}
And here is the version with the vtkPointLocator mentionned in QnD's answer :
int main(int argc, char *argv[])
{
// Ensure a filename was specified
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " InputFilename" << endl;
return EXIT_FAILURE;
}
// simply set filename here (oh static joy)
std::string inputFilename = "setYourPathToVtkFileHere";
// Get all data from the file
vtkSmartPointer<vtkGenericDataObjectReader> reader = vtkSmartPointer<vtkGenericDataObjectReader>::New();
reader->SetFileName(inputFilename.c_str());
reader->Update();
vtkSmartPointer<vtkPolyData> polydata = reader->GetPolyDataOutput();
//Building locator
vtkSmartPointer<vtkPointLocator> locator = vtkPointLocator::New();
locator->SetDataSet(polydata);
locator->BuildLocator();
//Finding point
const double pt[3] = { 0.1, 0.2, 0.3 };
vtkIdType id = locator->FindClosestPoint(pt);
double pnt_found[3];
polydata->GetPointData()->GetScalars()->GetTuple(id, pnt_found);
return EXIT_SUCCESS;
}
And the CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
PROJECT(GenericDataObjectReader)
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
add_executable(GenericDataObjectReader MACOSX_BUNDLE GenericDataObjectReader)
if(VTK_LIBRARIES)
target_link_libraries(GenericDataObjectReader ${VTK_LIBRARIES})
else()
target_link_libraries(GenericDataObjectReader vtkHybrid vtkWidgets)
endif()
Please let me know if I need to do any specific action to correctly credit the other answer. I'm new and don't know all the specifics yet.

Reading text file by scanning for keywords

As part of a bigger application I am working on a class for reading input from a text file for use in the initialization of the program. Now I am myself fairly new to programming, and I only started to learn C++ in December, so I would be very grateful for some hints and ideas on how to get started! I apologise in advance for a rather long wall of text.
The text file format is "keyword-driven" in the following way:
There are a rather small number of main/section keywords (currently 8) that need to be written in a given order. Some of them are optional, but if they are included they should adhere to the given ordering.
Example:
Suppose there are 3 potential keywords ordered like as follows:
"KEY1" (required)
"KEY2" (optional)
"KEY3" (required)
If the input file only includes the required ones, the ordering should be:
"KEY1"
"KEY3"
Otherwise it should be:
"KEY1"
"KEY2"
"KEY3"
If all the required keywords are present, and the total ordering is ok, the program should proceed by reading each section in the sequence given by the ordering.
Each section will include a (possibly large) amount of subkeywords, some of which are optional and some of which are not, but here the order does NOT matter.
Lines starting with characters '*' or '--' signify commented lines, and they should be ignored (as well as empty lines).
A line containing a keyword should (preferably) include nothing else than the keyword. At the very least, the keyword must be the first word appearing there.
I have already implemented parts of the framework, but I feel my approach so far has been rather ad-hoc. Currently I have manually created one method per section/main keyword , and the first task of the program is to scan the file for to locate these keywords and pass the necessary information on to the methods.
I first scan through the file using an std::ifstream object, removing empty and/or commented lines and storing the remaining lines in an object of type std::vector<std::string>.
Do you think this is an ok approach?
Moreover, I store the indices where each of the keywords start and stop (in two integer arrays) in this vector. This is the input to the above-mentioned methods, and it would look something like this:
bool readMAINKEY(int start, int stop);
Now I have already done this, and even though I do not find it very elegant, I guess I can keep it for the time being.
However, I feel that I need a better approach for handling the reading inside of each section, and my main issue is how should I store the keywords here? Should they be stored as arrays within a local namespace in the input class or maybe as static variables in the class? Or should they be defined locally inside relevant functions? Should I use enums? The questions are many!
Now I've started by defining the sub-keywords locally inside each readMAINKEY() method, but I found this to be less than optimal. Ideally I want to reuse as much code as possible inside each of these methods, calling upon a common readSECTION() method, and my current approach seems to lead to much code duplication and potential for error in programming. I guess the smartest thing to do would simply be to remove all the (currently 8) different readMAINKEY() methods, and use the same function for handling all kinds of keywords. There is also the possibility for having sub-sub-keywords etc. as well (i.e. a more general nested approach), so I think maybe this is the way to go, but I am unsure on how it would be best to implement it?
Once I've processed a keyword at the "bottom level", the program will expect a particular format of the following lines depending on the actual keyword. In principle each keyword will be handled differently, but here there is also potential for some code reuse by defining different "types" of keywords depending on what the program expects to do after triggering the reading of it. Common task include e.g. parsing an integer or a double array, but in principle it could be anything!
If a keyword for some reason cannot be correctly processed, the program should attempt as far as possible to use default values instead of terminating the program (if reasonable), but an error message should be written to a logfile. For optional keywords, default values will of course also be used.
In order to summarise, therefore, my main questions are the following:
1. Do you think think my approach of storing the relevant lines in a std::vector<std::string> to be reasonable?
This will of course require me to do a lot of "indexing work" to keep track of where in the vector the different keywords are located. Or should I work more "directly" with the original std::ifstream object? Or something else?
2. Given such a vector storing the lines of the text file, how I can I best go about detecting the keywords and start reading the information following them?
Here I will need to take account of possible ordering and whether a keyword is required or not. Also, I need to check if the lines following each "bottom level" keyword is in the format expected in each case.
One idea I've had is to store the keywords in different containers depending on whether they are optional or not (or maybe use object(s) of type std::map<std::string,bool>), and then remove them from the container(s) if correctly processed, but I am not sure exactly how I should go about it..
I guess there is really a thousand different ways one could answer these questions, but I would be grateful if someone more experienced could share some ideas on how to proceed. Is there e.g. a "standard" way of doing such things? Of course, a lot of details will also depend on the concrete application, but I think the general format indicated here can be used in a lot of different applications without a lot of tinkering if programmed in a good way!
UPDATE
Ok, so let my try to be more concrete. My current application is supposed to be a reservoir simulator, so as part of the input I need information about the grid/mesh, about rock and fluid properties, about wells/boundary conditions throughout the simulation and so on. At the moment I've been thinking about using (almost) the same set-up as the commercial Eclipse simulator when it comes to input, for details see
http://petrofaq.org/wiki/Eclipse_Input_Data.
However, I will probably change things a bit, so nothing is set in stone. Also, I am interested in making a more general "KeywordReader" class that with slight modifications can be adapted for use in other applications as well, at least it can be done in a reasonable amount of time.
As an example, I can post the current code that does the initial scan of the text file and locates the positions of the main keywords. As I said, I don't really like my solution very much, but it seems to work for what it needs to do.
At the top of the .cpp file I have the following namespace:
//Keywords used for reading input:
namespace KEYWORDS{
/*
* Main keywords and corresponding boolean values to signify whether or not they are required as input.
*/
enum MKEY{RUNSPEC = 0, GRID = 1, EDIT = 2, PROPS = 3, REGIONS = 4, SOLUTION = 5, SUMMARY =6, SCHEDULE = 7};
std::string mainKeywords[] = {std::string("RUNSPEC"), std::string("GRID"), std::string("EDIT"), std::string("PROPS"),
std::string("REGIONS"), std::string("SOLUTION"), std::string("SUMMARY"), std::string("SCHEDULE")};
bool required[] = {true,true,false,true,false,true,false,true};
const int n_key = 8;
}//end KEYWORDS namespace
Then further down I have the following function. I am not sure how understandable it is though..
bool InputReader::scanForMainKeywords(){
logfile << "Opening file.." << std::endl;
std::ifstream infile(filename);
//Test if file was opened. If not, write error message:
if(!infile.is_open()){
logfile << "ERROR: Could not open file! Unable to proceed!" << std::endl;
std::cout << "ERROR: Could not open file! Unable to proceed!" << std::endl;
return false;
}
else{
logfile << "Scanning for main keywords..." << std::endl;
int nkey = KEYWORDS::n_key;
//Initially no keywords have been found:
startIndex = std::vector<int>(nkey, -1);
stopIndex = std::vector<int>(nkey, -1);
//Variable used to control that the keywords are written in the correct order:
int foundIndex = -1;
//STATISTICS:
int lineCount = 0;//number of non-comment lines in text file
int commentCount = 0;//number of commented lines in text file
int emptyCount = 0;//number of empty lines in text file
//Create lines vector:
lines = std::vector<std::string>();
//Remove comments and empty lines from text file and store the result in the variable file_lines:
std::string str;
while(std::getline(infile,str)){
if(str.size()>=1 && str.at(0)=='*'){
commentCount++;
}
else if(str.size()>=2 && str.at(0)=='-' && str.at(1)=='-'){
commentCount++;
}
else if(str.size()==0){
emptyCount++;
}
else{
//Found a non-empty, non-comment line.
lines.push_back(str);//store in std::vector
//Start by checking if the first word of the line is one of the main keywords. If so, store the location of the keyword:
std::string fw = IO::getFirstWord(str);
for(int i=0;i<nkey;i++){
if(fw.compare(KEYWORDS::mainKeywords[i])==0){
if(i > foundIndex){
//Found a valid keyword!
foundIndex = i;
startIndex[i] = lineCount;//store where the keyword was found!
//logfile << "Keyword " << fw << " found at line " << lineCount << " in lines array!" << std::endl;
//std::cout << "Keyword " << fw << " found at line " << lineCount << " in lines array!" << std::endl;
break;//fw cannot equal several different keywords at the same time!
}
else{
//we have found a keyword, but in the wrong order... Terminate program:
std::cout << "ERROR: Keywords have been entered in the wrong order or been repeated! Cannot continue initialisation!" << std::endl;
logfile << "ERROR: Keywords have been entered in the wrong order or been repeated! Cannot continue initialisation!" << std::endl;
return false;
}
}
}//end for loop
lineCount++;
}//end else (found non-comment, non-empty line)
}//end while (reading ifstream)
logfile << "\n";
logfile << "FILE STATISTICS:" << std::endl;
logfile << "Number of commented lines: " << commentCount << std::endl;
logfile << "Number of non-commented lines: " << lineCount << std::endl;
logfile << "Number of empty lines: " << emptyCount << std::endl;
logfile << "\n";
/*
Print lines vector to screen:
for(int i=0;i<lines.size();i++){
std:: cout << "Line nr. " << i << " : " << lines[i] << std::endl;
}*/
/*
* So far, no keywords have been entered in the wrong order, but have all the necessary ones been found?
* Otherwise return false.
*/
for(int i=0;i<nkey;i++){
if(KEYWORDS::required[i] && startIndex[i] == -1){
logfile << "ERROR: Incorrect input of required keywords! At least " << KEYWORDS::mainKeywords[i] << " is missing!" << std::endl;;
logfile << "Cannot proceed with initialisation!" << std::endl;
std::cout << "ERROR: Incorrect input of required keywords! At least " << KEYWORDS::mainKeywords[i] << " is missing!" << std::endl;
std::cout << "Cannot proceed with initialisation!" << std::endl;
return false;
}
}
//If everything is in order, we also initialise the stopIndex array correctly:
int counter = 0;
//Find first existing keyword:
while(counter < nkey && startIndex[counter] == -1){
//Keyword doesn't exist. Leave stopindex at -1!
counter++;
}
//Store stop index of each keyword:
while(counter<nkey){
int offset = 1;
//Find next existing keyword:
while(counter+offset < nkey && startIndex[counter+offset] == -1){
offset++;
}
if(counter+offset < nkey){
stopIndex[counter] = startIndex[counter+offset]-1;
}
else{
//reached the end of array!
stopIndex[counter] = lines.size()-1;
}
counter += offset;
}//end while
/*
//Print out start/stop-index arrays to screen:
for(int i=0;i<nkey;i++){
std::cout << "Start index of " << KEYWORDS::mainKeywords[i] << " is : " << startIndex[i] << std::endl;
std::cout << "Stop index of " << KEYWORDS::mainKeywords[i] << " is : " << stopIndex[i] << std::endl;
}
*/
return true;
}//end else (file opened properly)
}//end scanForMainKeywords()
You say your purpose is to read initialization data from a text file.
Seems you need to parse (syntax analyze) this file and store the data under the right keys.
If the syntax is fixed and each construction starts with a keyword, you could write a recursive descent (LL1) parser creating a tree (each node is a stl vector of sub-branches) to store your data.
If the syntax is free, you might pick JSON or XML and use an existing parsing library.

Save/Load workspace in C++ application

I'm working on adding a new feature to an existing program. It's basically a save/load workspace feature, where a user can save the positions of their windows, and then load said positions whenever they want to by selecting a menu item. In order to implement this, I have created code which extracts the screen coordinates of the window and writes them to a file (below) :
void CMainFrame::SaveWorkspace()
{
RECT ctrlsize;
m_pDialog->GetWindowRect((LPRECT)&ctrlsize); //obtains location for window
ofstream Workspace("saveone", ios::out);
Workspace << ctrlsize.left << "," << ctrlsize.top << "," << ctrlsize.right << "," << ctrlsize.bottom;
}
And this (is supposed to) loads the workspace:
void CMainFrame::LoadWorkspace()
{
//Read in the data from the file
int data[3][4];
int r=0;
int a=0;
int b=0;
ifstream infile;
infile.open("saveone");
for(a = 0; a< 3; a++)
{
for(b = 0;b<4;b++)
{
infile >> data[a][b];
cout << data[a][b];
}
}
infile.close();
//Now, assign the extracted values
RECT ctrlset;
ctrlset.top = data[0][1];
ctrlset.left = data[0][0];
ctrlset.right = data[2][0];
ctrlset.bottom = data[0][3];
// Finally, reassign the window positions
m_pDialog->SetWindowPos(NULL, ctrlset.left, ctrlset.top, (ctrlset.right - ctrlset.left), (ctrlset.bottom - ctrlset.top), SWP_SHOWWINDOW);
}
Problems:
1) the SaveWorkspace function works sporadically; more often than not, it doesn't create a file.
2) the LoadWorkspace function doesn't work. Specifically, only the data[0][0] coordinate gets saved to the array (the first value in the file).
This seems like a fairly easy thing to do, I'm a bit embarrassed that it's giving me so much trouble...
EDIT: I've fixed problem #1. Now I just need to figure out why my array isn't working.
You have at least two problems in the reading.
Your array definition is wrong. It is :
data[2][3];
This has only 6 values.
However, in the loop you are reading 12 values out.
You have the "," values in the file. You are not getting rid of them.
Maybe as an easy solution, you could add a new line after each entry when you write them.
Or you could enter the details of a single rectangle on one line, then read the full line and parse for the individual components yourself.