How to read lines from a fiile, then split each line into words without arrays?

Pages: 12
OK, I've done my best to provide a minimalist version as close to yours as possible. (You will have to put the rest of your functions back.)

In order to use it for non-member functions (like the overloaded << operator) I'm afraid I've been forced to create a separate templated Node<M> class. Better people than I will be abled to get it inside the linked-list class itself, I'm sure - but I don't know how to, and all the implementations that I've seen have it as a separate class. I don't like nesting one class (or struct) inside another.

This would inevitably affect your other member functions, I'm afraid. Note that Node<> is now templated (and, as befits a class, I've given it a capital letter at the start).
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
#include <initializer_list>
using namespace std;


template <class M>
class Node
{
public:
   M data;
   Node<M> *nxt;
   Node( M val ) : data( val ) { nxt = NULL; };
};


template <class M> class
LinkedList
{
private:
   Node<M> *head;
   Node<M> *tail;

public:
   LinkedList() { head = NULL;   tail = NULL; }
   LinkedList( initializer_list<M> lst );
   void push_back( M val );
   void multidisplay();
   template <class T> friend ostream &operator << ( ostream &strm, LinkedList<T> L );
};


template <class M> void LinkedList<M>::push_back( M val )
{
   Node<M> *n = new Node<M>( val );
   if ( head == NULL ) head       = n;
   else                tail->nxt = n;   
   tail = n;
}


template <class M> LinkedList<M>::LinkedList( initializer_list<M> lst )
{
   head = NULL;
   tail = NULL;
   for ( M val : lst ) push_back( val );
}


template <class M> void LinkedList<M>::multidisplay()
{
   int counter = 0;
   Node<M> *n = head;
   while( n != NULL )
   {
      counter++;
      cout << "Line " << counter << endl;
      cout << n->data << "\n\n";
      n = n->nxt;
   }
   cout << endl;
}


//======================================================================


template <class T> ostream &operator << ( ostream &strm, LinkedList<T> L )
{
   Node<T> *n = L.head;
   while( n != NULL )
   {
      strm << n->data << " ";
      n = n->nxt;
   }
   return strm;
}


//======================================================================


int main()
{
   LinkedList< LinkedList<string> > LL{ { "This", "is", "the", "winter" }, { "Of", "our", "discontent" } };
   LL.multidisplay();
}


Line 1
This is the winter 

Line 2
Of our discontent 

Last edited on
On the "template <class M> LinkedList<M>::LinkedList( initializer_list<M> lst )"

it gives this error

Problem description: Symbol 'initializer_list' could
not be resolved

and is "#include <initializer_list>" from the STL library?
Last edited on
Have you
#include <initializer_list>
???

See the full code sample.

It's just a standard header. It might need c++11. It runs fine in c++ shell.
http://www.cplusplus.com/reference/initializer_list/

It's just a quick way of populating the list for demonstration. You can bypass it and remove it from the code once you've created your list of lists some other way.
Last edited on
Have you
#include <initializer_list>
???

I did, I was just wondering if that was from the STL library.
I repeat - it's a standard header (in c++11, anyway).

Once you have got data into your linked list some other way then you can remove mention of it entirely. It's just that "some other way" is potentially quite a lot of lines of code.


EDIT: OK - here's a version with no mention of initializer lists, getting the data as earlier in this thread. This is a singly-linked list. You can put your prv pointer in Node<> as and when if you want a doubly-linked list.

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;


template <class M>
class Node
{
public:
   M data;
   Node<M> *nxt;
   Node( M val ) : data( val ) { nxt = NULL; };
};


template <class M> class
LinkedList
{
private:
   Node<M> *head;
   Node<M> *tail;

public:
   LinkedList() { head = NULL;   tail = NULL; }
   void push_back( M val );
   void multidisplay();
   template <class T> friend ostream &operator << ( ostream &strm, LinkedList<T> L );
};


template <class M> void LinkedList<M>::push_back( M val )
{
   Node<M> *n = new Node<M>( val );
   if ( head == NULL ) head      = n;
   else                tail->nxt = n;   
   tail = n;                         
}


template <class M> void LinkedList<M>::multidisplay()
{
   int counter = 0;
   Node<M> *n = head;
   while( n != NULL )
   {
      counter++;
      cout << "Line " << counter << endl;
      cout << n->data << "\n\n";
      n = n->nxt;
   }
   cout << endl;
}


//======================================================================


template <class T> ostream &operator << ( ostream &strm, LinkedList<T> L )
{
   Node<T> *n = L.head;
   while( n != NULL )
   {
      strm << n->data << " ";
      n = n->nxt;
   }
   return strm;
}


//======================================================================


LinkedList< LinkedList<string> > getData()
{
   LinkedList< LinkedList<string> > ListOfLists;
   string line, word;

   ifstream in( "input.txt" );
   while ( getline( in, line ) )
   {
      LinkedList<string> LineList;               // Create an ordinary list for each new line
      stringstream ss( line );
      while ( ss >> word )
      {
         LineList.push_back( word );             // Populate the list for that line
      }
      ListOfLists.push_back( LineList );         // At the end of the line put this in the list of lists
   }
   in.close();
   
   return ListOfLists;
}


//======================================================================

int main()
{
   LinkedList< LinkedList<string> > LL = getData();
   LL.multidisplay();
}


Donald Trump is building a wall;
Donald Trump wants it fifty feet tall;
All the Feds under President's orders
Can't plug the holes in America's borders.


Line 1
Donald Trump is building a wall; 

Line 2
Donald Trump wants it fifty feet tall; 

Line 3
All the Feds under President's orders 

Line 4
Can't plug the holes in America's borders. 
Last edited on
EDIT: OK - here's a version with no mention of initializer lists, getting the data as earlier in this thread.


That works great when I tested it (as is not with my own data yet), but isn't that just a singly linked list? I need to make a doubly linked list. Would it be similar and I would just add a prev pointer with the next?
Yes, it's a singly-linked list because this example didn't merit any more complexity. Put your prev pointer back in Node if you want a doubly-linked version. You will have to adjust the push_back routine accordingly. I think it just amounts to n->prev = tail; immediately after creating n.
Last edited on
so like this?
1
2
3
4
5
6
7
8
9
10
11
12
void LinkedList<M>::push_back(M info)
{
	Node<M> *n = new Node<M>(info);

	n->prev = tail;

	if (head == NULL)
		head = n;
	else
		tail->next = n;
	tail = n;
}


EDIT: I got that working. I also made another function that displays the lines in reverse order and that works too. Now I just need to be able to print the words in each line in reverse order...

EDIT: Figured that out. I made a push_front function and used it to insert the words into a list

That's pretty much all I needed! Last thing I need to do is determine how much memory the lists use, but I'm going to go to google to try to find that for a while. So far all I got is "8 (prev)+8 (next) + sizeof(string)" as a hint....

Thank you so much for all your help and patience!!!
Last edited on
Ok I may need a bit more help. I just found out that I can't format the words before I put them in the lists, I have to put them in the lists, then delete the node a word is in if it contains an invalid character. I was just formatting the line before I put it into the stringstream, but that's apparently not what I am supposed to do.

I already have a format function that accepts a string and a boolean variable. If the bool is true, it removes all symbols AND numbers, but if it is false it only removes the symbols and leaves the numbers. Also if there is something like "abc123" and the bool is false, then it has to change it to "abc 123".

My function does this all, so I am wondering if there is a fairly simple way to implement it inside of a delete() function. Any ideas?

The other issue I am having is determining the amount of memory the list is using. I'm supposed to use something like that "8 (prev)+8 (next) + sizeof(string)" to check it. I guess I need to iterate through every sublist and add this somehow. I'm just sort of clueless on how to do these things.
I'm sorry, @Metalman488, but I simply don't understand what you are asking in the first three paragraphs.

As far as memory is concerned, you've basically got two pointers and a load of nodes. sizeof() will tell you the size of a node.
I have to read the words from the file. The file will either have Clean = 'Y' or Clean = 'N'.

If Clean = 'N', then I have to delete all nodes containing symbols (like +,-'/*!@#$%, etc. Anything not a letter or number) and if there are strings like "abc123" I need to split them into two different nodes, "abc" and "123". Also if there is a word like "don't" it would be split into "don" and "t".

If Clean = 'Y', then I would need to do same as before, except I also need to delete any nodes containing numbers.

About memory, so would the size be the same for every node or would it depend on what string is in the node?

EDIT: So I just figured out that sizeof(string) is same for any string. For some reason I thought it would depend on the length of the string. Now I just need to figure out how to check memory for the lists of lists. I see that for the lists that contain the lines, I just need to add a counter to see how many nodes there are and multiply that by the equation, but I'm not sure what to do for the list of lists. Right now I just have it subtract the number of lines from the number of total nodes, so that I only count the nodes from the line lists.

EDIT: So I found out that all I got to do for the memory is count up all the nodes containing words and multiply it by "8+8+sizeof(string)". So I figured out my memory problem.

On the formatting I found out that the only symbols that may be in the file are , and maybe ', and I am allowed to delete the symbols before inserting anything in the list. So now all I need to do is make a function that will check each node and if the node contains a number that node gets deleted.

I know how to check strings for numbers, but I'm not sure how to do it with nodes
Last edited on
Ok, the only problem that I am having now is how to implement a delete function. I have a delete function that accepts a string and if the data inside a node is equal to that string it deletes it.

What I cannot figure out is how to implement it with a list of lists.
I wrote this code to traverse a list and look for nodes that have a number as the first character in their data:

1
2
3
4
5
6
7
8
Node *n = head;
	while (n != NULL)
	{
		if (isdigit(n->data[1]))
			deleteNode(n->data);
		n=n->next;
	}


I wrote this on a separate test program I have and it only has one doubly linked list and its not templated. I am not sure how to modify this to work with a templated list or a list of lists. This method does exactly what I need on my test program though.
Topic archived. No new replies allowed.
Pages: 12