0

Let's say I have a file abc.txt. That file contains huge paragraphs of text (for example, this one: https://loremipsum.io/generator/?n=5&t=p).

I'm trying to read and print the contents of abc.txt onto the console using stringstream and rdbuf(). While text displayed on the console is perfect, I would like to pad the beginning of each line by n spaces and it's end also by the same n spaces.

For example, the line:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor

should become:

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor    

Here, I have padded the beginning and the end of the line with 4 spaces.

Note that I don't want to alter the file in any way. It itself is opened in read mode, disallowing any writes to it.

I came across a lot of answers on SO about "centering text in C++", but most of them use setw. It's impossible for me to know that each line may contain different width (number of characters in a line) and setw needs to be greater than the width of the output (line).

How can I do this?

Here's how I'm reading from the file and sending it to the console out stream now:

string file_slurper(std::ifstream& infile)
{
    stringstream ssm;
    ssm << infile.rdbuf();
    return ssm.str();
}

std::ifstream read_f(files[ch_index]);
if (read_f.is_open())
{
    cout << file_slurper(read_f);
}

Also, if it helps I am using Boost libraries is in this file, so you can suggest something from Boost as well if that's better.

gourabix
  • 99
  • 1
  • 5
  • 22

2 Answers2

1

(I assume that you mean centered in respect to the terminal)

Instead of directly printing it, get it as a string, compare it's length to the terminal width (see Get size of terminal window (rows/columns)) and print padding accordingly.

Note that you have to think about what to do when the string is larger than the terminal.

void print_centered(const string& str)
{
    int difference = terminal_width() - str.length() ;
    if(difference < 0)
    {
        // see edit note below
        cout << str.substr(0, terminal_width()) << endl;
        print_centered(str.substr(terminal_width(), str.length() - terminal_width());
    }
    else
    {
        cout << string(difference/2, ' ');
        cout << str << endl;
    }
}

(with int terminal_width() being implemented as detailed in the link above, probably in several versions if you use multiple platforms)

Edit: Created something for the difference < 0 case that prints what it can and then centers the tail.
For a terminal width of 8, the word abcdefghijklmnopqrstuvwxyz would be printed as (with _ for spaces)

abcdefgh
ijklmnop
qrstuvwx
___yz___

A more sophisticated approach might separate the new line with a hyphen, might try to separate by keeping words intact if possible, or might even be smart enough to separate within words like a person would do (and as text software usually does), although that would require some major effort (or a really good library that knows languages).

Note that the centering would be, in any case, of course cease to be if the terminal window is resized by the user after the printing.

Also, from the comments of your question, as you seem to disfavor getline for reasons of speed:
You pretty much have to read line by line if you want to do this, and as somebody already said, reading and printing are both already slow, I'd really wonder if you are able to notice any difference.
Maybe an important advice here, premature optimization is usually a mistake. Optimize for speed only when it becomes an issue.

Aziuth
  • 3,652
  • 3
  • 18
  • 36
  • As far as I have seen (which is very limited, am a n00b), even if a string is larger than the terminal width, usually the Terminal wraps around the text to the next line. So say that I implement your function but don't handle the case where ```difference < 0```. What kind of troubles, if any can I run into? – gourabix Aug 14 '19 at 10:15
  • @blizzard_braver I'm not talking about troubles, just about you deciding what happens in that case. What "centering" means for something that is bigger than the line in which it is to be centered. If you do that in something like Word for example, the line created by the wrapping would be centered. – Aziuth Aug 14 '19 at 10:42
  • @blizzard_braver Edited the code for what I'd see as a reasonable solution for `difference < 0`. – Aziuth Aug 14 '19 at 10:54
  • Thanks Aziuth! You have expanded a lot in your answer about all the potential ways this could go and I appreciate your effort in this. And yeah, I have realized that ```getline()``` is the way to go for this particular problem. – gourabix Aug 14 '19 at 11:05
  • Also, I'd note the last line of your answer. It seems like an advice that's rare yet very true these days. – gourabix Aug 14 '19 at 11:06
0

If you want to center your output, you need to add a variable number of spaces in front of each line, the number being ((width of terminal)-(length of line being printed)/2). In order to be able to tell the length of each line, you need to read your input line by line, instead of character by character. std::getline is a good function for that.

If you want to pad with n spaces in front of each line, print n spaces in front of each line. You could also print n spaces after each line, but that would probably have no effect on the visible output, except for possible ugly and unnecessary line wrap-around. At least with default settings where spaces are invisible. It is convenient to use std::getline in this case too. Alternatively, you can output character by character and watch for newline characters yourself.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243