F# 如何使用类似printf的函数获得区域性感知输出?

F# 如何使用类似printf的函数获得区域性感知输出?,f#,printf,F#,Printf,有没有一种方法可以使用F#的sprintffloat格式和十进制逗号?如果这能奏效,那就太好了: sprintf "%,1f" 23.456 // expected: "23,456" 还是我只能用 编辑:我想要一个小数点分隔符。像大多数非英语国家一样使用它。看看Printf模块的源代码,它使用。我不认为printf类函数具有文化意识 如果您总是需要逗号,可以使用sprintf和string.Replace函数。如果您的代码依赖于区域性,那么最好使用ToString或String.Format

有没有一种方法可以使用F#的
sprintf
float格式和十进制逗号?如果这能奏效,那就太好了:

sprintf "%,1f" 23.456
// expected: "23,456"
还是我只能用


编辑:我想要一个小数点分隔符。像大多数非英语国家一样使用它。

看看
Printf
模块的源代码,它使用。我不认为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
开放系统
打开System.Text.RegularExpressions
开放系统.Linq.Expressions
(0-0-9[0-9]+)(([0-0-9[0-0-9 10 10-9 10 10 10 10-9 10 10 10 10-9)10 10 10 10-9)10,([0-0-9 10-9 10 10-9+)(([0-0-9 10-9 10-9 10 10 10-9+)、b(0-0-0-9 10-9 10 10 10-9 9)10 10 10 10 10 10 10 10-10 10 10 10 10 10-10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10++++++++10)以及以及b(b(0-0-10-10-10-10-10-10-10-10-10-10-10-10-10-10-10-10-9)以及以及(0-10-10-10-10-10-10-10-9)10 10-9++10 10 10 10 10 10 10 10-9++10 10 10 10 10 10 tCapture | | | RegexOptions.Compiled)
类型PrintfExpr=
|表达方式
|参数表达式*表达式的F
让我们使用sprintf'(c:System.Globalization.CultureInfo)(f:Printf.StringFormat't2->…->字符串
让cultureExpr=Expression.Constant(c):>Expression
设m=printfRegex.Match(f.Value)
let prefix=m.Groups.[“text”]。捕获[0]。值
让输入类型=
让rec循环t=
如果Reflection.FSharpType.ist函数
让dom,rng=Reflection.FSharpType.GetFunctionElements t
dom::loop rng
其他的
如果不是,那么
failwithf“意外返回类型:%A”t
[]

裁判(loop typeofSorry,我误解了这个问题。我编辑了我的答案。因此,从源代码修改后,我可以生成一个
sprintf
函数,该函数还包含一个CultureInfo参数?我编辑了标题和正文,以提高清晰度和可搜索性。如果它有错误,请随意回滚。因此,您是否建议使用
String.Format?:)+1表示纯粹的努力。