commit
This commit is contained in:
587
trie.go.bak
Normal file
587
trie.go.bak
Normal file
@@ -0,0 +1,587 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dghubble/trie"
|
||||
)
|
||||
|
||||
// PathNode 表示路径节点,用于BFS遍历
|
||||
type PathNode struct {
|
||||
Path string // 完整路径
|
||||
Level int // 层级深度
|
||||
ParentID *int64 // 父节点ID,根节点为nil
|
||||
IsFile bool // 是否是文件
|
||||
}
|
||||
|
||||
// MFiles 表示 m_files 表的记录
|
||||
type MFiles struct {
|
||||
ID int64 `json:"id"`
|
||||
FileName string `json:"file_name"`
|
||||
FileType string `json:"file_type"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
FileKey string `json:"file_key"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// MDocuments 表示 m_documents 表的记录
|
||||
type MDocuments struct {
|
||||
ID int64 `json:"id"`
|
||||
ParentID int64 `json:"parent_id"`
|
||||
FileID *int64 `json:"file_id"`
|
||||
Type string `json:"type"` // 'file' or 'dir'
|
||||
Path string `json:"path"`
|
||||
ProjectID int64 `json:"project_id"`
|
||||
UID int64 `json:"uid"`
|
||||
IsDelete int8 `json:"is_delete"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
DeletedAt *string `json:"deleted_at"`
|
||||
Alias string `json:"alias"`
|
||||
}
|
||||
|
||||
// DatabaseSimulator 数据库模拟器
|
||||
type DatabaseSimulator struct {
|
||||
filesTable map[string]*MFiles // file_key -> record
|
||||
documentsTable map[string]*MDocuments // path -> record
|
||||
nextFileID int64
|
||||
nextDocID int64
|
||||
projectID int64 // 固定项目ID
|
||||
uid int64 // 固定用户ID
|
||||
}
|
||||
|
||||
// NewDatabaseSimulator 创建数据库模拟器
|
||||
func NewDatabaseSimulator(projectID, uid int64) *DatabaseSimulator {
|
||||
return &DatabaseSimulator{
|
||||
filesTable: make(map[string]*MFiles),
|
||||
documentsTable: make(map[string]*MDocuments),
|
||||
nextFileID: 1,
|
||||
nextDocID: 1,
|
||||
projectID: projectID,
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// isFilePath 判断路径是否为文件(基于是否有扩展名)
|
||||
func isFilePath(path string) bool {
|
||||
// 获取路径的最后一部分
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
fileName := parts[len(parts)-1]
|
||||
// 如果包含点且不是以点开头,则认为是文件
|
||||
return strings.Contains(fileName, ".") && !strings.HasPrefix(fileName, ".")
|
||||
}
|
||||
|
||||
// getFileName 从路径获取文件名
|
||||
func getFileName(path string) string {
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
// getFileExtension 获取文件扩展名
|
||||
func getFileExtension(fileName string) string {
|
||||
parts := strings.Split(fileName, ".")
|
||||
if len(parts) <= 1 {
|
||||
return ""
|
||||
}
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
// ProcessFileOperation 处理文件操作(插入或更新 m_files 表)
|
||||
func (db *DatabaseSimulator) ProcessFileOperation(fileKey string) (*MFiles, string) {
|
||||
fileName := getFileName(fileKey)
|
||||
fileExt := getFileExtension(fileName)
|
||||
currentTime := time.Now().Format("2006-01-02 15:04:05.000")
|
||||
|
||||
if existingFile, exists := db.filesTable[fileKey]; exists {
|
||||
// 文件已存在,更新记录
|
||||
updateSQL := fmt.Sprintf(`UPDATE m_files SET
|
||||
file_name = '%s',
|
||||
file_type = '%s',
|
||||
updated_at = '%s'
|
||||
WHERE file_key = '%s';`,
|
||||
fileName, fileExt, currentTime, fileKey)
|
||||
|
||||
existingFile.FileName = fileName
|
||||
existingFile.FileType = fileExt
|
||||
|
||||
return existingFile, updateSQL
|
||||
} else {
|
||||
// 插入新文件记录
|
||||
newFile := &MFiles{
|
||||
ID: db.nextFileID,
|
||||
FileName: fileName,
|
||||
FileType: fileExt,
|
||||
FileSize: 1024, // 模拟文件大小
|
||||
FileKey: fileKey,
|
||||
CreatedAt: currentTime,
|
||||
}
|
||||
|
||||
insertSQL := fmt.Sprintf(`INSERT INTO m_files (id, file_name, file_type, file_size, file_key, created_at)
|
||||
VALUES (%d, '%s', '%s', %d, '%s', '%s');`,
|
||||
newFile.ID, newFile.FileName, newFile.FileType, newFile.FileSize, newFile.FileKey, newFile.CreatedAt)
|
||||
|
||||
db.filesTable[fileKey] = newFile
|
||||
db.nextFileID++
|
||||
|
||||
return newFile, insertSQL
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessDocumentOperation 处理文档操作(插入或跳过 m_documents 表)
|
||||
func (db *DatabaseSimulator) ProcessDocumentOperation(path string, parentID *int64, isFile bool, fileID *int64) (*MDocuments, string) {
|
||||
currentTime := time.Now().Format("2006-01-02 15:04:05.000")
|
||||
|
||||
// 检查是否已存在且未删除
|
||||
if existingDoc, exists := db.documentsTable[path]; exists && existingDoc.IsDelete == 0 {
|
||||
// 文档已存在且未删除,不处理
|
||||
return existingDoc, ""
|
||||
}
|
||||
|
||||
// 确定父级ID(如果没有父级则为0)
|
||||
actualParentID := int64(0)
|
||||
if parentID != nil {
|
||||
actualParentID = *parentID
|
||||
}
|
||||
|
||||
// 确定文档类型
|
||||
docType := "dir"
|
||||
if isFile {
|
||||
docType = "file"
|
||||
}
|
||||
|
||||
// 插入新文档记录
|
||||
newDoc := &MDocuments{
|
||||
ID: db.nextDocID,
|
||||
ParentID: actualParentID,
|
||||
FileID: fileID,
|
||||
Type: docType,
|
||||
Path: path,
|
||||
ProjectID: db.projectID,
|
||||
UID: db.uid,
|
||||
IsDelete: 0,
|
||||
CreatedAt: currentTime,
|
||||
UpdatedAt: currentTime,
|
||||
DeletedAt: nil,
|
||||
Alias: getFileName(path),
|
||||
}
|
||||
|
||||
fileIDStr := "NULL"
|
||||
if fileID != nil {
|
||||
fileIDStr = fmt.Sprintf("%d", *fileID)
|
||||
}
|
||||
|
||||
insertSQL := fmt.Sprintf(`INSERT INTO m_documents (id, parent_id, file_id, type, path, project_id, uid, is_delete, created_at, updated_at, alias)
|
||||
VALUES (%d, %d, %s, '%s', '%s', %d, %d, %d, '%s', '%s', '%s');`,
|
||||
newDoc.ID, newDoc.ParentID, fileIDStr, newDoc.Type, newDoc.Path,
|
||||
newDoc.ProjectID, newDoc.UID, newDoc.IsDelete, newDoc.CreatedAt, newDoc.UpdatedAt, newDoc.Alias)
|
||||
|
||||
db.documentsTable[path] = newDoc
|
||||
db.nextDocID++
|
||||
|
||||
return newDoc, insertSQL
|
||||
}
|
||||
|
||||
// PathTrieBuilder 路径字典树构建器
|
||||
type PathTrieBuilder struct {
|
||||
trie *trie.PathTrie
|
||||
}
|
||||
|
||||
// NewPathTrieBuilder 创建新的路径字典树构建器
|
||||
func NewPathTrieBuilder() *PathTrieBuilder {
|
||||
return &PathTrieBuilder{
|
||||
trie: trie.NewPathTrie(),
|
||||
}
|
||||
}
|
||||
|
||||
// BuildTrie 构建字典树
|
||||
func (ptb *PathTrieBuilder) BuildTrie(paths []string) {
|
||||
for _, path := range paths {
|
||||
// 将路径按分隔符分割
|
||||
parts := strings.Split(path, "/")
|
||||
|
||||
// 逐步构建路径并插入到trie中
|
||||
currentPath := ""
|
||||
for i, part := range parts {
|
||||
if i == 0 {
|
||||
currentPath = part
|
||||
} else {
|
||||
currentPath = currentPath + "/" + part
|
||||
}
|
||||
|
||||
// 将当前路径插入到trie中,值为路径的层级
|
||||
ptb.trie.Put(currentPath, len(strings.Split(currentPath, "/")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BFSTraverse 使用BFS遍历字典树,构建带有父子关系的路径节点
|
||||
func (ptb *PathTrieBuilder) BFSTraverse() []PathNode {
|
||||
var result []PathNode
|
||||
var queue []PathNode
|
||||
|
||||
// 获取所有根路径(第一层)
|
||||
ptb.trie.Walk(func(key string, value interface{}) error {
|
||||
level := value.(int)
|
||||
if level == 1 { // 第一层的路径
|
||||
queue = append(queue, PathNode{
|
||||
Path: key,
|
||||
Level: level,
|
||||
ParentID: nil,
|
||||
IsFile: isFilePath(key),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// BFS遍历
|
||||
for len(queue) > 0 {
|
||||
// 取出队列头部节点
|
||||
current := queue[0]
|
||||
queue = queue[1:]
|
||||
|
||||
// 添加到结果中
|
||||
result = append(result, current)
|
||||
|
||||
// 查找当前节点的子节点
|
||||
ptb.trie.Walk(func(key string, value interface{}) error {
|
||||
level := value.(int)
|
||||
// 如果是当前路径的直接子路径
|
||||
if strings.HasPrefix(key, current.Path+"/") && level == current.Level+1 {
|
||||
// 确保是直接子路径,而不是孙子路径
|
||||
remainingPath := strings.TrimPrefix(key, current.Path+"/")
|
||||
if !strings.Contains(remainingPath, "/") {
|
||||
childNode := PathNode{
|
||||
Path: key,
|
||||
Level: level,
|
||||
ParentID: nil,
|
||||
IsFile: isFilePath(key),
|
||||
}
|
||||
queue = append(queue, childNode)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ProcessPathsWithDatabase 处理路径并生成数据库操作
|
||||
func ProcessPathsWithDatabase(paths []PathNode) {
|
||||
fmt.Println("=== 模拟数据库操作 ===")
|
||||
fmt.Println("按BFS顺序处理路径并生成SQL语句:")
|
||||
fmt.Println()
|
||||
|
||||
// 创建数据库模拟器
|
||||
db := NewDatabaseSimulator(1, 1001) // projectID=1, uid=1001
|
||||
pathToDocIDMap := make(map[string]int64) // 路径到document ID的映射
|
||||
|
||||
// 预先插入一些已存在的记录
|
||||
fmt.Println("=== 预置数据库记录 ===")
|
||||
existingDocs := []*MDocuments{
|
||||
{ID: 100, ParentID: 0, FileID: nil, Type: "dir", Path: "home", ProjectID: 1, UID: 1001, IsDelete: 0, Alias: "home"},
|
||||
{ID: 101, ParentID: 100, FileID: nil, Type: "dir", Path: "home/user", ProjectID: 1, UID: 1001, IsDelete: 0, Alias: "user"},
|
||||
}
|
||||
|
||||
for _, doc := range existingDocs {
|
||||
db.documentsTable[doc.Path] = doc
|
||||
pathToDocIDMap[doc.Path] = doc.ID
|
||||
if doc.ID >= db.nextDocID {
|
||||
db.nextDocID = doc.ID + 1
|
||||
}
|
||||
fmt.Printf("已存在文档: ID=%d, Path=%s, Type=%s, ParentID=%d\n",
|
||||
doc.ID, doc.Path, doc.Type, doc.ParentID)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// 统计计数器
|
||||
var (
|
||||
filesInserted = 0
|
||||
filesUpdated = 0
|
||||
dirsInserted = 0
|
||||
dirsSkipped = 0
|
||||
sqlStatements []string
|
||||
)
|
||||
|
||||
// 处理BFS遍历的路径
|
||||
for i, node := range paths {
|
||||
// 获取父节点ID
|
||||
parentID := getParentDocumentID(node.Path, pathToDocIDMap)
|
||||
|
||||
fmt.Printf("[%03d] 处理路径: %s (Level %d, Type: %s)\n",
|
||||
i+1, node.Path, node.Level, func() string {
|
||||
if node.IsFile {
|
||||
return "文件"
|
||||
}
|
||||
return "目录"
|
||||
}())
|
||||
|
||||
if node.IsFile {
|
||||
// 处理文件
|
||||
fmt.Printf(" → 1. 处理文件表 (m_files):\n")
|
||||
|
||||
// 先处理 m_files 表
|
||||
fileRecord, fileSQL := db.ProcessFileOperation(node.Path)
|
||||
if strings.Contains(fileSQL, "INSERT") {
|
||||
fmt.Printf(" INSERT文件: %s\n", fileSQL)
|
||||
filesInserted++
|
||||
} else if strings.Contains(fileSQL, "UPDATE") {
|
||||
fmt.Printf(" UPDATE文件: %s\n", fileSQL)
|
||||
filesUpdated++
|
||||
}
|
||||
sqlStatements = append(sqlStatements, fileSQL)
|
||||
|
||||
// 再处理 m_documents 表
|
||||
fmt.Printf(" → 2. 处理文档表 (m_documents):\n")
|
||||
docRecord, docSQL := db.ProcessDocumentOperation(node.Path, parentID, true, &fileRecord.ID)
|
||||
if docSQL != "" {
|
||||
fmt.Printf(" INSERT文档: %s\n", docSQL)
|
||||
pathToDocIDMap[node.Path] = docRecord.ID
|
||||
dirsInserted++
|
||||
sqlStatements = append(sqlStatements, docSQL)
|
||||
} else {
|
||||
fmt.Printf(" SKIP文档: 路径已存在且未删除\n")
|
||||
dirsSkipped++
|
||||
}
|
||||
} else {
|
||||
// 处理目录
|
||||
fmt.Printf(" → 处理文档表 (m_documents) - 目录:\n")
|
||||
docRecord, docSQL := db.ProcessDocumentOperation(node.Path, parentID, false, nil)
|
||||
if docSQL != "" {
|
||||
fmt.Printf(" INSERT目录: %s\n", docSQL)
|
||||
pathToDocIDMap[node.Path] = docRecord.ID
|
||||
dirsInserted++
|
||||
sqlStatements = append(sqlStatements, docSQL)
|
||||
} else {
|
||||
fmt.Printf(" SKIP目录: 路径已存在且未删除\n")
|
||||
// 即使跳过,也要记录ID供子节点使用
|
||||
if existingDoc, exists := db.documentsTable[node.Path]; exists {
|
||||
pathToDocIDMap[node.Path] = existingDoc.ID
|
||||
}
|
||||
dirsSkipped++
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// 输出统计信息
|
||||
fmt.Println("=== 操作统计 ===")
|
||||
fmt.Printf("总共处理路径: %d 个\n", len(paths))
|
||||
fmt.Printf("文件插入: %d 条\n", filesInserted)
|
||||
fmt.Printf("文件更新: %d 条\n", filesUpdated)
|
||||
fmt.Printf("目录插入: %d 条\n", dirsInserted)
|
||||
fmt.Printf("目录跳过: %d 条\n", dirsSkipped)
|
||||
fmt.Printf("生成SQL语句: %d 条\n", len(sqlStatements))
|
||||
|
||||
fmt.Println("\n=== 完整SQL执行顺序 ===")
|
||||
for i, sql := range sqlStatements {
|
||||
fmt.Printf("[%03d] %s\n", i+1, sql)
|
||||
}
|
||||
}
|
||||
|
||||
// getParentDocumentID 根据路径获取父文档ID
|
||||
func getParentDocumentID(path string, pathToDocIDMap map[string]int64) *int64 {
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) <= 1 {
|
||||
return nil // 根节点没有父节点
|
||||
}
|
||||
|
||||
// 构建父路径
|
||||
parentParts := parts[:len(parts)-1]
|
||||
parentPath := strings.Join(parentParts, "/")
|
||||
|
||||
if parentID, exists := pathToDocIDMap[parentPath]; exists {
|
||||
return &parentID
|
||||
}
|
||||
|
||||
return nil
|
||||
} // GenerateSamplePaths 生成示例路径数据,包含文件和目录
|
||||
func GenerateSamplePaths() []string {
|
||||
return []string{
|
||||
// 文档目录相关
|
||||
"home/user/documents",
|
||||
"home/user/documents/readme.txt",
|
||||
"home/user/documents/project.pdf",
|
||||
"home/user/downloads",
|
||||
"home/user/downloads/setup.exe",
|
||||
"home/user/pictures/vacation",
|
||||
"home/user/pictures/vacation/beach.jpg",
|
||||
"home/user/pictures/vacation/sunset.png",
|
||||
"home/user/pictures/family",
|
||||
"home/user/pictures/family/photo1.jpg",
|
||||
|
||||
// 管理员目录
|
||||
"home/admin/config",
|
||||
"home/admin/config/app.yaml",
|
||||
"home/admin/logs/error",
|
||||
"home/admin/logs/error/app.log",
|
||||
"home/admin/logs/access",
|
||||
"home/admin/logs/access/access.log",
|
||||
|
||||
// 系统日志
|
||||
"var/log/system",
|
||||
"var/log/system/boot.log",
|
||||
"var/log/application/debug",
|
||||
"var/log/application/debug/debug.log",
|
||||
"var/log/application/info",
|
||||
"var/log/application/info/info.log",
|
||||
|
||||
// 缓存目录
|
||||
"var/cache/temp",
|
||||
"var/cache/temp/temp_file.tmp",
|
||||
"var/cache/sessions",
|
||||
"var/cache/sessions/session_123.dat",
|
||||
|
||||
// 用户程序目录
|
||||
"usr/local/bin",
|
||||
"usr/local/bin/app",
|
||||
"usr/local/lib/python",
|
||||
"usr/local/lib/python/module.py",
|
||||
"usr/local/lib/nodejs",
|
||||
"usr/local/lib/nodejs/package.json",
|
||||
|
||||
// 共享资源
|
||||
"usr/share/docs",
|
||||
"usr/share/docs/manual.pdf",
|
||||
"usr/share/icons/themes",
|
||||
"usr/share/icons/themes/icon.png",
|
||||
|
||||
// 配置文件
|
||||
"etc/nginx/sites",
|
||||
"etc/nginx/sites/default.conf",
|
||||
"etc/nginx/conf.d",
|
||||
"etc/nginx/conf.d/ssl.conf",
|
||||
"etc/ssl/certs",
|
||||
"etc/ssl/certs/server.crt",
|
||||
"etc/ssh/keys",
|
||||
"etc/ssh/keys/id_rsa.pub",
|
||||
|
||||
// 软件目录
|
||||
"opt/software/database",
|
||||
"opt/software/database/config.ini",
|
||||
"opt/software/webserver",
|
||||
"opt/software/webserver/httpd.conf",
|
||||
|
||||
// 临时上传
|
||||
"tmp/uploads/images",
|
||||
"tmp/uploads/images/upload.jpg",
|
||||
"tmp/uploads/documents",
|
||||
"tmp/uploads/documents/doc.docx",
|
||||
"tmp/cache/thumbnails",
|
||||
"tmp/cache/thumbnails/thumb.jpg",
|
||||
|
||||
// 数据备份
|
||||
"data/backups/daily",
|
||||
"data/backups/daily/backup_20240829.sql",
|
||||
"data/backups/weekly",
|
||||
"data/backups/weekly/backup_week34.tar.gz",
|
||||
"data/exports/csv",
|
||||
"data/exports/csv/users.csv",
|
||||
"data/exports/json",
|
||||
"data/exports/json/data.json",
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// // 密钥长度:32字节(256位)
|
||||
// key := make([]byte, 32)
|
||||
// // Nonce长度:12字节(96位)
|
||||
// nonce := make([]byte, 12)
|
||||
|
||||
// // 使用crypto/rand包安全地生成随机字节
|
||||
// _, err := rand.Read(key)
|
||||
// if err != nil {
|
||||
// log.Fatalf("Error generating key: %v", err)
|
||||
// }
|
||||
|
||||
// _, err = rand.Read(nonce)
|
||||
// if err != nil {
|
||||
// log.Fatalf("Error generating nonce: %v", err)
|
||||
// }
|
||||
|
||||
// // 打印生成的字节数组的字面值
|
||||
// fmt.Printf("生成的ChaCha20密钥:\n%#v\n", key)
|
||||
// fmt.Printf("生成的ChaCha20 Nonce:\n%#v\n", nonce)
|
||||
// db, err := gorm.Open(mysql.Open(""), &gorm.Config{
|
||||
// Logger: logger.Default.LogMode(logger.Info),
|
||||
// })
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// // 配置连接池
|
||||
// sqlDB, err := db.DB()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// // 设置连接池参数
|
||||
// if conf.MaxIdleConns > 0 {
|
||||
// sqlDB.SetMaxIdleConns(conf.MaxIdleConns)
|
||||
// }
|
||||
// if conf.MaxOpenConns > 0 {
|
||||
// sqlDB.SetMaxOpenConns(conf.MaxOpenConns)
|
||||
// }
|
||||
// if conf.ConnMaxLifetime > 0 {
|
||||
// sqlDB.SetConnMaxLifetime(time.Duration(conf.ConnMaxLifetime) * time.Second)
|
||||
// }
|
||||
|
||||
fmt.Println("=== 路径字典树构建和数据库操作演示 ===")
|
||||
fmt.Println()
|
||||
|
||||
// 生成示例路径
|
||||
paths := GenerateSamplePaths()
|
||||
fmt.Printf("生成了 %d 个示例路径(包含文件和目录):\n", len(paths))
|
||||
for i, path := range paths {
|
||||
pathType := "目录"
|
||||
if isFilePath(path) {
|
||||
pathType = "文件"
|
||||
}
|
||||
fmt.Printf(" [%02d] %s (%s)\n", i+1, path, pathType)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// 创建路径字典树构建器
|
||||
builder := NewPathTrieBuilder()
|
||||
|
||||
// 构建字典树
|
||||
fmt.Println("正在构建字典树...")
|
||||
builder.BuildTrie(paths)
|
||||
fmt.Println("字典树构建完成!")
|
||||
fmt.Println()
|
||||
|
||||
builder.trie.Walk(func(key string, value interface{}) error {
|
||||
println("key is ", key)
|
||||
return nil
|
||||
})
|
||||
|
||||
// BFS遍历字典树
|
||||
// fmt.Println("开始BFS遍历字典树...")
|
||||
// bfsResult := builder.BFSTraverse()
|
||||
// fmt.Printf("BFS遍历完成,共获得 %d 个节点\n", len(bfsResult))
|
||||
// fmt.Println()
|
||||
|
||||
// 处理路径并生成数据库操作
|
||||
// ProcessPathsWithDatabase(bfsResult)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("=== 程序执行完成 ===")
|
||||
|
||||
p := "a/b/c"
|
||||
dir, base := path.Split(p)
|
||||
println("dir:", dir, "base:", base)
|
||||
p = "a/b/c/"
|
||||
dir, base = path.Split(p)
|
||||
println("dir:", dir, "base:", base)
|
||||
println("tst", strings.Join([]string{"good"}, "/"))
|
||||
}
|
||||
Reference in New Issue
Block a user