37

In unit test I would like to hard code a block of lines as a string.

In C# I would do

var sb = new StringBuilder();
sb.AppendLine("myline1");
sb.AppendLine("myline2");
sb.AppendLine("myline3");

Since I converted to F# I tried to minimize the usage of .Net method by using bprintf instead, but somehow there is no bprintfn support which seems strange to me.

It is tedious to add \r\n at the end of each line manually.

Or is there any better way than StringBuilder?

Pac0
  • 21,465
  • 8
  • 65
  • 74
colinfang
  • 20,909
  • 19
  • 90
  • 173

7 Answers7

107

Little known feature: you can indeed indent string content - by ending each line with a backslash. Leading spaces on the following line are stripped:

let poem = "The lesser world was daubed\n\
            By a colorist of modest skill\n\
            A master limned you in the finest inks\n\
            And with a fresh-cut quill.\n"

You will still need to include \n or \n\r at line ends though (as done in the example above), if you want these embedded in your final string.

Edit to answer @MiloDCs question:

To use with sprintf:

let buildPoem character =
    sprintf "The lesser world was daubed\n\
             By a colorist of modest skill\n\
             A master limned %s in the finest inks\n\
             And with a fresh-cut quill.\n" character

buildPoem "you"            
buildPoem "her"
buildPoem "him"
Kit
  • 2,089
  • 1
  • 11
  • 23
  • Good tip @Kit--that's a new one to me too. – Onorio Catenacci Jan 31 '13 at 18:50
  • 3
    I don't like this approach because I guess it wouldn't use the platform-agnostic `Environment.NewLine` – knocte Apr 17 '14 at 13:17
  • How to do this with sprintf? – MiloDC Jul 16 '20 at 06:26
  • @MiloDC Edited to answer your question. ;-) – Kit Jul 17 '20 at 08:19
  • With sprintf, I get an error when using formatters. `sprintf "foo %s bar,\n\ ` then on the other line: `foobar %s\n." "a" "b"` (with indentation) is getting me `expecting a string -> a -> b but given a string -> string`. EDIT : wait, it compiles and work fine! The error is only in Visual Studio editor! Looks like a VS bug. – Pac0 Nov 05 '20 at 09:51
56

If you are under F# 3.0, triple-quoted strings may be the answer:

let x = """
myline1
myline2
myline3"""   
Be Brave Be Like Ukraine
  • 7,596
  • 3
  • 42
  • 66
  • caveat with that approach is that you must "clip" the string lines at the beginning of line, otherwise the indentation spaces will be taken into the string. It looks a bit ugly when you have indented code, – Pac0 Nov 05 '20 at 10:38
15

I'm surprised nobody has mentioned this:

[ "My first line"
  "second line"
  "another line" ]
|> String.concat "\n"
J D
  • 48,105
  • 13
  • 171
  • 274
  • it is basically the same as @jellyfish 's method, but looks extremely useful under such formatting. – colinfang Jan 31 '13 at 20:45
  • this is IMO how triple quotes should have worked. If you want to write pretty code with triple quotes as it is now it just screws up the indentation. – Michelrandahl Aug 05 '16 at 16:39
10

You can create directly multi-line string literals in F#:

let multiLineStr = 
  "myline1
myline2
myline3"

and C#:

var multiLineStr =
  @"myline1
myline2
myline3";
MiMo
  • 11,793
  • 1
  • 33
  • 48
8

I think there is no problem with using StringBuilder in F# as you did.

There is a function fprintfn in Printf module, so you can use it with a StringWriter object:

let sw = new StringWriter()
fprintfn sw "myline1"
fprintfn sw "myline2"
fprintfn sw "myline3"
sw.ToString()

I like fprintf and fprintfn since they are flexible. You can write to console output by supplying stdout instead.

pad
  • 41,040
  • 7
  • 92
  • 166
2

You could define your own bprintfn function:

let bprintfn bldr fmt =
  (bldr, fmt) ||> Printf.kbprintf bldr.AppendLine

Or, to define a multi-line literal you could use triple-quotes as bytebuster suggested or a "verbatim literal," which begins with @ (see Strings on MSDN).

Daniel
  • 47,404
  • 11
  • 101
  • 179
0

I'm out of touch with F#, but you might be able to do adapt my normal approach:

['line1', 'line2', 'line3'].join('\n'); //javascript

StringUtils.join(Arrays.asList("line1", "line2", "line3"), "\n")); // java, using Apache Commons Lang
craigsnyders
  • 107
  • 1
  • 1
  • 8