[C++] Inconsistent Output

Pages: 12
yes sorry I meant const int *const a; but i guess it should be initialized while declaring.

Thanks, now i understand.
Last edited on
New code. What do you think?

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
string multiLineString(string line, int lineLen, int linePadding, string endLine = ""){
    bool firstRun = true;
    int pos = 0;
    string multiLine = "";
    unsigned int beg = 0, end = lineLen;

    // Loop through string while we're not at the end
    while(end < line.length()){
        // If we're not on the first run / line, then we need to add a line break and spaces
        if(!firstRun){
            multiLine += "\n";
            multiLine.append(linePadding, ' ');
        }

        // Find the last occurrence of a space in up to "end" characters
        pos = line.find_last_of(" ", end);

        // Check to see if something was found
        /* Because find_last_of searches from the beginning, it might find an older space
        ** that doesn't apply to the next segment. If this is the case, it will be less
        ** than the starting position of the next segment.
        */
        if(pos < beg){
            // If nothing was found, then place lineLen characters
            // NOTE: Second attribute how many characters to read
            multiLine += line.substr(beg, lineLen);
            beg = end;
            end += lineLen;
        }
        else{
            // If something was found, then place pos - beg characters
            // NOTE: Second attribute how many characters to read
            multiLine += line.substr(beg, pos - beg);

            // Update beg and end
            // Beg = pos + 1 to skip space
            beg = pos + 1;
            end = beg + lineLen;
        }
        if(firstRun){
            // Add endLine to end
            multiLine += endLine;
            firstRun = false;
        }
    }

    // Add the final line
    multiLine += "\n";
    multiLine.append(linePadding, ' ');
    multiLine += line.substr(beg); // beg to the end of string`

    return multiLine;
}
Hi Meesa,

Just wondering, did you try any of my code - or an easily adapted version of it?

Also, am a bit confused as to the exact format of your input / output. Could you show some actual examples of input along with the expected output, and confirm whether the input needs to be modified or not? Is the position of the last space guaranteed to be less than the Field Width? Will there be spaces before position 47?

I still have the impression you are writing too much code for what you need to do, although it is good that you are using the STL functions now. I can see that you a building a new string, which is fine, but I have a strong impression that there is no need to do that, just cout substr's

Have cherry picked these bits out of previous posts:

Other Output A string
of text. <- this was to be right aligned but couldn't format it like that here

Is this printing with a Field Width, & right aligning the second line? Could use the last example.

With my last example, it uses the setfill function to pad the output, I specified a space char to do it with. The default for this is to right align the output, but you can change by using std::left or std::right.

1
2
3
/* Other code*/
<< "  " << multiLineString(books[i].bookTitle, 55, 21) << endl
<< "  " << books[i].bookTitle << endl;


How do you know what the padding is in advance?

It occurred to me that even though this accepts a char*, it's for output.


So, can you could use the last example with a const string?

Another approach when using const strings, is to use find & substr to build a new string, but it sounds as though this might not be necessary.

Well, basically I need to read the first 50 characters, unless the spaces are at positions 47 and 53, then I'd need to just read the first 47.


Could you adapt my second example to work with this, using the find algorithm, and printing substrings? Or, if there is no need to modify the string, just cout the the substrings? You could do this with any of the first 3 examples.

.... as on my TODO list is to add an option so it will break it at words instead of characters. ....


This is the second example again.

Another idea, is to find out where the spaces are using the second example, but push_back the found positions into a local std::vector. Make use of these values to decide what to do next. Use the values as arguments for substr's

I hope you don't mind all these suggestions, I am just trying to strive for an elegant solution for you.
Last edited on
No, I appreciate the feedback. I'm building a new string, because I want the code to be reusable. And this is being placed inside of a cout statement.

As for an example:
1
2
3
4
5
cout << showpoint << setprecision(2) << fixed << left
     << " "  << setw(3)          << books[i].getQty()
     << "  " << setw(10)         << books[i].getDate()
     << "  " << setw(ISBN_WIDTH) << books[i].getISBN()
     << "  " << multiLineString(books[i].getTitle(), 45, 33) << endl;


As you can see, I'm passing a value that can't just be modified. That needs to stay the same.

And the output of that (It's in a loop)


Qty  Date Added  ISBN           Title
----------------------------------------------------------
10   2013-06-15  1-56563-807-7  The Essential Arthur W. Pink Collection
30   2013-05-26  0-13-277289-2  Starting Out with C++: From Control
                                Structures through Objects: Brief Edition,
                                7th Edition
25   2010-06-21  1-5581-9876-8  KJV Bible
20   2004-11-07  0-310-93204-1  KJV Bible


The part that allows something more to be added would be if I need to have an output like


30   2013-05-26  0-13-277289-2  Starting Out with C++: From Control          $ 150.99
                                Structures through Objects: Brief Edition,
                                7th Edition
Last edited on
OK, I will have a go at writing some more code, am going to use the approach of storing the positions of the spaces in a vector, like I mentioned in my last post.

Might be a little while, have a couple other things to do first.
I guess what would the point be? I mean, this is my definition of the function

1
2
3
4
5
6
7
8
/********************************************************
** multiLineString function definition                  *
** Take a string, a line length, and amount of padding  *
** and break up the string of text into multiple lines. *
** This allows printing "A String of Text" as:          *
** Other Data A String                                  *
**            of Text                                   *
********************************************************/


Now, yes, I could make the function a void function, and output within. The expense is two lines, which would be the return statement, and the blank line above it. The rest would just be converted to cout statements.

To me, the function is very simple. Using pseudo numbers...

LOOP WHILE we're not at the end
-Take 50 characters and find the last space in those 50.
-Note where the last space was, and then take the next 50 characters from that point

Add final line

There's a couple if statements in there, for rare cases, such as if I only want 10 characters at a time, and I have an 11 character word. And then I have to make sure I'm not added line breaks unless I have more than one line.

To me it seems you're making things more complicated by trying to make them simple. But maybe I'm wrong.
Last edited on
Oh well, It is up to you what you want to do.

The find_last_of seems to be better than storing positions of spaces in a vector.

At least your code is much better now because you are using the STL well, than what you had to start with, so that is a big positive.

Enjoy the rest of your weekend :+)

Well, I'm open to ideas. I just can't help but wonder if by trying to make things simple, you're actually going to make them complicated.
You were right, my idea of using the vector of delimiter positions was over complicating the situation.

Ok, here is some code, which uses the same approach as you did - I should have said that you did that quite well :+)

My code doesn't change the string or create a new one, and I tried to use good variable names. It is a little more concise, the bit that does the work is 10LOC. So up to you which you prefer.

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

int main() {
   const std::string BookTitle = "Starting Out with C++: From Control Structures through Objects: Brief Edition, 7th Edition";
   const std::size_t FieldWidth = 45; // Print multiline string this wide
   const std::string Delim = " ";     // The delimiting chars
   
   std::size_t LastDelimPos = 0; // The position of the last delim char of this line
   std::size_t StartPos =0; // The start position of this line
   std::size_t EndLinePos =0; // A possible position of the end of this line   
   std::size_t StrLen = BookTitle.length();

   for(;;) { // continuous loop saves an End variable
      EndLinePos = StartPos + FieldWidth;
      if(EndLinePos > StrLen ) { // We are on the last line
         std::cout << BookTitle.substr( StartPos,(StrLen-StartPos) ) << "\n";
         break; // exit the continuous loop
      }
      else {
      LastDelimPos = BookTitle.find_last_of( Delim,EndLinePos );
      std::cout << BookTitle.substr( StartPos,(LastDelimPos-StartPos) ) << "\n";
      StartPos = ++LastDelimPos; // So we don't print a space at the start of the next line
      }
   }
   return 0;
}


I also had an idea about how to print out all of the other data as well. When there is a multi-line, create a small array of structs which represent each field. One item in the array is 1 line to be printed. Populate this from your existing array of Books, and have the multi-line function return a vector of strings - just push_back the substr instead cout. Use this info to fill in the rest of the array as required. Then it should be easy to print the array with the appropriate field widths & justification. Delete the array so you don't duplicate your entire data. Create a new one for the next record that contains a multi-line.

I am thinking this will be easier than complex logic having to calculate padding to print 1 line at a time, especially if there multiple fields that have multi-lines. Up to you whether you think that is a good idea or not.

As I said earlier, you are using the STL now, so you should be off and racing ahead. Now it's the methodology that has potential to be tricky in the future. Often people find that there is nearly always someone out there with a better idea, but that is really about how much exposure one has.

The struct idea is interesting, but if I were to do that, then I'd be facing the need to rewrite the function for any future use, based on a new structure.

Your code is simple, but it lacks several core features that mine needed.

Line padding so the multi-line can be put in the middle. of a cout statement.
If your line length was 20, and you had a word such as hippopotomonstrosesquippedaliophobia, yours would fail. Mine would still break it up.
Yours can't add additional data to the end. (Although the code below is needed for it to work properly)

Here's what I have. For now, I'll stick with it, because it works. Someday though, I may be kicking myself for not using suggestions. Although, you will notice I made my variable names more clear.

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
string multiLineString(string line, int lineLen, int linePadding, string endLine = ""){
    bool firstRun = true;
    int lastPosFound = 0;
    string multiLine = "";
    unsigned int begPos = 0, endPos = lineLen;

    // Loop through string while we're not at the endPos + the length of the line
    while(endPos < line.length() + lineLen){
        // If we're not on the first run / line, then we need to add a line break and spaces
        if(!firstRun){
            multiLine += "\n";
            multiLine.append(linePadding, ' ');
        }

        // Find the last occurrence of a space in up to "endPos" characters
        lastPosFound = line.find_last_of(" ", endPos);

        // Check to see if something was found
        /* Because find_last_of searches from the beginning, it might find an older space
        ** that doesn't apply to the next segment. If this is the case, it will be less
        ** than the starting position of the next segment.
        */
        if(lastPosFound < begPos || endPos > line.length()){
            // If nothing was found, then place lineLen characters
            // NOTE: Second attribute how many characters to read
            multiLine += line.substr(begPos, lineLen);
            begPos = endPos;
            endPos += lineLen;
        }
        else{
            // If something was found, then place lastPosFound - begPos characters
            // NOTE: Second attribute how many characters to read
            multiLine += line.substr(begPos, lastPosFound - begPos);

            // Update begPos and endPos
            // BegPos = ++lastPosFound to skip space
            begPos = ++lastPosFound;
            endPos = begPos + lineLen;
        }
        if(firstRun){
            // Add spaces to fill lineLen; Spaces need to fill however long the line is - how long the first line is.
            multiLine.append(lineLen - multiLine.length(), ' ');
            // Add endLine to endPos
            multiLine += endLine;
            firstRun = false;
        }
    }

    return multiLine;
}
Ok, looks as though you have it all sorted, I won't bug you with any more ideas, because I haven't got any !

Although, with this :

The struct idea is interesting, but if I were to do that, then I'd be facing the need to rewrite the function for any future use, based on a new structure.


If the format of the output changes, one has to modify code somewhere, the idea is to make this as easy as possible. My proposed version of the multiLineString function doesn't need to change, it stores sub-strings in a vector. If you had several fields with potential for multi-lines in them, you can overload this function, which allows customisation of individual fields. The definition of the struct only needs to change if you reorder the way fields are printed, or change field widths say, but this could be helped by having variables that store these values. The code that would most likely change is the code that populates the table, but this could be made quite transparent as well.

In my view, structuring the code this way would make it relatively easy to cope with future changes. Up to a point, this concept is worth keeping in mind.

Any way that is enough from me, I am not well - need to go to the pharmacy & get something with PE in it, then contemplate a little hibernation!!!
Someday I'll probably revisit this thread for your ideas. Thank you very much for your help. Your username is well suited.
Cheers!!
Topic archived. No new replies allowed.
Pages: 12