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

Pages: 12
This may be a dumb question, but I am trying to figure out how I can read each line from a file, then split each line into words without the use of arrays.

I need to make a linked list for each line of the file and each word of a line needs to be a node. I'm then going to need to make a linked list that holds all the line linked lists, but I have a different forum post on here about that....

For this I just need to know how to split the lines without using an array.

Thanks in advance!
Last edited on
You could put line into a stringstream and then stream the words out of that.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

int main()
{
   string line, word;
   int counter = 0;

   ifstream in( "input.txt" );
   while ( getline( in, line ) )
   {
      counter++;
      cout << "\nLine " << counter << ":\n";
      stringstream ss( line );
      while ( ss >> word ) cout << word << '\n';
   }

   in.close();
}


Once more unto the breach, dear friends, once more;
Or close the wall up with our English dead!

Line 1:
Once
more
unto
the
breach,
dear
friends,
once
more;

Line 2:
Or
close
the
wall
up
with
our
English
dead!


If you use the same stringstream repeatedly, you will need to clear and empty it after every line.

You will probably have to filter the words to remove any punctuation immediately before or after, and maybe put them in upper or lower case. Up to you.
You could put line into a stringstream and then stream the words out of that.


Yeah I was thinking of that. My only issue with that is that I can't figure out how to put the words from each line into a linked list. I currently have an array of linked lists (which I really need to change into a linked lists of linked lists, but I haven't found anything helpful online or my other post yet) and I tried to replace
while ( ss >> word ) cout << word << '\n';
with
1
2
3
4
while ( ss >> word )
	      {
	    	  words[0].Insert(word);
	      }

but it just separates the entire file into words and puts them all into the first linked list (I did this just to see if it when I printed out words[0], it would show only the contents of the first line), so then I tried
1
2
3
4
5
6
int i = 0;
	      while ( ss >> word )
	      {
	    	  words[i].Insert(word);
	    	  i++;
	      }

but now it only shows the first word of each line and I have no idea how that happened.

Apparently I am allowed to use arrays, just not to store the entire line.
A hint I was given was to use a char array. I'm guessing that I need to use that to search for spaces and everytime it finds one it adds the word to the list, then moves on until it find the end of the line (marked by '\n' ?) I'm still not sure how to implement that.

Last edited on
Doubly linked list or singly linked list?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <fstream>
#include <iostream>
#include <list>

int main()
{
   std::ifstream in("loremipsum.txt");

   std::string word;
   std::list<std::string> words;
   while(in >> word) {
       words.push_back(word);
   }
   in.close();
   
   std::ofstream out("word_by_word.txt");
   for(const auto& s : words) { out << s << '\n';}
   out.close();

   return 0;
}


loremipsum.txt (part):

Lorem ipsum dolor sit amet, ei usu exerci vocibus invidunt, ea tantas civibus sed, ne vitae alterum probatus eam. Assum ornatus ius ea, autem commodo tamquam eam ex. Est habeo signiferumque an, ut his novum definitionem. Quod hendrerit omittantur ad duo, ei usu diceret delicata. Te cum populo scripta gubergren.

Duo vide consul et, ne est abhorreant moderatius. Option partiendo eos eu, per ea timeam mandamus mediocritatem. Nec at minim sonet consequuntur. Sea eu semper aliquid volumus, et solum copiosae pertinax eum. Possit dictas facilisis mei ex.

Mundi persius platonem no vim. Sea no aperiam omittantur. Ad vel sanctus utroque assentior, mei ei aeque suscipit patrioque. Alia mundi postulant nam eu, his at amet feugait hendrerit. Ex quis dicam aeterno sea, rebum recusabo et mel.
. . .


word_by_word.txt (part):

Lorem
ipsum
dolor
sit
amet,
ei
usu
exerci
vocibus
invidunt,
ea
tantas
civibus
sed,
ne
vitae
alterum
probatus
eam.
Assum
ornatus
ius
ea,
autem
commodo
tamquam
eam
ex.
Est
habeo
signiferumque
an,
ut
his
novum
definitionem.
Quod
hendrerit
. . .

Doubly linked list or singly linked list?


Doubly. Sorry, forgot to mention that.

I'm also not allowed to use the linked list template in the STL library.

And doesn't the code you posted above just put the entire file into one linked list? I need each line to be in a different linked list.
Last edited on
I need to make a linked list for each line of the file and each word of a line needs to be a node.

Sorry, I’ve missed that point. Let’s reverse to lastchance’s code :)

Hints:
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
#include <fstream>
#include <iostream>
#include <list>
#include <sstream>
#include <string>
#include <vector>

int main()
{
    std::ifstream in("loremipsum.txt");

    std::string line;
    std::vector<std::list<std::string>> lines;
    while(std::getline(in, line)) {
        std::list<std::string> words;
        std::istringstream ss(line);
        std::string word;
        while(ss >> word) {
            words.push_back(word);
        }
        lines.push_back(words);
    }
    in.close();

    std::ofstream out("word_by_word.txt");
    int i {};
    for(const auto& l : lines) {
        out << "Line " << ++i << ":\n";
        for(const auto& s : l) { out << s << '\n';}
        out << '\n';
    }
    out.close();

    return 0;
}


It doesn’t manage empty lines correctly.

I'm also not allowed to use the linked list template in the STL library.

Well, ok, let's say what above is just one of the possible logics - you could easily change from std::vector into a std::list of std::lists.
If the code you have for doubly linked list works differently, maybe even the logic needs to change. Sorry.
isn't
words.push_back(word);
the vector's insert function? I keep trying to replace in with my linked list's insert function, but it just inserts all words into one linked lists instead of doing a line in each list.
I don't see why you need a list of lists - it sounds like a modern-day Domesday book. A vector< vector<string> > would probably be far easier.

Also, list.insert() is NOT the same as list.push_back(). See the reference section.

A list is traversable, but tends not to be indexable. So words[0] or words[i] would be fine for a vector but not for a list.

If you are writing your own linked list then do so for a generic type T using a template. (I'm sure this has been pointed out on your other thread.) There is nothing sacrosanct about strings.

The following uses std::list. Just replace that with your own if required.
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
#include <iostream>
#include <fstream>
#include <sstream>
#include <list>
#include <string>
using namespace std;

int main()
{
   list< list<string> > ListOfLists;
   string line, word;

   ifstream in( "input.txt" );
   while ( getline( in, line ) )
   {
      list<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();


   int counter = 0;                              
   for ( auto L : ListOfLists )                                           // Now traverse the lines ...
   {
      counter++;
      cout << "\nLine " << counter << ":\n";
      for ( auto p = L.begin(); p != L.end(); p++ ) cout << *p << '\n';   // ... printing out words
   }
}


With apologies to Humpty Dumpty ...
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
closed account (48T7M4Gy)
@OP

DUPLICATE POSTS ARE BAD.

Why have you opened two threads at least on this same subject. Stop wasting people's time and effort.

I don't care whether you take my or any other advice or not but you're a pest duplicating your questions and it's unfair on the people who take the time and trouble to contribute to help you and also to the other people asking for advice.

Sign off by green ticking one or all of your posts!

http://www.cplusplus.com/forum/beginner/221640/
lastchance, let me compliment you on introducing a poem in a C++ post :) (besides your good advice, ça va sans dire).
Cheers @Enoizat. Sometimes writing dire poetry is welcome relief from trying to write code to cover all possibilities of list<vector<string>> or vector<list<string>> or list<list<string>> or even some grand templated container of everything.
1
2
3
4
5
6
for ( auto L : ListOfLists )                                           // Now traverse the lines ...
   {
      counter++;
      cout << "\nLine " << counter << ":\n";
      for ( auto p = L.begin(); p != L.end(); p++ ) cout << *p << '\n';   // ... printing out words
   }


Ok I got my template built and I put the code in to make the List of Lists and everything, but I'm not understanding how to print the lists. What is begin() and end() in this and what is L?

EDIT: Ok now I see that begin() and end() are part of the STL list template. I can't use the STL template, so I have to make my own. I have it made, but I cannot figure out how to print everything.
Last edited on
I used two sorts of traversal loops here (more for illustration than anything else). You do not have an indexing notation [i] available to you for lists, so you need some other means of traversing your list.


for ( auto L : ListOfLists )
This is a range-based for loop. It basically says "for each element - let's call it L - of ListOfLists do something". The elements of ListOfLists are of type list<string> so I could write
for ( list<string> L : ListOfLists )
instead. However, since the compiler knows perfectly well what sort of objects are held in ListOfLists you can just use auto and sit back and let the compiler automatically select the right type for you. If you want to be super-efficient you could use a reference for the element instead: for ( auto &L : ListOfLists ), and if you are supremely risk averse, then const-qualify it as for ( const auto &L : ListOfLists ). On the other hand, you are only talking about writing out one line of text here ...


for ( auto p = L.begin(); p != L.end(); p++ )
This is closer to the standard
for ( i=0; i < imax; i++)
type of loop, except that, since indexing isn't available to you, you have to use an iterator. The full form would be
for ( list<string>::iterator p = L.begin(); p != L.end(); p++ )
but auto doesn't half save some typing for the lazy now.


You do indeed have to make your own traverser of lists. There are many ways, but you will basically have to start at the HEAD node and keep following the NEXT pointer ... until it points to nullptr (or whatever you are using to signify the end).
I've no idea what your linked list looks like, but when I wrote one my traverse-and-display routine looked like
1
2
3
4
5
6
7
8
9
10
template <class T> void MyList<T>::display()
{
   Node<T> *n = head;
   while( n != nullptr )
   {
      cout << n->value << " ";
      n = n->next;
   }
   cout << endl;
}

Alternatively, you could write your own iterators to traverse the list, but that is beyond my capabilities.
Last edited on
You do indeed have to make your own traverser of lists.


My print function looks almost exactly like yours except instead of
Node<T> *n = head;
I have
node *temp = head;

If that isn't the problem then it may have something to do with my begin() and end() functions
Here they are:
1
2
3
4
5
6
7
8
9
10
11
12
13
template <class M>
M LinkedList<M>::begin() const
{
	assert(head != NULL);
	return head->data;
}

template <class M>
M LinkedList<M>::end() const
{
	assert(tail != NULL);
	return tail->data;
}


I just sort of borrowed this from a book and tried to modify it to fit my needs.
Last edited on
Your begin() and end() functions might be better named front() and back(), since they are returning data values of the first and last nodes, NOT iterators or pointers associated with them. See the list of functions for std::list at http://www.cplusplus.com/reference/list/list/.
"just sort of borrowed this from a book" is not always helpful.

If you are dead set on having a list of lists (sense my horror) then you will have to traverse an inner list within an outer list container. Something like:
1
2
3
4
5
6
7
8
9
template <class T> void MyList<T>::display()
{
   Node<T> *n = head;
   while( n != nullptr )
   {
      cout << n->value << " "; // <==== replace with something writing out the whole list n->value
      n = n->next;
   }
}

Your T would probably be string in this case, so if you hardcode that then you wouldn't need a template class.

On the indicated line, n->value will itself hold a list (that of the words on a line) so that you will need to display this inner list. You have many ways open to you, including
- writing it directly in this routine;
- writing and calling a separate routine just for a single list (appropriate to the words on a line);
- overloading the << operator for your list class.


Last edited on
If you are dead set on having a list of lists (sense my horror)


I really dislike the idea of lists of lists also, but I have to use them for this.

since they are returning data values of the first and last nodes, NOT iterators or pointers associated with them. See the list of functions for std::list at


I have been looking at that site to try and see how those functions are coded, but all it tells me is what they are used for and how to call them in the main function. I have tried to look them up to see how it is coded, but I haven't had any luck so far.

On the indicated line, n->value will itself hold a list (that of the words on a line) so that you will need to display this inner list. You have many ways open to you, including


On the first one, What would I replace that with? I'm sorry if I should know this, but I can't seem to find it in my books or online.

On the second, I'm not sure what you mean by that. You mean try to write out each list individually? Like having to hard code it where I would need to know how many there are?

On the third, I was looking this up and it sounded good, but I'm not sure how to implement it.

Thank you for replying!
Here's an outline. I have overloaded << to do the inner list.

Sorry for the omitted lines, but the whole file is long and my version is unlikely to correspond to yours.

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
template <class T> class MyList
{
private:
   // usual stuff
public:
   //
   // various member functions, including an initialiser-list constructor
   //
   template <class T> void MyList<T>::multidisplay();
   //
   template <class U> friend ostream &operator << ( ostream &strm, MyList<U> L );     // need a friend to get at private data
};


//
//
//


template <class T> void MyList<T>::multidisplay()                 // specifically set up for lists of lists
{
   int counter = 0;
   Node<T> *n = head;
   while( n != nullptr )
   {
      counter++;
      cout << "Line " << counter << endl;
      cout << n->value << "\n\n";                          // note use of overloaded  <<  if n->value is itself a list
      n = n->next;
   }
   cout << endl;
}


//
//
//


template <class T> ostream &operator << ( ostream &strm, MyList<T> L )          // overloads << for class MyList<T>
{
   Node<T> *n = L.head;
   while( n != nullptr )
   {
      strm << n->value << " ";          // currently set to output item followed by space, but tailor to whatever you want
      n = n->next;
   }
   return strm;
}


//
//
//


int main()
{
   MyList< MyList<string> > M{ { "This", "is", "the", "winter" }, { "Of", "our", "discontent" } };

   M.multidisplay();
}


1
2
3
4
5
Line 1
This is the winter 

Line 2
Of our discontent 

Last edited on
1
2
strm << n->value << " ";
      n = n->next;


For this my value is named "data" and next is "nxt", but it is saying that neither of them can be resolved.

EDIT: I removed the <T> part from Node<T> *n = L.head; and now it can detect them.
I am thinking that my node must be coded wrong...
Does this look ok?
1
2
3
4
5
6
7
8
struct node
	{
		M data;
		struct node *nxt;
		struct node *prv;
	};
	node *head;
	node *tail;

(In my code it's template<class M> instead of template<class T>)

Last edited on
For this my value is named "data" and next is "nxt", but it is saying that neither of them can be resolved.


Is your class templated?

Can you show the definition of your class. (Not the individual functions, just the prototypes.)
I need to see how you have declared the << operator as a friend within that.

Can you also show what you have turned the display() function and operator << function into for your code. It is near impossible to debug this blind.

What are the actual error messages?


EDIT. Hmm, just seen your edit. I have Node as a separate class, so separately templated; looks like yours is declared within the class (or it wouldn't know what M was). Not wrong ... just different. Just provide (below please) the requested information and tell me whether your code is working.
Last edited on
This is the class with just the prototypes:
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
template <class M>
class LinkedList
{
private:
	struct node
	{
		M data;
		struct node *nxt;
		struct node *prv;
	};
	node *head;
	node *tail;

public:
	LinkedList()
	{
		head = NULL;
		tail = NULL;
	}
	//~LinkedList();
	void Insert(M);
	void PrintR();
	void PrintF();
	M begin() const;
	M end() const;
	ostream &operator << (ostream &strm, LinkedList<M> L);
	//void destroyList();
};


Not sure what you mean by "turned the display() function and operator << function into for your code."
but here are my display functions (one for forward one for reverse):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template <class M>
void LinkedList<M>::PrintR()
{
	node *temp = head;

	while(temp != NULL)
	{
		cout << temp->data << " ";
		temp = temp->nxt;
	}
}

template <class M>
void LinkedList<M>::PrintF()
{
	node* temp = tail;

	while(temp != NULL)
	{
		cout << temp-> data << " ";
		temp = temp-> prv;
	}
	cout << endl;
}

They are kind of opposite of what they normally would be because of where I chose to insert my new nodes.

At the moment, it is saying that "node *n = L.head;" is wrong because neither node nor n were declared in this scope.
Here is what it prints out in the console, but if I'm supposed to do more than out LoL.PrintF(); at the end, then that may cause it.
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
08:20:33 **** Incremental Build of configuration Debug for project LinkedLists-Reverse2 ****
make all 
Building file: ../linkedlist-reverse.cpp
Invoking: Cross G++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"linkedlist-reverse.d" -MT"linkedlist-reverse.d" -o "linkedlist-reverse.o" "../linkedlist-reverse.cpp"
../linkedlist-reverse.cpp:32:54: error: ‘std::ostream& LinkedList<M>::operator<<(std::ostream&, LinkedList<M>)’ must take exactly one argument
  ostream &operator << (ostream &strm, LinkedList<M> L);
                                                      ^
../linkedlist-reverse.cpp: In function ‘std::ostream& operator<<(std::ostream&, LinkedList<M>)’:
../linkedlist-reverse.cpp:84:2: error: ‘node’ was not declared in this scope
  node *n = L.head;
  ^
../linkedlist-reverse.cpp:84:8: error: ‘n’ was not declared in this scope
  node *n = L.head;
        ^
../linkedlist-reverse.cpp: In instantiation of ‘std::ostream& operator<<(std::ostream&, LinkedList<M>) [with M = std::__cxx11::basic_string<char>; std::ostream = std::basic_ostream<char>]’:
../linkedlist-reverse.cpp:75:8:   required from ‘void LinkedList<M>::PrintF() [with M = LinkedList<std::__cxx11::basic_string<char> >]’
../linkedlist-reverse.cpp:146:13:   required from here
../linkedlist-reverse.cpp:17:8: error: ‘LinkedList<std::__cxx11::basic_string<char> >::node* LinkedList<std::__cxx11::basic_string<char> >::head’ is private
  node *head;
        ^
../linkedlist-reverse.cpp:84:10: error: within this context
  node *n = L.head;
          ^
make: *** [linkedlist-reverse.o] Error 1
subdir.mk:18: recipe for target 'linkedlist-reverse.o' failed

08:20:35 Build Finished (took 2s.239ms)


Thank you for your patience.
Last edited on
Pages: 12