Golang中空字段的惯用扫描方法

Golang中空字段的惯用扫描方法 是否存在一种惯用方法来扫描包含空字段的字符串?

具体来说…

这个小例子运行正常:https://play.golang.org/p/VrQtKqtorFi

但如果目标字符串包含空字段,比如这样(注意开头的字段现在是空的):

target := ";1;1551121142780;303421;26.70921316;true"

那么我会得到

panic: strconv.ParseFloat: parsing “”: invalid syntax

因为 strconv 无法解析空字符串。

我应该创建一个带有.Scan方法的自定义"Float"对象,还是有更直接的方法?

谢谢!


更多关于Golang中空字段的惯用扫描方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

谢谢,约翰。我认为这是正确的解决方案。

W

更多关于Golang中空字段的惯用扫描方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题似乎出在 Scanf 上。 这样写可以正常工作:

s, _:= strconv.ParseFloat("", 32)
fmt.Println(s)

感谢Yamil,但在你的示例中,ParseFloat实际上抛出了同样的错误。你的代码只是没有检查错误,所以返回的零值看起来是正确的:以下是错误信息

链接

package main

import (
	"fmt"
	"strconv"
)

func main() {
	s, err := strconv.ParseFloat("", 32)
	fmt.Println(s, err)

}

可以这样做:https://goplay.space/#cG7oZD_co3C 虽然需要多写一些代码,但如果你能接受默认值(0和0.0),就可以忽略strconv函数返回的错误。

package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	target := ";1;155142780;303421;26.70921316;true"

	fields := strings.Split(target, ";")

	scale, _ := strconv.ParseFloat(fields[0], 32)
	size, _ := strconv.ParseInt(fields[1], 0, 64)
	stamp, _ := strconv.ParseInt(fields[2], 0, 64)
	count, _ := strconv.ParseInt(fields[3], 0, 64)

	fmt.Printf("Scale:%f Size:%d Stamp:%d Count:%d\n", scale, size, stamp, count)

}

在Go中处理包含空字段的字符串扫描时,确实有几种惯用方法。最直接的方式是使用strings.Split()结合条件检查来处理空值,而不是依赖单一的fmt.Sscanf()

以下是处理这种情况的示例代码:

package main

import (
    "fmt"
    "strconv"
    "strings"
)

type Record struct {
    Field1 string
    Field2 int
    Field3 int64
    Field4 int
    Field5 float64
    Field6 bool
}

func parseRecord(data string) (*Record, error) {
    parts := strings.Split(data, ";")
    if len(parts) != 6 {
        return nil, fmt.Errorf("invalid number of fields: %d", len(parts))
    }
    
    record := &Record{}
    
    // Field1 (string) - 直接赋值,空字符串就是空字符串
    record.Field1 = parts[0]
    
    // Field2 (int) - 处理空字符串
    if parts[1] != "" {
        val, err := strconv.Atoi(parts[1])
        if err != nil {
            return nil, fmt.Errorf("invalid Field2: %w", err)
        }
        record.Field2 = val
    }
    
    // Field3 (int64) - 处理空字符串
    if parts[2] != "" {
        val, err := strconv.ParseInt(parts[2], 10, 64)
        if err != nil {
            return nil, fmt.Errorf("invalid Field3: %w", err)
        }
        record.Field3 = val
    }
    
    // Field4 (int) - 处理空字符串
    if parts[3] != "" {
        val, err := strconv.Atoi(parts[3])
        if err != nil {
            return nil, fmt.Errorf("invalid Field4: %w", err)
        }
        record.Field4 = val
    }
    
    // Field5 (float64) - 处理空字符串
    if parts[4] != "" {
        val, err := strconv.ParseFloat(parts[4], 64)
        if err != nil {
            return nil, fmt.Errorf("invalid Field5: %w", err)
        }
        record.Field5 = val
    }
    
    // Field6 (bool) - 处理空字符串
    if parts[5] != "" {
        val, err := strconv.ParseBool(parts[5])
        if err != nil {
            return nil, fmt.Errorf("invalid Field6: %w", err)
        }
        record.Field6 = val
    }
    
    return record, nil
}

func main() {
    target := ";1;1551121142780;303421;26.70921316;true"
    
    record, err := parseRecord(target)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    fmt.Printf("Field1: '%s'\n", record.Field1)
    fmt.Printf("Field2: %d\n", record.Field2)
    fmt.Printf("Field3: %d\n", record.Field3)
    fmt.Printf("Field4: %d\n", record.Field4)
    fmt.Printf("Field5: %f\n", record.Field5)
    fmt.Printf("Field6: %t\n", record.Field6)
}

另一种更简洁的方法是使用指针类型和辅助函数:

package main

import (
    "fmt"
    "strconv"
    "strings"
)

type Record struct {
    Field1 *string
    Field2 *int
    Field3 *int64
    Field4 *int
    Field5 *float64
    Field6 *bool
}

func parseOptionalInt(s string) (*int, error) {
    if s == "" {
        return nil, nil
    }
    val, err := strconv.Atoi(s)
    if err != nil {
        return nil, err
    }
    return &val, nil
}

func parseOptionalFloat(s string) (*float64, error) {
    if s == "" {
        return nil, nil
    }
    val, err := strconv.ParseFloat(s, 64)
    if err != nil {
        return nil, err
    }
    return &val, nil
}

func parseOptionalBool(s string) (*bool, error) {
    if s == "" {
        return nil, nil
    }
    val, err := strconv.ParseBool(s)
    if err != nil {
        return nil, err
    }
    return &val, nil
}

func parseRecord(data string) (*Record, error) {
    parts := strings.Split(data, ";")
    if len(parts) != 6 {
        return nil, fmt.Errorf("invalid number of fields: %d", len(parts))
    }
    
    record := &Record{}
    
    // Field1
    if parts[0] != "" {
        record.Field1 = &parts[0]
    }
    
    // Field2
    field2, err := parseOptionalInt(parts[1])
    if err != nil {
        return nil, fmt.Errorf("invalid Field2: %w", err)
    }
    record.Field2 = field2
    
    // Field3
    if parts[2] != "" {
        val, err := strconv.ParseInt(parts[2], 10, 64)
        if err != nil {
            return nil, fmt.Errorf("invalid Field3: %w", err)
        }
        record.Field3 = &val
    }
    
    // Field4
    field4, err := parseOptionalInt(parts[3])
    if err != nil {
        return nil, fmt.Errorf("invalid Field4: %w", err)
    }
    record.Field4 = field4
    
    // Field5
    field5, err := parseOptionalFloat(parts[4])
    if err != nil {
        return nil, fmt.Errorf("invalid Field5: %w", err)
    }
    record.Field5 = field5
    
    // Field6
    field6, err := parseOptionalBool(parts[5])
    if err != nil {
        return nil, fmt.Errorf("invalid Field6: %w", err)
    }
    record.Field6 = field6
    
    return record, nil
}

func main() {
    target := ";1;1551121142780;303421;26.70921316;true"
    
    record, err := parseRecord(target)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    fmt.Printf("Field1: %v\n", record.Field1)
    fmt.Printf("Field2: %v\n", record.Field2)
    fmt.Printf("Field3: %v\n", record.Field3)
    fmt.Printf("Field4: %v\n", record.Field4)
    fmt.Printf("Field5: %v\n", record.Field5)
    fmt.Printf("Field6: %v\n", record.Field6)
}

第一种方法使用零值表示空字段,第二种方法使用指针来明确区分空值和零值。这两种都是Go中处理这种情况的惯用方法。

回到顶部