I would like to know if there is a simple means of formatting in columns the
output of data to a file in Smalltalk. ie so that you get this.... a 1 z ab 12 z abc 123 z (actually that doesnt work exactly in this post!) instead of :- a 1 z ab 12 z abc 123 z etc in C there is a setwidth() or something. Is there a strightforward way ofoding this in ST? The code I am doing it with is ad hoc and is becoming awfully complicated. |
> I would like to know if there is a simple means of formatting in columns
t> he output of data to a file in Smalltalk. ie so that you get this.... [] > in C there is a setwidth() or something. Is there a strightforward way > ofoding this in ST? The code I am doing it with is ad hoc and is > becoming awfully complicated. To some extent it depends on how you will be viewing the output file after it has been created. If you will be viewing the file using a fixed width font you can use the Dolphin version of the #sprintf function. So '%6d' sprintfWith: 123 will output 123 right justified in a field of 6 characters. %-6s' sprintfWith: 'abc' will output "abc" left justified in a 6 character field. This only works for Integer, Character and String arguments though - Floats are not supported. In a similar vein, Dolphin also support a set of #formatWith: methods. If you are viewing with a variable width font then the easiest way is just to separate the fields using tabs (which will obviously also work with fixed pitch fonts). This will align the starts of the fields and, if the viewing editor supports it, will also allow variable width tabs with centre or right justification (and even line up the decimal points) within each tab field. Ian |
Ian, Ripcat
A quick word of warning: do *NOT* use String>>sprintfWith:with: without *FIRST* fixing the buffer bug. The buffer should be allocated inside the loop (as it is in String>>sprintfWith:) ... crt := CRTLibrary default. size := self size + 128. [ buf := String new: size. n := crt _snprintf: buf count: size format: self with: arg1 with: arg2. ... Without the fix you will be at risk of destroying your image. -- chris |
In reply to this post by Ripcat UK
Ripcat UK <[hidden email]> wrote in message
news:[hidden email]... > > I would like to know if there is a simple means of formatting in columns the > output of data to a file in Smalltalk. ie so that you get this.... > > a 1 z > ab 12 z > abc 123 z (actually that doesnt work exactly in this post!) ... > in C there is a setwidth() or something. Is there a strightforward way ofoding > this in ST? The code I am doing it with is ad hoc and is becoming awfully > complicated. I don't think this is exactly what you want unless you can use RTF, and want to invest in a little framework overhead. If not of use, perhaps it will be of interest. The RTF "decoration" could be built into an RTF report stream. The generated RTF files can be opened in just about any decent document editor (even WordPad), and Ian has a great goodie for RTF printing. Unfortunately I think my code is going to be mangled a bit. ============= strm := String writeStream. "RTF Header." strm nextPutAll: '{\rtf1\ansi\deff0{\fonttbl{\f0\froman Times New;}}{\colortbl\red0\green0\blue0;}{\stylesheet{\fs20\snext0 Normal;}}\margl1440\margr720\ftnbj\ftnrestart\sectd\sbknone\endnhere\pard\sl 0\fs22'. "Set Tab Stops." strm nextPutAll: '{\tx100\tx200\tx300 '. data := #(('a' 1 'z')('ab' 12 'z')('abc' 123 'z')). data do: [:eachRow | eachRow do: [:eachColumn | strm nextPutAll: eachColumn displayString] separatedBy: [strm tab]. strm nextPutAll: '\par ']. "Close tab section and document." strm nextPutAll: '}}'. "Display the results." rte := RichTextEdit show. rte rtfText: strm contents. ============= Chris |
In reply to this post by Chris Uppal-3
Chris,
> Without the fix you will be at risk of destroying your image. Slight moment of panic there :-) On checking I see that after your earlier warning I had added the fix to my image setup script and just forgotten about it. Thanks for the heads-up though Regards Ian |
In reply to this post by Christopher J. Demers
Chris, Ripcat,
> I don't think this is exactly what you want unless you can use RTF, and want > to invest in a little framework overhead. Just curious: does this example actually work for you -- on my machine (W2K) I have to change the tabstops to something like: strm nextPutAll: '{\tx600\tx1200\tx1800 ' Or they are too small to affect the layout. Also, as I mentioned in the "reports" thread a little while ago, there's a package at: http://www.smalltalking.net/Goodies/Dolphin/ that *seems* to provide a comprehensive (I haven't used it) framework for RTF generation. May be overkill, though... -- chris |
Hi. Thanks for the replies. Perhaps I should rephrase this, as I am a beginner
at all this. What is the "simplest" way of achieving this....can you send things like strings out to a stream in columns, right justified to avoid my problem. What class would these methods be found in (I only have the dolphin free download) |
hmm, I am also not sure why sprintf with a letter, ie 'z', gives me a big long
number as a reply... |
'%6d' sprintfWith: 'tom'
gives me the following answer !! '37089900' not only that, but everything ELSE I do it with afterwards gives me the same answer too. It seemed to work for a few minutes and then that started happening. Wazzatallabowt? |
In reply to this post by Ripcat UK
Ripcat,
[I hope you don't mind me calling you by your first name ;-) ] > Hi. Thanks for the replies. Perhaps I should rephrase this, as I am a beginner Ah, right. Sorry. > at all this. What is the "simplest" way of achieving this....can you send > things like strings out to a stream in columns, right justified to avoid my > problem. Well, the *really* simple way -- that might be good enough, but often isn't -- is just to use hard tabs for the column separators in your file. Something like: (FileStream write: 'C:\temp\somethiing.txt') display: 'a'; tab; display: 12; tab; display: $z; tab; cr; ... and so on... close. Otherwise it depends heavily on whether you are a C programmer (or familiar with one of the other languages where the C "printf" format is used). If so then printf() is probably second nature to you and that is the place to start in Dolphin. More below. If not, then "printf" format is rather arcane and you'd probably be better off sticking with your ad-hoc solution, since there's not much else pre-packaged for doing formatted output. There are a few helper methods on Integer and Float classes, look in the 'printing' category, but they won't help you line up your columns. (It may help, if you haven't noticed it, to mention the #position method on OutputStream -- if you keep track of the #position at the start of every line, then you can use that and #position when you come to write each "field" to know how many spaces-worth of padding to use. Actually it'd be reasonably easy to write a subclass of OutputStream that did that kind of thing for you, but I'd better stop talking about it because my coding fingers are already starting to twitch.) Anyway, if you *are* conversant with printf() then here's an example. Say you have a list of objects which have a String #name, integer #age, and floating point #height. Say you want to write them to a file with the name right-aligned in a field of width 40, the age left-aligned in a field of width 4, and the height left-aligned in a field of width 10. Here's one way to write it: stream := FileStream write: 'C:\temp\somethiing.txt'. aList do: [:each | stream nextPutAll: ('%-40s' sprintfWith: each name); nextPutAll: ('%4d' sprintfWith: each age); nextPutAll: ('%10s' sprintfWith: each height displayString); cr]. stream close. #sprintfWith: is a method on String that assumes that the String is in C's "printf" format and contains one format specifier (the %d or %s bit) and answers a new String that is the argument formatted according to the specifier. Try a "print it" of: '%12d' sprintfWith: 400 in a workspace. As Ian has mentioned, you can't use this technique to format floating point numbers, but a workaround is to convert the float into a String (by sending it #displayString, or one of the other similar methods on Float), and then print that String with the desired padding. That's what I've done in the third #nextPutAll: of my example. I hope this hasn't just been confusing. -- chris |
In reply to this post by Ripcat UK
> Hi. Thanks for the replies. Perhaps I should rephrase this, as I am a
> beginner at all this. What is the "simplest" way of achieving this....can > you send things like strings out to a stream in columns, right justified > to avoid my problem. What class would these methods be found in > (I only have the dolphin free download) Some demos... Copy the following into a workspace and evaluate it all. data := #(('Col1' 99 'Col3') ('Col11' 100 'Col31') ('Col12' 1000 'Col32')). stream := String writeStream. "stream := FileStream write: 'filename.txt'." data do: [:each | stream tab; nextPutAll: (each at: 1); tab; print: (each at: 2); tab; nextPutAll: (each at: 3); cr]. "stream close." textP := TextPresenter show: 'Multiline text'. textP view font: (Font name: 'Arial' pointSize: 12). textP value: stream contents The first line just sets up an 3x3 array of the data that we are going to display. Line 2 sets up a stream that we will use for collecting the textual output. I've used an internal stream on a String for simplicity but you could just as easily create a FileStream and output to a file (the commented out code) Lines 4 to 12 read the data array, a line at a time, and write the each of the items to the stream. Each of the three items on the line is preceded by a tab stop to make them align on the left. Lines 14 - 16 open up a TextPresenter, set a proportional font and display the contents of the stream. The above should display in a proportional font with the left hand edges aligned The second example does the same again but this time #sprintf: is used to display each of the fields with a width of 12 characters. The idea now is to make the right hand sides align. data := #(('Col1' 99 'Col3') ('Col11' 100 'Col31') ('Col12' 1000 'Col32')). stream := String writeStream. data do: [:each | stream nextPutAll: ('%12s' sprintfWith: (each at: 1)); nextPutAll: ('%12d' sprintfWith: (each at: 2)); nextPutAll: ('%12s' sprintfWith: (each at: 3)); cr]. textP := TextPresenter show: 'Multiline text'. textP view font: (Font name: 'Arial' pointSize: 12). textP value: stream contents You will notice that this doesn't work. Because Arial is a proportional font the characters (including the spaces) all have different widths and the right hand edges are therefore out of line. You can also left justify this example my changing the format specifiers to '%-12.... It still results in a mess, just now it's a left hand justified mess. Now try replacing the lines in the above examples that set the font. Change them to textP view font: (Font name: 'Courier new' pointSize: 12). Courier is a fixed width font so when you evaluate the example code all of the characters have exactly the same width. This does not make a lot of difference to the first example but cures the jagged edge problem in the second. I hope this demonstrates why it is not only the format that you create the file with that is important but also the way you display it. > hmm, I am also not sure why sprintf with a letter, ie 'z', gives me a big > long number as a reply... You mentioned C in an earlier post so I just assumed you would be familiar with sprintf. The first String is a format specifier '%d' means the argument is a decimal integer '%s' means the argument is a string '%12s' means format the string with a field width of 12 characters and right justify it '%-12s' means the same but left justified In Dolphin you pass the arguments after the selector so '%12d' sprintfWith: 1234 would answer as String containing 1234 right justified in a field of 12 characters. You can have two arguments (bearing in mind Chris' earlier warning about a Dolphin bug) '%d to %d' sprintfWith: 12 with: 34 would answer "12 to 34" There's more to it, but that should be enough to get you going. Regards Ian |
In reply to this post by Chris Uppal-3
Chris Uppal <[hidden email]> wrote in message
news:3dddfbc4$0$116$[hidden email]... > > Just curious: does this example actually work for you -- on my machine (W2K) I > have to change the tabstops to something like: > strm nextPutAll: '{\tx600\tx1200\tx1800 ' > Or they are too small to affect the layout. Actually it seems that it only _appears_ to work for me. ;) On Windows NT with Dolphin 5 apparently my code does not actually use the tab stops I set, but just uses the defaults. My stops look good, your stops look the same. Actually you just reminded me of an RTF quirk, on NT and Win 98 the tab stop set code has to start with \pard or it has no effect, Windows 2000 is less picky. This line should make it look good on all versions of windows: strm nextPutAll: '{\pard\tx6000\tx12000\tx18000 '. > Also, as I mentioned in the "reports" thread a little while ago, there's a > package at: > > http://www.smalltalking.net/Goodies/Dolphin/ > > that *seems* to provide a comprehensive (I haven't used it) framework for RTF > generation. May be overkill, though... I looked at that, and I appreciated the link, there is some interesting stuff there. I am currently using my own RTFStream class (it is actually an enhanced version of an example from John Pletzke's book, Advanced Smalltalk). My class does what I need for now, but I did look at the RTF goody above, hopping it might be even better. It may well be better, but it is rather huge (326 classes), for now I will stick to my simpler approach until I need more out of RTF. Chris |
Chris,
> This line should make it look good on all versions of windows: > strm nextPutAll: '{\pard\tx6000\tx12000\tx18000 '. RichText is just Too Wierd (tm), changing that line to: strm nextPutAll: '{\pard\tx600\tx1200\tx1800 '. has no effect whatever on my machine. I give up. > > http://www.smalltalking.net/Goodies/Dolphin/ > [...] is rather huge (326 classes) I think you've just secured the Understatement of the Week Award. -- chris |
In reply to this post by Ripcat UK
Ripcat,
> '%6d' sprintfWith: 'tom' In case you haven't already worked it our from Ian's examples. You need to use '%s' for Strings, '%d' for Integers, and '%c' for Characters. If you use '%d' for a String then I think you'll end up printing out its address in memory. There's a fair number of other options, google for "printf format" or ask here for more info. -- chris P.S. For printf() aficionados everwhere, you may like (or be horrified by): b := (ByteArray new: 8) doubleAtOffset: 0 put: Float pi; yourself. '%12.4lf' sprintfWith: (b sdwordAtOffset: 0) with: (b sdwordAtOffset: 4). Enjoy! -- c |
In reply to this post by Chris Uppal-3
Chris,
> RichText is just Too Wierd (tm), Oooh, that's a bit unfair. I've been getting a lot of enjoyment from the RichEdit control in the last week or so (or does that just make me wierd as well!). The secret for inner happiness is to avoid mucking about with the generated text but instead to muck about with something that generates the text for you - it makes it a lot easier. > changing that line to: > > strm nextPutAll: '{\pard\tx600\tx1200\tx1800 '. > > has no effect whatever on my machine. That's not surprising as the arguments are in twips. You are setting the tab stops to 0.4, 0.8 and 1.25 inches which doesn't leave a lot of room for displaying characters :-) self align: #centre while: [ self boldWhile: [ self underline: #wave while: [ self font: #header while: [ self appendTextCrCr: 'Ian']]]] (you'll have to picture the rtf output for yourself) |
Ian,
> Oooh, that's a bit unfair. I frequently am :-) Especially where software from a certain mega-corporation (or indeed any mega-corporation) is concerned. > > strm nextPutAll: '{\pard\tx600\tx1200\tx1800 '. [...] > That's not surprising as the arguments are in twips. You are setting the > tab stops to 0.4, 0.8 and 1.25 inches What I don't understand is why multiplying all the tabstops by ten makes no perceptible difference at all. A bit of experimenting suggests that it might be doing some sort of sanity checking and ignoring tabstops that it considers unreasonable. Things have come to a pretty pass if a mere text edit control thinks it can second-guess *me*. Pah! -- chris |
In reply to this post by Chris Uppal-3
"Chris Uppal" <[hidden email]> wrote in message
news:3ddd0501$0$122$[hidden email]... > Ian, Ripcat > > A quick word of warning: do *NOT* use String>>sprintfWith:with: without *FIRST* > fixing the buffer bug. The buffer should be allocated inside the loop (as it > is in String>>sprintfWith:) > For the benefit of anyone else concerned by this bug, please find below our patch for it. It is relevant if you expect to use #sprintfWith:with: to format up strings of greater than approximately 128 characters. I should note that sprintf(), while convenient, is a fundamentally dangerous function, in that there is some risk of causing an access violation should the format string not match the actual arguments passed. The '...' variable argument mechanism of C/C++ is fundamentally non-typesafe in a statically typed language with intrinsic value types. Of course in everything-is-an-object dynamically-typed Smalltalk we could implement a typesafe version of it :-). Regards Blair ------------------------------ !String methodsFor! sprintfWith: arg1 with: arg2 "Answer a String which is a message formatted from the receiver (assumed to be a C-printf format String) with substituations from the arguments. Note: This is much faster than formatWith:with:." | written lib buf size | lib := CRTLibrary default. size := self size + 128. [buf := String new: size. written := lib _snprintf: buf count: size format: self with: arg1 with: arg2. written < 0] whileTrue: [size := size * 2]. ^buf copyFrom: 1 to: written! ! !String categoriesFor: #sprintfWith:with:!printing!public! ! |
Free forum by Nabble | Edit this page |