有沒有辦法使用F#的sprintf
浮點格式與一個十進制逗號?這將是很好,如果這工作:如何使用類似printf的函數獲得文化感知輸出?
sprintf "%,1f" 23.456
// expected: "23,456"
或者我可以只使用String.Format Method (IFormatProvider, String, Object())?
編輯:我想有a comma not a point作爲小數點分隔符。像大多數非英語國家一樣使用它。
有沒有辦法使用F#的sprintf
浮點格式與一個十進制逗號?這將是很好,如果這工作:如何使用類似printf的函數獲得文化感知輸出?
sprintf "%,1f" 23.456
// expected: "23,456"
或者我可以只使用String.Format Method (IFormatProvider, String, Object())?
編輯:我想有a comma not a point作爲小數點分隔符。像大多數非英語國家一樣使用它。
看看Printf
模塊的源代碼,它使用invariantCulture
。我不認爲類似printf的功能是文化意識。
如果您總是需要一個逗號,您可以使用sprintf
和string.Replace
函數。如果您的代碼依賴於文化,則使用ToString
或String.Format
是最佳選擇。
這是一個相當痛苦,但是你可以寫你自己的版本的sprintf
但這正是你想要什麼:
open System
open System.Text.RegularExpressions
open System.Linq.Expressions
let printfRegex = Regex(@"^(?<text>[^%]*)((?<placeholder>%(%|((0|-|\+|)?([0-9]+)?(\.[0-9]+)?b|c|s|d|i|u|x|X|o|e|E|f|F|g|G|M|O|A|\+A|a|t)))(?<text>[^%]*))*$", RegexOptions.ExplicitCapture ||| RegexOptions.Compiled)
type PrintfExpr =
| K of Expression
| F of ParameterExpression * Expression
let sprintf' (c:System.Globalization.CultureInfo) (f:Printf.StringFormat<'a>) : 'a =
//'a has form 't1 -> 't2 -> ... -> string
let cultureExpr = Expression.Constant(c) :> Expression
let m = printfRegex.Match(f.Value)
let prefix = m.Groups.["text"].Captures.[0].Value
let inputTypes =
let rec loop t =
if Reflection.FSharpType.IsFunction t then
let dom, rng = Reflection.FSharpType.GetFunctionElements t
dom :: loop rng
else
if t <> typeof<string> then
failwithf "Unexpected return type: %A" t
[]
ref(loop typeof<'a>)
let pop() =
let (t::ts) = !inputTypes
inputTypes := ts
t
let exprs =
K(Expression.Constant(prefix)) ::
[for i in 0 .. m.Groups.["placeholder"].Captures.Count - 1 do
let ph = m.Groups.["placeholder"].Captures.[i].Value
let text = m.Groups.["text"].Captures.[i+1].Value
// TODO: handle flags, width, precision, other placeholder types, etc.
if ph = "%%" then yield K(Expression.Constant("%" + text))
else
match ph with
| "%f" ->
let t = pop()
if t <> typeof<float> && t <> typeof<float32> then
failwithf "Unexpected type for %%f placeholder: %A" t
let e = Expression.Variable t
yield F(e, Expression.Call(e, t.GetMethod("ToString", [| typeof<System.Globalization.CultureInfo> |]), [cultureExpr]))
| "%s" ->
let t = pop()
if t <> typeof<string> then
failwithf "Unexpected type for %%s placeholder: %A" t
let e = Expression.Variable t
yield F(e, e)
| _ ->
failwithf "unhandled placeholder: %s" ph
yield K (Expression.Constant text)]
let innerExpr =
Expression.Call(typeof<string>.GetMethod("Concat", [|typeof<string[]>|]), Expression.NewArrayInit(typeof<string>, exprs |> Seq.map (fun (K e | F(_,e)) -> e)))
:> Expression
let funcConvert =
typeof<FuncConvert>.GetMethods()
|> Seq.find (fun mi -> mi.Name = "ToFSharpFunc" && mi.GetParameters().[0].ParameterType.GetGenericTypeDefinition() = typedefof<Converter<_,_>>)
let body =
List.foldBack (fun pe (e:Expression) ->
match pe with
| K _ -> e
| F(p,_) ->
let m = funcConvert.MakeGenericMethod(p.Type, e.Type)
Expression.Call(m, Expression.Lambda(m.GetParameters().[0].ParameterType, e, p))
:> Expression) exprs innerExpr
Expression.Lambda(body, [||]).Compile().DynamicInvoke() :?> 'a
sprintf' (Globalization.CultureInfo.GetCultureInfo "fr-FR") "%s %f > %f" "It worked!" 1.5f -12.3
那麼你會推薦'String.Format'嗎? :)純粹的努力。 – Daniel
我已經編輯了一些更加清晰和可搜索性的標題和正文。如果它錯了,隨意回滾。 – bytebuster