Line/Text Justification using strings

Howdy, y'all, I was wondering if anyone could lend a hand with an assignment I've been working on for over a week now.

What we have to do is, using a string of less than 80 characters given to us by the user, is justify the line so it is 80 characters wide. (our sample text is some basic Lorem Ipsum stuff) Double spaces need to be added after every . , ; ! ? (punctuation marks) and, if the line is still not justified, then spaces will be added randomly after words in the string.

I'm quite lost on this project, to be honest. I'm not a very strong programmer, but I'm trying my best. What I have so far works for the punctuation marks, but I just cannot figure out the random number/random spaces part. This code also needs to be used in a later program to justify a larger string and when I've tried it, all I've gotten is a pure mess. I've been trying to fix this for far too long and my TA is of no help whatsoever, so hopefully some fresh, intelligent eyes can guide me!

Thanks in advanced!

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
  #include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>

using std::cin; using std::cout; using std::endl; 
using std::string;

const int lineWidth = 80;

int main() {

	string s1;
	cout << "Input string and hit return: " << endl;
	getline(cin, s1);

	if (s1.size() != lineWidth) {
		int pos;
		pos = s1.find('.');
		s1.insert(pos + 1, 2, ' ');

		int pos2;
		pos2 = s1.find(',');
		s1.insert(pos2 + 1, 2, ' ');

		int pos3;
		pos3 = s1.find(';');
		s1.insert(pos3 + 1, 2, ' ');

		int pos4;
		pos4 = s1.find('!');
		s1.insert(pos4 + 1, 2, ' ');

		int pos5;
		pos5 = s1.find('?');
		s1.insert(pos5 + 1, 2, ' ');
	}

	cout << "Your justified line is: " << endl; 
	cout << s1 << endl;
}
Suppose you had a string that's size 77 to start with and also had a full-stop, a comma and a semi-colon in it. Then if you ran the program from line 17 of your code without checking the resultant string size at each step you'd end up with a string that was size 83. Hence some changes in this part of your code as below ...

Then suppose you break out of these nested if loops with a string size of 79, what are you going to do? There's no point in checking for whitespace because that'd add 2 to your size and take you over 80, so you need a check here that I've commented out and you can put in whatever condition you like.

Same when you break out of the while loop at the bottom of my code (below) with a string size 79, so another commented out bit for you to fill in as you wish. Finally, you have your super-long string, size 80! The program takes a bit of time to run though, so don't panic if nothing happens for a while:

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
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include<cctype>
using namespace std;

const int lineWidth = 80;

int main()
{
    string s1;
	cout << "Input string and hit return: \n";
	getline(cin, s1);

	if (s1.size() <= lineWidth - 2)
    {
		int pos;
		pos = s1.find('.');
		s1.insert(pos + 1, 2, ' ');
		if(s1.size() <= lineWidth - 2)
        {
            int pos;
            pos = s1.find(',');
            s1.insert(pos + 1, 2, ' ');
            if(s1.size() <= lineWidth - 2)
            {
                int pos;
                pos = s1.find(';');
                s1.insert(pos + 1, 2, ' ');
                if(s1.size() <= lineWidth - 2)
                {
                    int pos;
                    pos = s1.find('!');
                    s1.insert(pos + 1, 2, ' ');
                    if(s1.size() <= lineWidth - 2)
                    {
                        int pos;
                        pos = s1.find('?');
                        s1.insert(pos + 1, 2, ' ');
                    }
                }
            }
        }
    }

	cout << "Your justified line is: \n"<<s1<<" with size: "<<s1.size()<<"\n";

	//if(s1.size == lineWidth - 1)
	//{
	    //do something to add a single whitespace to s1;
	//}

    while(s1.size() <= lineWidth - 2)
    {
        srand( (unsigned)time( NULL ) );
        int pos = rand()%(s1.size()-1) + 1;//s1.size() - 1 since last character of existing string can't be whitespace;
        if(isspace(s1.at(pos)))
        {
            s1.insert(pos + 1, 2, ' ');
        }
    }
    //if(s1.size == lineWidth - 1)
	//{
	    //do something to add a single whitespace to s1;
	//}
    cout<<"Super long string is: "<<s1<<"\nof size: "<<s1.size()<<"\n";
}

Sample Output
1
2
3
4
5
6
Input string and hit return:
jon.a,t?;n
Your justified line is:
  jon.  a,  t?  ;  n with size: 20
Super long string is:   jon.                              a,              t?                  ;      n
of size: 80


edit: this code works only if each punctuation mark is present no more than once in the string, otherwise only the first punctuation mark will be read. If there's more than one punctuation mark check some of my recent posts, I'd put out something last week I reckon about how to handle multiple delimiters through locale and facets
Last edited on
One possible technique is to:
- break your line into individual words and put them into a vector of strings (routine splitText below);

- check if each word (other than the last) ends in one of the designated punctuation marks; if so, add a space.
- add up the combined length of words and see how much less than 80 it is (obviously if it exceeds 80 - you aren't going to be able to justify it in one line);
- generate random numbers between 1 and your number of words (minus one, for the end of the line, which needs no blank) and add a blank to that word.

- write the vector of words one after another (or use stringstream to put them back in a string).

The code below will split your sentence and write out individual words (I've enclosed them in [] in the output only to check where any blanks are). Then it is over to you to test each word and add the requisite blanks.

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
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;


vector<string> splitText( string text )
{
    vector<string> words;
    string oneWord;
    
    stringstream ss( text );
    while( ss >> oneWord ) words.push_back( oneWord );
    
    return words;
}



int main()
{
    string message = "    Hello, is this a test? Try now.  ";
    
    vector<string> words = splitText( message );
    cout << "There are " << words.size() << " words, as follows:" << endl;

    for ( int i = 0; i < words.size(); i++ )
    {
        cout << "[" + words[i] + "]" << endl;
    }
}

Last edited on
OP: I was over-complicating things in my previous post, this is a much simpler program that also handles multiple instances of the same punctuation and what happens if string.size() == 79:

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
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include  <cctype>
#include <vector>
using namespace std;

const int lineWidth = 80;

int main()
{
    string s1;
	cout << "Input string and hit return: \n";
	getline(cin, s1);
	vector<int> v;

	for (unsigned int i = 0; i < s1.size(); i++)
    {
        if(s1.at(i) == '.' || s1.at(i) == ',' || s1.at(i) == ';' || s1.at(i) == '?'||s1.at(i) == '!')
        {
            v.push_back(i);
        }
    }
    for (unsigned int i = 0; i < v.size() && s1.size()<=lineWidth - 2; i++)
    {
            s1.insert(v[i] + 2*i + 1, 2, ' ');

    }

    cout << "Your justified line is: \n"<<s1<<" with size: "<<s1.size()<<"\n";

	if(s1.size() == lineWidth - 1)
	{
	    cout<<"Stopping the program at string size 79, not possible to add 2 more spaces. \n";
	    cout<<"The string is: "<<s1<<"\n";
	    exit (1);
	}

    while(s1.size() <= lineWidth - 2)
    {
        srand( (unsigned)time( NULL ) );
        int pos = rand()%(s1.size()-1) + 1;//s1.size() - 1 since last character of existing string can't be whitespace;
        if(isspace(s1.at(pos)))
        {
            s1.insert(pos + 1, 2, ' ');
        }
    }
    if(s1.size() == lineWidth - 1)
	{
	    cout<<"Stopping the program at string size 79, not possible to add 2 more spaces. \n";
	    cout<<"The string is: "<<s1<<"\n";
	}
	else
	{
	    cout<<"Super long string is: "<<s1<<"\nof size: "<<s1.size()<<"\n";
	}



}

Sample Output
1
2
3
4
5
6
Input string and hit return:
I, mu.st; s!a,y? th,a!t it's. ho;t to,d.a?y
Your justified line is:
I,   mu.  st;   s!  a,  y?   th,  a!  t it's.   ho;  t to,  d.  a?  y with size: 69
Stopping the program at string size 79, not possible to add 2 more spaces.
The string is: I,   mu.  st;   s!  a,  y?   th,      a!  t it's.     ho;  t to,      d.  a?  y 
Last edited on
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
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;


void splitText( string text, vector<string> &words )       // Splits a string into words
{
    string oneWord;
    stringstream ss( text );
    while( ss >> oneWord ) words.push_back( oneWord );
}


string combineText( vector<string> words )                 // Concatenates words to a string
{
    string text = "";
    for ( int i = 0; i < words.size(); i++ ) text = text + words[i];
    return text;
}


bool isPunctuation( string s )                             // Ends in punctuation?
{
   string punc = ",.;:!?";
   return ( punc.find( s[s.size()-1] ) != string::npos );
}


string justified( string message, int linelength )         // Justifies a supplied message
{
   vector<string> words;
   int numWords;
   int i, w;
   int tempLength = 0, remain;

   splitText( message, words );                                           // Split into words
   numWords = words.size();                                               

   for ( i = 0; i < numWords - 1; i++ )                                   // Except last word on line ...
   {
      if ( isPunctuation( words[i] ) ) words[i] = words[i] + " ";         // Add blank for any punctuation
      words[i] = words[i] + " ";                                          // Add normal blank after a word
   }

   for ( i = 0; i < words.size(); i++ ) tempLength += words[i].size();    // Find current length and number of extra blanks needed
   remain = linelength - tempLength;

   srand( time(NULL) );
   if ( remain >= 0 )                                                     // Add blanks to remain random words
      for ( i = 1; i <= remain; i++ )
      {
         w = rand() % ( numWords - 1 );
         words[w] = words[w] + " ";
      }
   else
      cout << "Too many characters in the string" << endl;

   return combineText( words );                                           // Recombine
}


int main()
{
    string message = "We hold these truths to be self-evident: that all men are created equal, ";
    cout << "Original:  " + message                  << endl;
    cout << "Justified: " + justified( message, 80 ) << endl;
}
Last edited on
Topic archived. No new replies allowed.