Коллеги объясните балбесу, почему? код не работает

Рейтинг: -1Ответов: 1Опубликовано: 24.03.2023

Так значит проблема вот в чем,решил я решил порефакторить функцию ,а переписал в итоге все покрасивше))) но чего-то не заводится. почему не могу понять))) Помогите))) и да вот репозиторий https://github.com/yuzameOne/cli_ip_range_creator/blob/main/main.go где все работает 3 горутины 2 канала (так что не пишите иди про горутины читай))))) проект учебный.... выеживаюсь хочу сделать как то по другому Ps.Заранее спасибо за ответ ))))

func main() {

    fmt.Println("Start work")

    start := time.Now()

    // Get current working directory.
    path, err := os.Getwd()
    if err != nil {
        fmt.Printf("Error getting current working directory: %v", err)
    }

    // Parse command line arguments.
    var filePath, savePath string
    flag.StringVar(&filePath, "ptf", "range_ip.txt", "Path to input file")
    flag.StringVar(&savePath, "ptsf", "", "Path to save output file")
    flag.Parse()

    // Check for missing input file path argument.
    if filePath == "" {
        fmt.Println("Missing required argument: path to input file")
    }

    // Set default output file path if not provided.
    if savePath == "" {
        savePath = path + "/new_" + filePath
    }

    // Open input file.
    inputFile, err := os.Open(filePath)
    if err != nil {
        fmt.Printf("Error opening input file: %v", err)
    }
    defer inputFile.Close()

    // Open output file.
    outputFile, err := os.OpenFile(savePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
    if err != nil {
        fmt.Printf("Error opening output file: %v", err)
    }
    defer outputFile.Close()

    // Create wait group.
    var wg sync.WaitGroup

    // Create input line scanner.
    scanner := bufio.NewScanner(inputFile)

    lineCount := 0
    for scanner.Scan() {
        lineCount++
    }

    for i := 0; i < lineCount; i++ {

        wg.Add(1)

        go func() {

            defer wg.Done()

            for scanner.Scan() {

                line := strings.TrimRight(scanner.Text(), "\n")

                partsIP := strings.Split(line, "-")

                firstIP := net.ParseIP(partsIP[0])
                secondIP := net.ParseIP(partsIP[1])

                firstIPOctets := firstIP.To4()
                secondIPOctets := secondIP.To4()

                if firstIPOctets[2] == secondIPOctets[2] && firstIPOctets[3] == secondIPOctets[3] {
                    return
                }

                if firstIPOctets[2] > secondIPOctets[2] {

                    for i := 0; i < 256; i++ {
                        outputFile.WriteString(fmt.Sprintf("%d.%d.%d.%d", firstIPOctets[0], firstIPOctets[1], firstIPOctets[2], i))
                    }
                }

                if firstIPOctets[2] == secondIPOctets[2] && firstIPOctets[3] != secondIPOctets[3] {

                    for i := 0; i < 256; i++ {
                        outputFile.WriteString(fmt.Sprintf("%d.%d.%d.%d", firstIPOctets[0], firstIPOctets[1], firstIPOctets[2], i))
                    }
                }

                if firstIPOctets[2] < secondIPOctets[2] {

                    for {

                        var ipresult string

                        for i := 0; i < 256; i++ {

                            outputFile.WriteString(fmt.Sprintf("%d.%d.%d.%d", firstIPOctets[0], firstIPOctets[1], firstIPOctets[2], i))

                        }

                        ipresult = fmt.Sprintf("%d.%d.%d.%d", firstIPOctets[0], firstIPOctets[1], firstIPOctets[2], 255)

                        if ipresult == partsIP[1] {
                            break
                        }

                        firstIPOctets[2] = firstIPOctets[2] + 1

                    }

                }

            }
        }()
    }

    // Wait for all goroutines to complete.
    wg.Wait()

    duration := time.Since(start)

    fmt.Printf("Time taken: %v, output file: %s\n", duration, savePath)
}

Ответы

▲ 1

Почему ваш код не работает. Удивительно, если бы он работал. Проблем более одной.

  1. Сначала вы прочитываете весь файл, когда подсчитываете количество строк:

     for scanner.Scan() {
         lineCount++
     }
    

    Как следствие, сканер находится в конце файла, и следующее обращение к scanner.Scan вернёт false. Соответственно, все горутины, в которых вы поставили цикл for scanner.Scan() { } в этот цикл не заходят, так как метод возвращает false.

  2. Теперь про горутины. Это вообще за гранью моего понимания.

    1. Во-первых, вы запускаете горутины по числу строк во входном файле. Зачем? Зачем вообще горутины, если вы последовательно читаете строку за строкой?
    2. Во-вторых, все горутины читают из одного источника - scanner. То есть горутины наперегонки пытаются считать строка за строкой из объекта, который не является tread-safe. Результат такого параллельного чтения непредсказуем.
    3. В-третьих, все горутины читают этот scanner до его исчерпания. То есть в случае небольших файлов одна горутина успеет до исчерпания своего кванта времени прочитать до конца весь файл (в предположении что scanner не пуст).
    4. В-четвёртых, все горутины пишут в один и тот же файл, и при этом каждая горутина пишет идентичные данные. То есть у вас будет по несколько экземпляров одних и тех же строк.

Я предполагаю, что вы хотели напечатать все адреса, принадлежащие диапазону, но делать это нужно иначе.

Я бы сделал итератор адресов

// Iterates addresses from Start till End inclusive.
type IPAddressIterator struct {
    Start net.IP
    End   net.IP
    value net.IP
}

// Returns true if the iterator is not over, and shifts to the next value
func (ipr *IPAddressIterator) Next() bool {
    if ipr.value == nil {
        ipr.value = ipr.Start
    } else {
        inc(ipr.value)
    }
    return bytes.Compare(ipr.value, ipr.End) <= 0
}

// Returns the current value of the iterator.
func (ipr *IPAddressIterator) Value() net.IP {
    if ipr.value == nil {
        panic("Value not initialized, call IPRangeIterator.Next() first")
    }
    return ipr.value
}

// Increments byte array from right to left
func inc(addr []byte) {
    for i := len(addr) - 1; i >= 0; i-- {
        addr[i] += 1
        if addr[i] != 0 {
            break
        }
    }
}

Тогда ваш код перебора адресов можно сильно упростить:

    // Create input line scanner.
    scanner := bufio.NewScanner(inputFile)

    ipRanges := []IPAddressIterator{}

    lineCount := 0
    for scanner.Scan() {
        lineCount++
        line := strings.TrimRight(scanner.Text(), "\n")
        partsIP := strings.Split(line, "-")

        firstIP := net.ParseIP(partsIP[0])
        secondIP := net.ParseIP(partsIP[1])
        ipRange := IPAddressIterator{
            Start: firstIP,
            End:   secondIP,
        }
        ipRanges = append(ipRanges, ipRange)
    }

    for _, iter := range ipRanges {
        for iter.Next() {
            fmt.Fprintln(outputFile, iter.Value())
        }
    }

На примере 31.22.48.0-31.22.50.15 этот код отрабатывает за 40 миллисекунд, генерирует 528 адресов (256+256+16)