I recently got bit by becoming complacent writing things like
printf "\n%f\n" 3.2
instead of
printf "%s%f%s" Environment.NewLine 3.2 Environment.NewLine
My question is: is there any way to write the safe second version as nicely as the first (i.e. a special character in the format string which inserts Environment.Newline so that an argument for each newline instance in the format string isn't required)?
How about using kprintf for a double pass, replacing \n with NewLine:
let nprintf fmt = Printf.kprintf (fun s -> s.Replace("\n", Environment.NewLine) |> printf "%s") fmt
Then in nprintf "\n%f\n" 3.2 all \n get replaced by NewLine.
There's not an escape sequence, but you could shorten it:
[<AutoOpen>]
module StringFormatting =
let nl = System.Environment.NewLine
//Usage
printfn "%s%f%s" nl 3.2 nl
Here is the list of character escapes on MSDN.
As an aside, I wonder what this would do:
printfn #"
%f
" 3.2
Related
I would like to solve the following task with parsec, although splitOn "\n\n" is probably the simpler answer.
I have an inputstring like
testInput = unlines ["ab", "cd", "", "e"] -- "ab\ncd\n\ne"
The parser shall stop when encountering an empty line.
I tried this
import Text.ParserCombinators.Parsec
inputFileP :: GenParser Char st String
inputFileP = many (lower <|> delimP)
delimP :: GenParser Char st Char
delimP = do
x <- char '\n'
notFollowedBy (char '\n')
return x
This fails with unexpected '\n'.
Why?
I was under the impression that many x parses x until it fails and then stops.
I was under the impression that many x parses x until it fails and then stops.
This is only the case if x fails without consuming any input. If x fails after consuming input, the whole parse will fail unless there's a try somewhere (this isn't just specific to many: x <|> y would also fail in that case even if y would succeed). In your case delimP fails on the notFollowedBy (char '\n') after already consuming the first \n, so the whole parse fails.
To change this behaviour, you need to explicitly enable backtracking using try like this:
delimP = try $ do
x <- char '\n'
notFollowedBy (char '\n')
return x
Alternatively, you can make it so that delimP fails without consuming any input (and thus no need for try) by making it look ahead by two characters before matching the \n:
delimP = do
notFollowedBy (string "\n\n")
char '\n'
The best way to parse any character except few, is to use noneOf combinator,
Unfortunately it doesn't work if I combine it in the following way:
Combine.parse (Combine.parens <| Combine.many <| Combine.Char.noneOf ['"', '\\']) "()"
Err ((),{ data = "()", input = "", position = 2 },["expected \")\""])
: Result.Result
(Combine.ParseErr ()) (Combine.ParseOk () (List Char))
Your use of noneOf results in that parser consuming all characters including the closing parenthesis. Since the inner portion consumes the closing paren, the Combine.parens parser will not see the closing paren. You need to cause the many <| noneOf ... parser to halt on a closing parenthesis.
Consider adding the closing parenthesis to the list of characters in noneOf:
Combine.parse (Combine.parens <| Combine.many <| Combine.Char.noneOf ['"', '\\', ')']) "()"
Wildcard patterns are file system standards that match any characters to a ( ? ) and any sequence characters to an ( * ).
I am trying to use the erlang re:replace/3 function to replace:
a) * into .*
b) ? into .
c) . into \.
d) if a wildcard pattern does not start in a wildcard, then add a ^ (start-match in regex) to the end of the pattern
e) if a wildcard pattern does not end in a wildcard, then add a $ (end-match in regex) to the end of the pattern
Somehow I am unable to get the re:replace to achieve this.
Examples:
trying to replace based on item a) above
re:replace("something*.log","\*","\.\*").
exception error: bad argument
If you are confident in the completeness of your spec, you can write the conversion directly (I guess there is no performance problem because regular expression are generally short list)
-module(rep).
-compile([export_all]).
replace(L) when is_list(L) -> lists:reverse(replace(L,wildcard(hd(L)))).
% take care of the first character
replace(L,W={true,_}) -> replace(L,W,[]);
replace(L,W={false,_}) -> replace(L,W,[$^]).
% take care of the last character
replace([_],{true,R},Res) -> R ++ Res;
replace([_],{false,R},Res) -> [$$|R] ++ Res;
% middle characters
replace([_|Q],{_,R},Res) -> replace(Q,wildcard(hd(Q)),R++Res).
wildcard($*) -> {true,[$*,$.]};
wildcard($?) -> {true,[$.]};
wildcard($.) -> {true,[$.,$\\]};
wildcard(C) -> {false,[C]}.
with your example:
11> rep:replace("something*.log").
"^something.*\\.log$"
Note that the \\ is one single character as you can verify with:
12> length(rep:replace("something*.log")).
18
In your re:replace call:
re:replace("something*.log","\*","\.\*").
the backslashes don't actually end up in the strings, since they just escape the following characters. Some backslash escapes have special meanings, such as "\n" meaning a newline, but the ones that don't only let the character through unchanged:
4> "\*".
"*"
So you need a double backslash for the backslash to actually be part of the string:
5> re:replace("something*.log","\\*","\.\*").
[<<"something">>,<<".*">>|<<".log">>]
Note that the backslashes in "\.\*" are not needed.
The return value above is an iolist, which is usually perfectly useful (in particular if you want to write the result to a file or a socket), but sometimes you may want a plain string at additional memory and CPU cost. You can pass a fourth argument to re:replace:
7> re:replace("something*.log","\\*","\.\*", [{return, list}]).
"something.*.log"
I would like to parse string literals using FParsec. By "string literals" I mean something between opening and closing quote (in my case -- single quote):
'Please, switch off your mobile phone'
What I am currently doing is the following:
let string = between (pstring "'") (pstring "'") (manySatisfy isLetter)
But this stops after the first letter consumed. Is there any way to make it greedy?
It's already greedy; manySatisfy isLetter parses a sequence of letters from the input stream.
The problem is that the parser fails with , or spaces since they are not letters. It could be fixed by using:
manyChars (noneOf "'")
or more explicitly using:
manySatisfy ((<>) '\'')
instead.
This is not working...
I get error FS0001: The type 'string' is not compatible with the type 'seq'
for the last line. Why?
let rec Parse (charlist) =
match charlist with
| head :: tail -> printf "%s " head
Parse tail
| [] -> None
Parse (Seq.toList "this is a sentence.") |> ignore
The problem is that printf "%s " head means that head must be a string, but you actually want it to be a char, so you'll see that Parse has inferred type string list -> 'a option. Therefore, F# expects Seq.toList to be applied to a string seq, not a string.
The simple fix is to change the line doing the printing to printf "%c " head.