Extracting a specific line from a file

Pages: 12
!Changed title from '2D array for mixed types' to better reflect contents of this thread!

Hi again. Not sure if it belongs here, though i am a total newb.
I am trying to add a matrix of 6 columns and 34 rows with mixed types inside it. The format is constant, being:

1
2
3
4
5
6
        col_1  col_2  col_3  col_4  col_5  col_6
row_1  string   int  double double double double
row_2...........................................
row_3...........................................
etc
row_34..........................................


What i found on the net is this (changed slightly to use my names and types)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  struct ForTableI
{
    enum {is_int, is_double, is_string} type;
    union
    {
        int ival;
        double dval;
        std::string sval;
    }
    val;
}
table_i[6][34];
table_i[0][0].type = is_string;
table_i[0][0].val.sval = Be;


My problems. Firstly, i am running into a use-of-deleted-function problem.

||=== Build: Release in Crystal (compiler: GNU GCC Compiler) ===|
(...)\Crystal\crystal_datainput.cpp|192|error: use of deleted function 'ForTableI::ForTableI()'|
(...)\Crystal\crystal_datainput.cpp|181|note: 'ForTableI::ForTableI()' is implicitly deleted because the default definition would be ill-formed:|
(...)\Crystal\crystal_datainput.cpp|181|error: use of deleted function 'ForTableI::<anonymous union>::<constructor>()'|
(...)\Crystal\crystal_datainput.cpp|185|note: 'ForTableI::<anonymous union>::<constructor>()' is implicitly deleted because the default definition would be ill-formed:|
(...)\Crystal\crystal_datainput.cpp|188|error: union member 'ForTableI::<anonymous union>::sval' with non-trivial 'std::basic_string<_CharT, _Traits, _Alloc>::basic_string() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'|
(...)\Crystal\crystal_datainput.cpp|181|error: use of deleted function 'ForTableI::<anonymous union>::~<constructor>()'|
(...)\Crystal\crystal_datainput.cpp|185|note: 'ForTableI::<anonymous union>::~<constructor>()' is implicitly deleted because the default definition would be ill-formed:|
(...)\Crystal\crystal_datainput.cpp|188|error: union member 'ForTableI::<anonymous union>::sval' with non-trivial 'std::basic_string<_CharT, _Traits, _Alloc>::~basic_string() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'|
(...)\Crystal\crystal_datainput.cpp|192|error: use of deleted function 'ForTableI::~ForTableI()'|
(...)\Crystal\crystal_datainput.cpp|181|note: 'ForTableI::~ForTableI()' is implicitly deleted because the default definition would be ill-formed:|
(...)\Crystal\crystal_datainput.cpp|181|error: use of deleted function 'ForTableI::<anonymous union>::~<constructor>()'|
(...)\Crystal\crystal_datainput.cpp|192|error: use of deleted function 'ForTableI::~ForTableI()'|
(...)\Crystal\crystal_datainput.cpp|193|error: expected unqualified-id before '{' token|
(...)\Crystal\crystal_datainput.cpp||In function 'void __static_initialization_and_destruction_0(int, int)':|
(...)\Crystal\crystal_datainput.cpp|192|error: use of deleted function 'ForTableI::~ForTableI()'|
(...)\Crystal\crystal_datainput.cpp||In function 'void __tcf_1()':|
(...)\Crystal\crystal_datainput.cpp|192|error: use of deleted function 'ForTableI::~ForTableI()'|
||=== Build failed: 11 error(s), 0 warning(s) (0 minute(s), 3 second(s)) ===|


Not sure how to handle that.

Secondly, i do not know if this approach is correct.

The point of this is to be able to use a line (row) of variables as parameters for another function, except the string which will be a name that will call (point,define,specify) the rest.

Example:

1
2
3
4
5
6
7
std::string input;
std::cin >> input; //calling a specific row
...
magic
...
x = (&const so it doesnt copy everything all the time) table_i[0][1]+[0][2]*[0][3]+etc
return x;


and this will cause the rest (int and doubles) to be used as arguments elsewhere.

Writing this i am starting to think that i went overboard :/ But i need a way of doing this or something similar, as making 34 structs and 34 variations of functions using these arguments doesn't seem bright.

The general idea is this:
1
2
3
4
ask the user to choose material;
input of material;
lookup of material (int and doubles describe properties)
use properties in equation;
Last edited on
You know the type of each variable (column); a union like class is not required.

Something like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct row // define a row
{
    std::string  col_1 ;
    int          col_2 = 0 ;
    double       col_3 = 0 ;
    double       col_4 = 0 ;
    double       col_5 = 0 ;
    double       col_6 = 0 ;
};

std::istream& operator>> ( std::istream& stm, row& r ) // read a row from input stream
{ return stm >> r.col_1 >> r.col_2 >> r.col_3 >> r.col_4 >> r.col_5 >> r.col_6 ; }

std::ostream& operator<< ( std::ostream& stm, const row& r ) // write a row to output stream
{
    const char tab = '\t' ;
    return stm << r.col_1 << tab << r.col_2 << tab << r.col_3 << tab
               << r.col_4 << tab << r.col_5 << tab << r.col_6 ;
}


ant then:
1
2
3
4
5
6
7
8
constexpr std::size_t NROWS = 34 ;
row table[NROWS] ; // define the table

for( row& r : table ) std::cin >> r ; // read rows from stdin

for( const row& r : table ) std::cout << r << '\n' ;

// etc. 
So, if i understand correctly, the solution could be to make a 34x6 table, extract a row based on first item in a row, then put it into a struct and finally use the variables from the struct ? A middle man solution, so to say.
gentleguy wrote:
Can you post the full code?

Sorry for missing your post. What i posted is most of it. Currently i am looking for more of an idea i can learn ;)
Last edited on
I have failed. I do not understand how to implement that to read a particular row of an 2d array.
closed account (48T7M4Gy)
You can't have it both ways. It is either a 2d array or not.

If you want a 1d array of structs, you have the answer, which I recognise you don't like, Ater. That's OK.

The second alternative is to use a 2d array which is what you want. Now, assuming it is to be a 2d c-style array you can only have a single data type (unless it's a 2d array of structs, but that is getting a bit silly maybe), so as a single data type why not use a string and write a couple of functions to convert as required?

( For proof that it's only one data type just ask yourself how you declare the array,you can only say string arr_2D[6][] etc )
Perhaps i was unclear in what i wanted to do, so i'll try explaining again.

There is a table (a data sheet of material properties) with 6 columns and 34 rows. The format is:
 
string int double double double double

The string is a name of an element, the int is mass and the doubles are more bizzare parameters.
I want to be able to extract a single row that contains the properties of a single material. The string is only for identification, but the rest are used in two calculations (calculation #1 relies on calculation #2, so thoes can be put one after the other).

I wanted to use an array because i thought it to be the most concise way of doing said calculations (write an array->figure out how to extract row->calculate). Contrasted with writing 68 (34(rows/properties)* 2(interdependant calculations))(actually i am omiting some variables, so that number might be closer to 200, but i if learn how to do a 6x34 i can expand upon it to as much as i need). The exact row to be extracted bases on user input (string/name of element).

Edit. I'd rather the table be inside the program, but a file with it is also fine.
Edit2. Maybe change string to int via enum ?
Edit3. Example:
 
std::string target = Al, int Z2 = 13, double Us = 3.39, double QZ2 = 1, double WZ2 = 2.17, double sZ2= 2.5 // single row 
Last edited on
closed account (48T7M4Gy)
I want to be able to extract a single row that contains the properties of a single material.

Maybe I understand what your problem is so I'll have another go on the basis that it is a stock file reading exercise?? I'm using the info in the IO tutorial on this site.

1
2
3
4
string xyz;
int number;
double truble;
etc

now once the file is opened and a few other simple things:

myfile >> xyz >> number >> truble >> ... ; as required, it's that easy!

It might mean you have to export from a spreadsheet or whatever but the idea is essentially the same.

http://www.cplusplus.com/doc/tutorial/files/

That's the easy way to read your data and each line above is multi-typed so I can see a struct or full blown class, even using a <map> could be useful once you have done the initial extraction.
Last edited on
No, not exactly that.

If i have 3 lines in the file:

C 6 7.37 1.70 1.84 2.5 //line_1
Al 13 3.0 3.39 1 2.17 2.5 //line_2
Si 14 4.63 0.66 2.32 2.6 //line_3


i want to type into console Al and that will cause the line_2 to be read and assigned to variables in the program. Only that line, because i will need only Al-related variables.

And i'm checking out <map> now.
Last edited on
closed account (48T7M4Gy)
It's still the same. Read each line of the file and check string xyz each time
1
2
if(searchstring == xyz)
cout << "bingo";


the way a <map> works is you create a map of structs with the key value being the string component. that map is read in from the file in its entirety to load up the map in computer memory.

You then search the map by a direct key search and bingo. <maps> are one of the STL containers and you can read about them in the tutorials or reference section here.

Won't i have to write an if-else (or a swith) for every possible material (34 in total) ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
struct Yield //to keep the values
{
    std::string material;
    long double E;
    long double Eth;
    long double M1;
    long double M2;
    long double QZ1;
    long double QZ2;
    long double alpha;
    long double Us;
    long double SnE;
};

int main()
{
std::ifstream file (".//database//yield.txt");//might change to .dat for +10% coolness
Yield vbl;
std::cout << "Enter material abbrv."
std::string searchstring;
std::cin >> searchstring;

    do
    {
        if (!file.is_open())
            {
            std::cerr << "Unable to locate file.";
            break;
            }
        else
            {
            if (searchstring == material_name_by_user_is_on_list) //one of my problems
                {
                file >> vbl.E >> vbl.Eth >> vbl.M1 >> vbl.M2 >> vbl.QZ1 >> vbl.QZ2 >> vbl.alpha >> vbl.Us >> vbl.SnE)

                long double gamma = vbl.e*vbl.Eth; //just an example
                long double Y = gamma/vbl.M1; //ditto
                std::cout << "Y=" << Y << for << searchstring << "\n"; //will be also appended to a file

                }
            else
                {
                std::cerr << "Material not in database";
                break;
                }
            }
    }
    while (!std::cin.fail()); //another of my problems
    file.close();
}


Need a way of formulating the condition in line 31. Also don't know how to handle 47, as cin will fail at the end of file and i need it to fail at the end of line. And my biggest problem is stoping reading after a line, because i think cin will fill up the struct and then keep going 'till eof (line 31 again).
Last edited on
closed account (48T7M4Gy)
Won't i have to write an if-else (or a swith) for every possible material (34 in total) ?


Not at all, that would be horrendous if any human was expected to do that. :)

Do it in steps:
1a. Create a struct as you have been shown above.
1b. create a <map>
2. Declare one instance of it say temp.
3. Now read in each line myfile >> temp.xyz >> temp.more_stuff etc.
4. Since you now have an object with all it's members filled, place temp in the <map> using the member you have selected at the key.
5. Loop (automatically via the while) through all the file lines.
6. close the file if you like.
7. Now separetely interrogate the contents of the map via a key matching exercise. It's a on-liner because a <map> knows how to look for things.
8. Get the matching object and deconsruct/process it as you want. :)

Why have you got a ')' at the endlof line 33?
Thanks for the explanation, i will try doing that.

Same reason you typed 33, a typo :P
Completely diffrent from what i was trying to do. Much to learn i have.
closed account (48T7M4Gy)
Yeah, I hope it is worthwhile to you Here is the code I posted a short while ago. I took it down because it is only reading the first two Elements and should read all 3. I haven't fathomed why yet( it will be an ah derr moment when I do, but here it is:

PS It's all fixed and working properly. Your data contains an extra 1 and is therefore corrupt (more or less because maybe it's an intended additional complexity)

elements.txt

C   6 7.37 1.70 1.84 2.5
Al 13  3.0 3.39 2.17 2.5
Si 14 4.63 0.66 2.32 2.6


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <map>

#include <fstream>
#include <string>

int main ()
{
    struct Element
    {
        std::string name;
        int e1;
        double e2;
        double e3;
        double e4;
        double e5;
    };
    
    Element temp;
    std::map<std::string, Element> Elephant_Map;
    std::map<std::string, Element>::iterator it;
    std::string key = " ";
    
    std::ifstream myfile ("elements.txt");
    if (myfile.is_open())
    {
        while ( myfile >> temp.name >> temp.e1 >> temp.e2 >> temp.e3 >> temp.e4 >> temp.e5)
        {
            Elephant_Map.insert ( std::pair<std::string, Element>(temp.name, temp) );
            std::cout << temp.name << '\n';
        }
        myfile.close();
    }
    
    else
        std::cout << "Unable to open file";
    
    std::cout << "Please enter a key: ";
    std::cin >> key;
    
    it = Elephant_Map.find( key );
    
    if (it != Elephant_Map.end())
        std:: cout << it->second.name << '\t' << it -> second.e1 + it->second.e3 << '\n';
    
    return 0;
}

C
Al
Si
Please enter a key: Si
Si 14.66
Program ended with exit code: 0
Last edited on
Thanks for the update. I was wondering why you deleted your post. After i implement it, i will run it with my numbers. Using the map as a function argument will require to put something like long double calcBizzaro(const Element &temp); into the header ?
closed account (48T7M4Gy)
Good, I took it down because there is nothing worse than showing somebody something and it doesn't work. I knew the '1' was a problem but the way I set it up in XCode the deletion didn't work because the real data file was hidden. ah derrr ...

Anyway enough excuses.

My code stands alone so any header I assume is a function you have and just put it in as you normally would with any function including the Element structure going in after the struct block.

I was playing around and modified the struct to include the abiliity for an Element to display() itself, no pun intended.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Element
    {
        std::string name;
        int e1;
        double e2;
        double e3;
        double e4;
        double e5;
        
        void display()
        {
            std::cout
            << std::setw(5) << name
            << std::setw(6) << e1
            << std::setw(6) << e2
            << std::setw(6) << e3
            << std::setw(6) << e4
            << std::setw(6) << e5 << '\n';
        }
    };


    C     6  7.37   1.7  1.84   2.5
   Al    13     3  3.39  2.17   2.5
   Si    14  4.63  0.66  2.32   2.6
Please enter a key: Al
Al	16.39
   Al    13     3  3.39  2.17   2.5
Program ended with exit code: 0



Last edited on
I made a mistake and i do not understand what the compiler wants from me.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
struct Element
{
    std::string name;
    int M1;
    int M2;
    double E;
    double Us;
};

long double calcAlphaYield(const Element &tempmaterialmap)
{
    long double M1;
    long double M2;
    long double alpha;

    alpha = 0.249*pow(M2/M1,0.56)+0.0035*pow(M2/M1,1.5);

    return alpha;
}

long double calcYield(const Element &tempmaterialmap)
{
    long double M1;
    long double M2;
    long double E;
    long double Us;
    long double alpha = calcAlphaYield(tempmaterialmap);
    long double Y;

    Y = (3/(4*pow(M_PI,2)))*alpha*((4*M1*M2)/(pow(M1+M2,2)))*(E/Us);

    return Y;
}

int main
{
    Element tempmaterialmap;
        std::map<std::string, Element> Properties_Map;
        std::map<std::string, Element>::iterator it;
        std::string key = " ";

        std::ifstream myfile ("elements.txt");
    if (myfile.is_open())
    {
        while ( myfile >> tempmaterialmap.name >> tempmaterialmap.M1 >> tempmaterialmap.M2 >> tempmaterialmap.E >> tempmaterialmap.Us)
        {
            Properties_Map.insert ( std::pair<std::string, Element>(tempmaterialmap.name, tempmaterialmap) );
            std::cout << tempmaterialmap.name << '\n';
        }
        myfile.close();
    }

    else
    {
        std::cout << "Unable to open file";
    }

    std::cout << "Please enter abbreviated element name: ";
    std::cin >> key;

    it = Properties_Map.find( key );

    if (it != Properties_Map.end())
    long double alpha = calcAlphaYield(const Element &tempmaterialmap);
    long double Y = calcYield(const Element &tempmaterialmap);

    std::cout << "Y=" << Y;
    std::ofstream o("Yield.txt", std::ios::app);
    o << currentDateTime() << "\t" << Y << std::endl;
}


Error log

||=== Build: Release in Crystal (compiler: GNU GCC Compiler) ===|
(...)\C++\Crystal\crystal_header.h||In constructor 'Element::Element()':|
(...)\C++\Crystal\crystal_header.h|66|warning: 'Element::name' should be initialized in the member initialization list [-Weffc++]|
(...)\C++\Crystal\crystal_header.h|66|warning: 'Element::M1' should be initialized in the member initialization list [-Weffc++]|
(...)\C++\Crystal\crystal_header.h|66|warning: 'Element::M2' should be initialized in the member initialization list [-Weffc++]|
(...)\C++\Crystal\crystal_header.h|66|warning: 'Element::E' should be initialized in the member initialization list [-Weffc++]|
(...)\C++\Crystal\crystal_header.h|66|warning: 'Element::Us' should be initialized in the member initialization list [-Weffc++]|
(...)\C++\Crystal\crystal_functions.cpp||In function 'void calcYieldFromFile()':|
(...)\C++\Crystal\crystal_functions.cpp|292|note: synthesized method 'Element::Element()' first required here |
(...)\C++\Crystal\crystal_functions.cpp|319|error: expected primary-expression before 'const'|
(...)\C++\Crystal\crystal_functions.cpp|319|warning: unused variable 'alpha' [-Wunused-variable]|
(...)\C++\Crystal\crystal_functions.cpp|320|error: expected primary-expression before 'const'|
||=== Build failed: 2 error(s), 6 warning(s) (0 minute(s), 4 second(s)) ===|
Last edited on
closed account (48T7M4Gy)
You should really put all of your code so I don't have to work out what you have or have not #included etc. :)
Sorry about that.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <math.h>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <fstream>
#include <ostream>
#include <conio.h>
#include <limits>
#include <string>
#include <windows.h>
#include <stdio.h>
#include <ctime>
#include <map> 
Pages: 12