2014-01-30 60 views
2

有沒有辦法使用F#的sprintf浮點格式與一個十進制逗號?這將是很好,如果這工作:如何使用類似printf的函數獲得文化感知輸出?

sprintf "%,1f" 23.456 
// expected: "23,456" 

或者我可以只使用String.Format Method (IFormatProvider, String, Object())

編輯:我想有a comma not a point作爲小數點分隔符。像大多數非英語國家一樣使用它。

+0

我已經編輯了一些更加清晰和可搜索性的標題和正文。如果它錯了,隨意回滾。 – bytebuster

回答

3

看看Printf模塊的源代碼,它使用invariantCulture。我不認爲類似printf的功能是文化意識。

如果您總是需要一個逗號,您可以使用sprintfstring.Replace函數。如果您的代碼依賴於文化,則使用ToStringString.Format是最佳選擇。

+0

對不起,我誤解了這個問題。我編輯了我的答案。 – pad

+0

所以從源代碼修改我可以做一個'sprintf'函數也需要一個CultureInfo參數? – Goswin

5

這是一個相當痛苦,但是你可以寫你自己的版本的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 
+0

那麼你會推薦'String.Format'嗎? :)純粹的努力。 – Daniel