4.2.1 加载和解析数据

4.2.1 加载和解析数据

 

    作为第一步,我们将实现一个函数 convertDataRow,它从这个 CSV 文件中取一行作为字符串,从这一行中返回两个组件到一个元组中。实现这个函数后,立刻测试它,通过给它一个示例输入 (字符串“Testing reading,1234”),应该能正确解析。在清单 4.2 中,可以这个函数的代码和测试的结果。

Listing 4.2 Parsing a row from the CSV file (F# Interactive)

 

> open System;;
> let convertDataRow(csvLine:string) =
    let cells = List.ofseq(csvLine.Split(','))
    match cells with
    | title::number::_ –>
    let parsedNumber = Int32.Parse(number)
    (title, parsedNumber)
  | _ -> failwith "Incorrect data format!"
;;
val convertDataRow : string -> string * int
 

> convertDataRow("Testing reading,1234");;
val it : string * int = ("Testing reading", 1234)

 

    启动 F# Interactive 以后,我们从 System 命名空间导入功能。我们需要打开这个命名空间,因为代码要使用 Int32.Parse 方法。这必须显式导入,反之,来自核心 F# 库的函数,比如,List.ofseq,能隐式使用。

    函数 convertDataRow 取一个字符串作为参数值,并把它拆分成使用逗号作为分隔符的值的列表,然后,用标准的 .NET 的 Split 方法执行此操作。当调用一个值的实例方法时,F# 编译器需要提前知道这个值的类型。不幸的是,在这种情况下,类型推断没有任何其他方式来推断出类型,所以,我们要使用的类型批注显式声明 csvLine 的类型是字符串。

    Split 方法使用 C# 的 params 关键字声明,取数量可变的字符作为参数值。我们指定只是一个分隔符:逗号。这个方法的结果是一个字符串数组,但我们想要使用的是列表,所以,要将结果转换为列表,使用来自 F # List 模块的 ofseq 函数。我们将在第 10 和 12 章中讨论有关数组和其他集合类型。

    一旦我们有了这个列表,就可以用 match 结构来测试,看格式是否正确。如果它包含两个或多个值,将匹配第一种情况(title::number:: _),标题赋给值 title、数值赋给 number,其余列(如果有)将被忽略。在这个分支中,我们使用 Int32.Parse 把字符串转换为整数,并返回一个元组,包含标题和这个值。第二个分支触发标准的 .NET 异常。

    如果你看这个签名,可以发现该函数取一个字符串,并返回一个元组,第一个值是字符串,第二个值是整数。这是完全符合我们的预期:标题返回作为一个字符串,第二列的数值转换为整数。在下一行演示如何在 F# Interactive 中方便地测试这个函数。示例调用的结果是一个元组,包含“Testing reading” 作为标题,"1234"作为数值。

 

在 F# 中使用 .NET 字符串

 

    在 F# 中使用 .NET 字符串,一般会使用通常的 .NET 方法。让我们看看如何在 F# 中使用它们,先选几个在 String 类中的静态方法,我们可以使用这些方法,就好像普通的 F# 函数(使用 String 类名)。这些函数的参数值必须在括号内指定,作为以逗号分隔的元组。在这个类型签名中,元组写成星号:

    ■ String.Concat (重载):接受可变数量的字符串类型或对象的参数值,并返回一个字符串,通过把所有的这些都起来:

> String.Concat("1 + 3", 3);;
val it : string = "1 + 33"

    ■ String.Join (sep:string * strs:string[]) : string:连接一个字符串数组 strs,使用指定的分隔符 sep,可以使用 [| ... |] 语法来构建数组文本:

> String.Join(", ", [| "1"; "2"; "3" |]);;
val it : string = "1, 2, 3"

    .NET 中的字符串也是对象,也有实例成员,能在 F# 中使用,用典型的点表示法。在前面的示例中,我们已经看到这个,当拆分字符串jf ,使用了 str.Split 。下面的示例假定我们有一个字符串 str 包含"Hello World!":

    ■ str.Length:属性,返回字符串的长度,在 F# 中访问属性的方式与 C# 是相同的,所以读取属性不跟大括号:

> str.Length;;
val it : int = 12

    ■ str.[index:int]:指定一个字符串的索引,写在方括号内,返回由 index 指定的位置的字符。注意,在方括号(前)的前面仍需要点,不像 C#:

> str.[str.Length - 1];;
val it : char = '!'

    我们还可以使用 FSharp.PowerPack.dll 库中的可用函数。F# 中大多数的字符串处理代码可以使用 .NET 方法来实现。

 

    在前面的清单中,我们实现了 convertDataRow 函数,取来自 CSV 文件一行的字符串,返回一个元组,包含一个标签和一个数值。下一步,我们将实现一个函数,取一个字符串列表,将每个字符串转换为元组,使用 convertDataRow。清单 4.3 显示了这个函数,然后,立即测试,解析一个示例字符串列表。

 

Listing 4.3 Parsing multiple lines from the input file (F# Interactive)

 

> let rec processLines(lines) = match lines with | [] -> [] | currentLine::remaining -> let parsedLine = convertDataRow(currentLine) let parsedRest = processLines(remaining) parsedLine :: parsedRest ;; val processLines : string list -> (string * int) list > let testData = processLines ["Test1,123"; "Test2,456"];; val testData : (string * int) list = [("Test1", 123); ("Test2", 456)]

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    这个函数在很多方面类似于我们在前一章实现的处理列表。正如你所看到的,声明这个函数用了 let rec 关键字,所以,它是递归的。它取一个字符串列表作为参数值(lines),使用模式匹配,测试列表是空的列表,还是cons cell。对于空列表,它直接返回元组的空列表。如果模式匹配执行的分支是针对 cons cell 的,它会从列表中的第一个元素的值赋给 currentLine,包含剩余元素的列表赋给 remainning。此分支的代码首先处理单行,使用清单 4.2 的 convertDataRow 函数,然后,以递归方式处理列表中的其余部分。最后,代码构造一个新的cons cell,包含:正在处理的行作为头,以递归方式处理列表中的其余部分作为尾。这意味着,对列表中的每个字符串,函数执行 convertDataRow,并把收集到的结果为新的列表。

    为更好地理解 processLines 函数做了什么,我们还可以通过 F# Interactive,查看打印出的类型签名。表明该函数取一个字符串列表(string list 类型)作为参数值,并返回包含类型为元组(strings * int)的列表。这正是完全解析一行的函数返回的类型,就好像是这个函数做了正确的事。我们通过调用示例列表作为参数值,验证这一点,可以看到,由 F# Interactive 打印出的调用的结果:它是包含两个元组的列表,元组有一个字符串和一个数字,所以,函数运行良好。

    现在,我们有了一个函数,将字符串列表转换为一种数据结构,将用在我们的图表绘制应用程序中。在我们可以实现关键的数据处理部分之前,还需要看一个简单的工具程序。

你可能感兴趣的:(字符串,C#,F#,休闲,函数编程)