Paul Hoang
Paul Hoang

Reputation: 1074

Python formatting of strings with line breaks

I'm trying to nicely format some text data from database to send in emails in Django. I'm encountering some problems with strings with line breaks.

In particular,my desired output is:

1)  t1                     t2
    t1_continue            t2_end
    t1_end

2)  s1                     s3
    s1_continue
    s1_end

Here the strings taken from database are "t1\nt1_continue\nt1_end", "t2\nt2_end", "s1\ns1_continue\ns1_end" and "s3".

This desired output is like what we see if we're having 3 columns in Excel.

What I want to say is that some strings contain line breaks so simple formatting such as:

print str(index) + '\t' + col1 + '\t' + col2 

won't work.

Please share with me your approach.

Many thanks.

Upvotes: 2

Views: 2153

Answers (4)

Tom
Tom

Reputation: 2016

I thought I could hack something together in a few minutes, but formatting text into columns is more difficult than I thought. However here is what I got so far, still pretty buggy...

class format_lines(object):
    """
    1)  some text goes      2nd column of text
        here. But not       its not really that
        all text goes       hard to add more text
        in this column      in a given format

    2)  another point
        for text
    """

    def __init__(self, *args, **kargs):
        self.max_char_width = 30
        self.gutter_width = 15
        self.gutter = ' '*self.gutter_width
        self.previous_line1 = None
        self.previous_line2 = None
        self.p1_index = 0
        self.p2_index = 0
        print args
        print "len args = ", len(args)
        if len(args) == 2:
            print "Starting the test"
            self.print_lines_(args[0], args[1])

    def print_lines_(self, p1, p2):
        while self.p1_index < len(p1.split()): 
            this = self.format_line_(p1, p2)
            p1=this['p1']
            p2=this['p2']
            print this['line']

    #problem with this format is it's going to messup words by
    #pushing them to the next line (this could be fixed with a -
    #or this function could be rewritten
    def format_line_(self, p1, p2):
        #must first figure out amount of words that can fit on a line
        p1_words = [""]
        p2_words = [""]
        if p1:
            p1_words = p1.split(' ')
            p1_words = p1_words[self.p1_index:]
        if p2:
            p2_words = p2.split(' ')
            p2_words = p2_words[self.p2_index:]
        #format left side's output
        loutput = p1_words[0]
        if len(loutput) < self.max_char_width:
            for i, word in enumerate(p1_words[1:]):
                if (len(loutput) + len(word)) <= self.max_char_width:
                    loutput += (' {0}'.format(word))
                    self.p1_index = i
                else:
                    break
            self.p1_index+=1 #for loop iteration starts at index 1 not 0 so
                        #a +1 is required
        elif (len(loutput) > self.max_char_width):
            long_word = []
            long_word.append(loutput[:len(loutput)/2])
            long_word.append(loutput[len(loutput)/2:])
            long_word[0]+='-'
            p1_words[0]=long_word[0]
            p1_words.insert(1, long_word[1])
            p1 = ' '.join(p1_words)
        else:
            #the left output is a single word
            #equal to the max_char_width
            pass
        #need to add 1 to the index, because at least 1 element in the list is
        #going to be printed
        self.p1_index+=1
        #format right side's output
        routput = p2_words[0]
        if len(routput) < self.max_char_width:
            for word in p2_words[1:]:
                if (len(routput) + len(word)) <= self.max_char_width:
                    routput += (' {0}'.format(word))
                else:
                    break
            self.p2_index+=1
        elif len(routput) > self.max_char_width:
            long_word = []
            long_word.append(routput[:len(routput)/2])
            long_word.append(routput[len(routput)/2:])
            long_word[0]+='-'
            p2_words[0]=long_word[0]
            p2_words.insert(1, long_word[1])
            p2 = ' '.join(p2_words)
        else:
            #the right output is a single word
            #equal to the max_char_width
            pass
        self.p2_index+=1

        #determin gutter size
        if len(loutput) < self.max_char_width:
            loutput+=(' '*(self.max_char_width-len(loutput)))
        #do not need to append spaces to the right hand size

        output = ''
        #if previous_line1 is not defined then there should be no way that previous_line2 is defined
        if not self.previous_line1:
            #must be the first line of a bullet point
            output = '1){0}{1}{2}{3}'.format(self.gutter,
                                             loutput,
                                             self.gutter,
                                             routput)
            self.previous_line1 = loutput
            self.previous_line2 = routput
        else:
            p1_width = len(self.previous_line1)
            p2_width = len(self.previous_line2)
            if loutput<p1_width:
                loutput+=(' '*(len(self.previous_line1)-p1_width))
            if routput<p2_width:
                routput+=(' '*(len(self.previous_line2)-p2_width))
            output = '  {0}{1}{2}{3}'.format(self.gutter,
                                             loutput,
                                             self.gutter,
                                             routput)
        ret_val = {'line':output, 'p1':(' '.join(p1_words)), 'p2':(' '.join(p2_words))}
        return ret_val

if __name__ == '__main__':
    t1 = "this is a very long line of text much larger than you can fit on one line"
    t2 = "this is another long line, only this line is a little different from the first line"
    test = format_lines(t2, t1)

Upvotes: 1

gonsalu
gonsalu

Reputation: 3184

You can use a <table> to display tabular data in the email body.

Before the loop that prints each table row, print the table header:

print('<table>')

For each data row, format it like so:

print('<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>'.format(index, col1, col2))   

Finally, after the loop that prints each table row, print the table footer:

print('</table>')

Upvotes: 1

larsks
larsks

Reputation: 311486

I think I understand what you're asking. You want to word-wrap text within a column, much like Excel can do if you have a block of text in a cell. I think this recipe will get you started; it appears to do pretty much what you want.

Upvotes: 0

brc
brc

Reputation: 5391

How \t is displayed will differ by OS, application, ect. For true string formatting, I would recommend checking the docs. For your example,

print('{0})  {1:<25} {2}'.format(index,col1,col2))

Will do what you want, assuming that col1 is never larger than 24 characters. If it is, you can always adjust that value to your liking.

Upvotes: 3

Related Questions