Skip to content

集合操作

本页面描述了CPE库中用于处理CPE集合的功能,包括集合创建、操作、过滤和分析。

CPE集合

CPESet

CPE集合的主要结构体。

go
type CPESet struct {
    items map[string]*CPE // CPE项目映射
    mutex sync.RWMutex    // 读写锁
}

NewCPESet

创建新的空CPE集合。

go
func NewCPESet() *CPESet

NewCPESetFromSlice

从CPE切片创建集合。

go
func NewCPESetFromSlice(cpes []*CPE) *CPESet

NewCPESetFromStrings

从CPE字符串列表创建集合。

go
func NewCPESetFromStrings(cpeStrings []string) *CPESet

示例:

go
// 创建空集合
set1 := cpe.NewCPESet()

// 从切片创建
cpes := []*cpe.CPE{cpe1, cpe2, cpe3}
set2 := cpe.NewCPESetFromSlice(cpes)

// 从字符串创建
cpeStrings := []string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
}
set3 := cpe.NewCPESetFromStrings(cpeStrings)

fmt.Printf("集合大小: %d, %d, %d\n", set1.Size(), set2.Size(), set3.Size())

基本操作

Add

向集合添加CPE。

go
func (s *CPESet) Add(cpe *CPE) bool

Remove

从集合移除CPE。

go
func (s *CPESet) Remove(cpe *CPE) bool

Contains

检查集合是否包含CPE。

go
func (s *CPESet) Contains(cpe *CPE) bool

Size

获取集合大小。

go
func (s *CPESet) Size() int

Clear

清空集合。

go
func (s *CPESet) Clear()

示例:

go
set := cpe.NewCPESet()

// 添加CPE
cpe1, _ := cpe.ParseCpe23("cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*")
cpe2, _ := cpe.ParseCpe23("cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*")

added1 := set.Add(cpe1)
added2 := set.Add(cpe2)
added3 := set.Add(cpe1) // 重复添加

fmt.Printf("添加结果: %t, %t, %t\n", added1, added2, added3)
fmt.Printf("集合大小: %d\n", set.Size())

// 检查包含
if set.Contains(cpe1) {
    fmt.Println("集合包含Windows CPE")
}

// 移除CPE
removed := set.Remove(cpe1)
fmt.Printf("移除结果: %t, 新大小: %d\n", removed, set.Size())

集合运算

Union

并集操作。

go
func (s *CPESet) Union(other *CPESet) *CPESet

Intersection

交集操作。

go
func (s *CPESet) Intersection(other *CPESet) *CPESet

Difference

差集操作。

go
func (s *CPESet) Difference(other *CPESet) *CPESet

SymmetricDifference

对称差集操作。

go
func (s *CPESet) SymmetricDifference(other *CPESet) *CPESet

示例:

go
// 创建两个集合
set1 := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
})

set2 := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
    "cpe:2.3:a:oracle:java:11.0.12:*:*:*:*:*:*:*",
})

fmt.Printf("集合1大小: %d\n", set1.Size())
fmt.Printf("集合2大小: %d\n", set2.Size())

// 并集
union := set1.Union(set2)
fmt.Printf("并集大小: %d\n", union.Size())

// 交集
intersection := set1.Intersection(set2)
fmt.Printf("交集大小: %d\n", intersection.Size())

// 差集
difference := set1.Difference(set2)
fmt.Printf("差集大小: %d\n", difference.Size())

// 对称差集
symDiff := set1.SymmetricDifference(set2)
fmt.Printf("对称差集大小: %d\n", symDiff.Size())

集合比较

Equals

检查两个集合是否相等。

go
func (s *CPESet) Equals(other *CPESet) bool

IsSubsetOf

检查是否为另一个集合的子集。

go
func (s *CPESet) IsSubsetOf(other *CPESet) bool

IsSupersetOf

检查是否为另一个集合的超集。

go
func (s *CPESet) IsSupersetOf(other *CPESet) bool

IsDisjoint

检查两个集合是否不相交。

go
func (s *CPESet) IsDisjoint(other *CPESet) bool

示例:

go
setA := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
})

setB := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
})

setC := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
})

fmt.Printf("A == B: %t\n", setA.Equals(setB))
fmt.Printf("B ⊆ A: %t\n", setB.IsSubsetOf(setA))
fmt.Printf("A ⊇ B: %t\n", setA.IsSupersetOf(setB))
fmt.Printf("A ∩ C = ∅: %t\n", setA.IsDisjoint(setC))

过滤操作

Filter

使用自定义函数过滤集合。

go
func (s *CPESet) Filter(predicate func(*CPE) bool) *CPESet

FilterByVendor

按供应商过滤。

go
func (s *CPESet) FilterByVendor(vendor string) *CPESet

FilterByProduct

按产品过滤。

go
func (s *CPESet) FilterByProduct(product string) *CPESet

FilterByPart

按组件类型过滤。

go
func (s *CPESet) FilterByPart(part string) *CPESet

FilterByVersion

按版本过滤。

go
func (s *CPESet) FilterByVersion(version string) *CPESet

示例:

go
// 创建大集合
largeSet := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:http_server:2.4.41:*:*:*:*:*:*:*",
    "cpe:2.3:o:canonical:ubuntu:20.04:*:*:*:*:*:*:*",
})

fmt.Printf("原始集合大小: %d\n", largeSet.Size())

// 按供应商过滤
microsoftCPEs := largeSet.FilterByVendor("microsoft")
fmt.Printf("Microsoft CPE数量: %d\n", microsoftCPEs.Size())

// 按组件类型过滤
applications := largeSet.FilterByPart("a")
fmt.Printf("应用程序数量: %d\n", applications.Size())

// 自定义过滤
versionedApps := largeSet.Filter(func(cpe *cpe.CPE) bool {
    return cpe.Part.ShortName == "a" && cpe.Version != "*"
})
fmt.Printf("有版本号的应用程序: %d\n", versionedApps.Size())

转换操作

ToSlice

转换为CPE切片。

go
func (s *CPESet) ToSlice() []*CPE

ToStringSlice

转换为字符串切片。

go
func (s *CPESet) ToStringSlice() []string

Map

映射转换。

go
func (s *CPESet) Map(mapper func(*CPE) interface{}) []interface{}

示例:

go
set := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
})

// 转换为切片
cpeSlice := set.ToSlice()
fmt.Printf("CPE切片长度: %d\n", len(cpeSlice))

// 转换为字符串切片
stringSlice := set.ToStringSlice()
fmt.Printf("字符串切片: %v\n", stringSlice)

// 映射转换 - 提取供应商名称
vendors := set.Map(func(cpe *cpe.CPE) interface{} {
    return cpe.Vendor
})
fmt.Printf("供应商列表: %v\n", vendors)

聚合操作

GroupBy

按指定字段分组。

go
func (s *CPESet) GroupBy(keyFunc func(*CPE) string) map[string]*CPESet

CountBy

按指定字段计数。

go
func (s *CPESet) CountBy(keyFunc func(*CPE) string) map[string]int

ForEach

遍历集合中的每个元素。

go
func (s *CPESet) ForEach(action func(*CPE))

示例:

go
set := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:http_server:2.4.41:*:*:*:*:*:*:*",
})

// 按供应商分组
vendorGroups := set.GroupBy(func(cpe *cpe.CPE) string {
    return cpe.Vendor
})

fmt.Println("按供应商分组:")
for vendor, group := range vendorGroups {
    fmt.Printf("  %s: %d 个CPE\n", vendor, group.Size())
}

// 按组件类型计数
partCounts := set.CountBy(func(cpe *cpe.CPE) string {
    return cpe.Part.LongName
})

fmt.Println("按组件类型计数:")
for part, count := range partCounts {
    fmt.Printf("  %s: %d\n", part, count)
}

// 遍历操作
fmt.Println("所有CPE:")
set.ForEach(func(cpe *cpe.CPE) {
    fmt.Printf("  %s %s\n", cpe.Vendor, cpe.ProductName)
})

统计分析

GetStatistics

获取集合统计信息。

go
func (s *CPESet) GetStatistics() *SetStatistics

SetStatistics

集合统计信息结构。

go
type SetStatistics struct {
    TotalCount           int                    // 总数量
    ApplicationCount     int                    // 应用程序数量
    OperatingSystemCount int                    // 操作系统数量
    HardwareCount        int                    // 硬件数量
    UniqueVendors        int                    // 唯一供应商数量
    UniqueProducts       int                    // 唯一产品数量
    VendorDistribution   map[string]int         // 供应商分布
    ProductDistribution  map[string]int         // 产品分布
    VersionDistribution  map[string]int         // 版本分布
}

示例:

go
set := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
    "cpe:2.3:o:canonical:ubuntu:20.04:*:*:*:*:*:*:*",
})

stats := set.GetStatistics()

fmt.Printf("集合统计信息:\n")
fmt.Printf("  总数量: %d\n", stats.TotalCount)
fmt.Printf("  应用程序: %d\n", stats.ApplicationCount)
fmt.Printf("  操作系统: %d\n", stats.OperatingSystemCount)
fmt.Printf("  硬件: %d\n", stats.HardwareCount)
fmt.Printf("  唯一供应商: %d\n", stats.UniqueVendors)
fmt.Printf("  唯一产品: %d\n", stats.UniqueProducts)

fmt.Println("供应商分布:")
for vendor, count := range stats.VendorDistribution {
    fmt.Printf("  %s: %d\n", vendor, count)
}

持久化操作

SaveToFile

保存集合到文件。

go
func (s *CPESet) SaveToFile(filename string) error

LoadFromFile

从文件加载集合。

go
func LoadCPESetFromFile(filename string) (*CPESet, error)

ExportToCSV

导出为CSV格式。

go
func (s *CPESet) ExportToCSV(filename string) error

ExportToJSON

导出为JSON格式。

go
func (s *CPESet) ExportToJSON(filename string) error

示例:

go
set := cpe.NewCPESetFromStrings([]string{
    "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
    "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
})

// 保存到文件
err := set.SaveToFile("cpe_set.json")
if err != nil {
    log.Printf("保存失败: %v", err)
} else {
    fmt.Println("✅ 集合已保存")
}

// 从文件加载
loadedSet, err := cpe.LoadCPESetFromFile("cpe_set.json")
if err != nil {
    log.Printf("加载失败: %v", err)
} else {
    fmt.Printf("✅ 加载了 %d 个CPE\n", loadedSet.Size())
}

// 导出为CSV
err = set.ExportToCSV("cpe_set.csv")
if err != nil {
    log.Printf("CSV导出失败: %v", err)
} else {
    fmt.Println("✅ CSV导出成功")
}

完整示例

go
package main

import (
    "fmt"
    "log"
    "github.com/scagogogo/cpe"
)

func main() {
    fmt.Println("=== CPE集合操作示例 ===")
    
    // 创建示例数据
    cpeStrings := []string{
        "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*",
        "cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
        "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*",
        "cpe:2.3:a:apache:http_server:2.4.41:*:*:*:*:*:*:*",
        "cpe:2.3:a:oracle:java:11.0.12:*:*:*:*:*:*:*",
        "cpe:2.3:o:canonical:ubuntu:20.04:*:*:*:*:*:*:*",
    }
    
    // 创建集合
    allCPEs := cpe.NewCPESetFromStrings(cpeStrings)
    fmt.Printf("创建了包含 %d 个CPE的集合\n", allCPEs.Size())
    
    // 过滤操作
    fmt.Println("\n=== 过滤操作 ===")
    
    microsoftCPEs := allCPEs.FilterByVendor("microsoft")
    fmt.Printf("Microsoft产品: %d\n", microsoftCPEs.Size())
    
    applications := allCPEs.FilterByPart("a")
    fmt.Printf("应用程序: %d\n", applications.Size())
    
    // 自定义过滤
    webServers := allCPEs.Filter(func(cpe *cpe.CPE) bool {
        return cpe.ProductName == "tomcat" || cpe.ProductName == "http_server"
    })
    fmt.Printf("Web服务器: %d\n", webServers.Size())
    
    // 集合运算
    fmt.Println("\n=== 集合运算 ===")
    
    set1 := allCPEs.FilterByVendor("microsoft")
    set2 := allCPEs.FilterByVendor("apache")
    
    fmt.Printf("Microsoft集合大小: %d\n", set1.Size())
    fmt.Printf("Apache集合大小: %d\n", set2.Size())
    
    union := set1.Union(set2)
    fmt.Printf("并集大小: %d\n", union.Size())
    
    intersection := set1.Intersection(set2)
    fmt.Printf("交集大小: %d\n", intersection.Size())
    
    // 分组统计
    fmt.Println("\n=== 分组统计 ===")
    
    vendorGroups := allCPEs.GroupBy(func(cpe *cpe.CPE) string {
        return cpe.Vendor
    })
    
    fmt.Println("按供应商分组:")
    for vendor, group := range vendorGroups {
        fmt.Printf("  %s: %d 个产品\n", vendor, group.Size())
        
        // 显示该供应商的产品
        group.ForEach(func(cpe *cpe.CPE) {
            fmt.Printf("    - %s %s\n", cpe.ProductName, cpe.Version)
        })
    }
    
    // 统计分析
    fmt.Println("\n=== 统计分析 ===")
    
    stats := allCPEs.GetStatistics()
    fmt.Printf("统计信息:\n")
    fmt.Printf("  总数量: %d\n", stats.TotalCount)
    fmt.Printf("  应用程序: %d\n", stats.ApplicationCount)
    fmt.Printf("  操作系统: %d\n", stats.OperatingSystemCount)
    fmt.Printf("  唯一供应商: %d\n", stats.UniqueVendors)
    fmt.Printf("  唯一产品: %d\n", stats.UniqueProducts)
    
    // 转换操作
    fmt.Println("\n=== 转换操作 ===")
    
    // 提取供应商列表
    vendors := allCPEs.Map(func(cpe *cpe.CPE) interface{} {
        return cpe.Vendor
    })
    
    // 去重
    uniqueVendors := make(map[string]bool)
    for _, vendor := range vendors {
        uniqueVendors[vendor.(string)] = true
    }
    
    fmt.Printf("唯一供应商列表: ")
    for vendor := range uniqueVendors {
        fmt.Printf("%s ", vendor)
    }
    fmt.Println()
    
    // 持久化
    fmt.Println("\n=== 持久化操作 ===")
    
    // 保存为JSON
    err := allCPEs.SaveToFile("all_cpes.json")
    if err != nil {
        log.Printf("保存失败: %v", err)
    } else {
        fmt.Println("✅ 集合已保存为JSON")
    }
    
    // 导出为CSV
    err = allCPEs.ExportToCSV("all_cpes.csv")
    if err != nil {
        log.Printf("CSV导出失败: %v", err)
    } else {
        fmt.Println("✅ 集合已导出为CSV")
    }
    
    // 从文件加载验证
    loadedSet, err := cpe.LoadCPESetFromFile("all_cpes.json")
    if err != nil {
        log.Printf("加载失败: %v", err)
    } else {
        fmt.Printf("✅ 从文件加载了 %d 个CPE\n", loadedSet.Size())
        
        // 验证数据一致性
        if allCPEs.Equals(loadedSet) {
            fmt.Println("✅ 数据一致性验证通过")
        } else {
            fmt.Println("❌ 数据一致性验证失败")
        }
    }
}

下一步

Released under the MIT License.