Введение
Эта статья будет очень быстрой и предназначена только для некоторых моих друзей, изучающих язык программирования go. В этой статье я расскажу об использовании файла go.mod для имитации модуля для вашего проекта go.
Зачем использовать модули?
Если вы не новый разработчик, я уверен, что в конечном итоге у вас были такие большие проекты, что вам нужно было использовать модули. Если вы новичок, то зачем нужны модули для работы с кодом? Исходя из опыта, каждый язык работает с модулями, для новичка модуль — это просто еще один файл с кодом, который импортируется в основной файл кода или другие модули кода (начало модуля LOL). Разработчики используют модули не для того, чтобы сделать программиста быстрее, а для того, чтобы упростить организацию и написание кода. Например, у вас есть калькулятор, который вычисляет что угодно, от геометрических алгоритмов до алгебраических функций. Вы не хотите, чтобы один файл запускал весь этот код, это было бы полным беспорядком для организации. Поэтому, чтобы решить эту проблему, вы используете модули кода или другие файлы кода для разделения этих функций кода. В нашем случае эти файлы будут примерно такими.
- Multiply.go
- Вычесть.go
- питаг.го
- замена.go
- Разделяй.го
Так далее и тому подобное. В каком-то смысле модули существуют только для того, чтобы помочь разработчикам легче исправлять ошибки, не просматривая, скажем, 900 строк чистого чудовища и хаоса. Большинство языков программирования, таких как C++, C или даже Perl, имеют собственные расширения файлов для модулей кода или, скорее, наборов кодов. C использует файлы .h или файлы заголовков, Perl использует файлы .pm, известные как файлы модулей Perl, а C++ использует файлы .hpp или H++.
Введение в то, как модули используются в go
Использование модулей в языке программирования Go может быть немного странным, честно говоря, иногда это может даже раздражать тем, как вам приходится импортировать и вызывать другие функции. Одна вещь, которую следует отметить в отношении go, заключается в том, что он очень уважительно относится к типам данных, что также приводит к тому, что вы, программист, используете правильный английский язык. Я имею в виду, что при написании модулей go, если вы хотите правильно экспортировать свой код, каждая функция, переменная, структура, карта, срез или что-либо из вышеперечисленного ДОЛЖНЫ, и я имею в виду, ДОЛЖНЫ начинаться с заглавной буквы. Чтобы писать модули, нам сначала нужна файловая структура. В этой статье мы напишем программу, которая использует несколько файлов кода для извлечения текста из файла и помещения каждого слова в фрагмент. Сначала нам нужно построить нашу файловую структуру, поэтому для этого примера наше файловое дерево будет выглядеть следующим образом.
Первый файл, который вы заметите, называется go.mod. Что такое файл go.mod? В базовом формате файл go.mod — это файл, который файл main.go будет использовать для определения данных и имени модуля. Файлы go.mod начинаются со следующего синтаксиса
module.
Ключевое слово module сообщает файлу go.mod, что такое модуль программы, но как именно мы его сгенерируем? Мы можем создать новое имя модуля, выполнив следующую команду.
go mod init <modulename>
поэтому в нашем примере имя нашего модуля main
поэтому наша команда выглядит как go mod init main
. Обычно при создании файлов go.mod вам нужно имя того, как вы будете импортировать другие файлы. Поэтому, если в вашем файле go.mod указано, что имя модуля — main
, вы импортируете пути к файлам как таковые.
import main/modulefilepath
Вот как мы будем импортировать все файлы вместе в каталоге modules
в каталоге нашего проекта. При создании путей к файлам, таких как modules
, с несколькими файлами .go, нам нужно убедиться, что исходный файл go имеет то же имя пакета. Например, скажем, мы открываем файл с именем open.go. Когда мы заглянем внутрь этого файла, первое, что мы увидим, это
package package_name
какое бы имя вы ни видели после ключевого слова пакета, каждый файл должен использовать одно и то же имя пакета. Ради этих статей и проектов все файлы .go будут иметь имя пакета ex. Если имя пакета не совпадает во всех файлах .go в этом текущем каталоге, go выдаст ошибку.
Пишем наш код
Мы собираемся игнорировать файл main.go на данный момент, так как нам нужно работать с модулями. Итак, мы собираемся перейти к файлу var.go внутри каталога модулей.
- вар.го
Здесь у нас есть имя нашего пакета, установленное как EX, имейте в виду, что каждый файл с .go в пути к файлу модулей ДОЛЖЕН иметь это ОДИНАКОВОЕ ТОЧНОЕ ИМЯ ПАКЕТА. Если это что-то еще, golang выдаст ошибку, что он ожидает такое же имя пакета, но нашел такое-то имя пакета. Затем мы определяем структуру типа с именем Data
, эта структура будет содержать всю информацию, необходимую для работы нашей программы. При этом мы объявляем список переменных с 3 переменными. Первая переменная — это X, которая представляет собой формат ошибки, F — это *os.file, который используется в качестве указателя на тип os.file, и S — это переменная, которую мы будем импортировать в наш основной файл, чтобы получить структуру данных с именем Data
.
- open.go
Это основной файл, о котором нам нужно беспокоиться, поэтому у нас есть две функции, одна с именем open, другая с именем write. Это более продвинутый набор кода для начинающих, но позвольте мне объяснить, что происходит. Наша первая функция, которая у нас есть, называется Open
, но перед объявлением имени функции мы определяем структуру нашего типа данных с помощью указателя. Поэтому мы делаем это, реализуя (Struct *Data) перед вызовом имени функции. Это делает программу немного более производительной с точки зрения упрощения доступа к нашим данным и их возврата. После того, как имя функции объявлено, функция называет тип *os.File
, который является типом данных, которые мы будем возвращать в этой функции. Мы вызываем две переменные, обратите внимание, что, поскольку у нас есть одно и то же имя пакета вверху в одном и том же файле, мы можем вызывать локальные переменные, как если бы они находились в одном и том же файле. Golang обычно заставляет вас определять новые переменные с помощью :=
, но поскольку этот файл имеет то же имя пакета, что и var.go, мы можем импортировать переменные F и X, которые мы предварительно объявили, используя ключевое слово списка VAR, что означает, что мы можем использовать F и X вместе. используя =, так как они уже существуют как переменные, которые были ранее объявлены. Затем мы проверяем, является ли ошибка или переменная X нулевой ( Nil = NULL, 0x00, EMPTY ). Если она не пуста, мы распечатываем ошибку и выходим с кодом выхода один, что означает FAIL. Здесь нам не нужен оператор else, потому что в случае ошибки программа завершит работу, поэтому мы просто вернем F, которая снова является предварительно объявленной переменной типа *os.File, которую мы заполнили, когда открывали файл и проверяли наличие ошибка. Мы переходим к другой функции, которая использует ту же общую проверку, вместо того, чтобы возвращать тип данных, это просто средство записи для записи или добавления к предварительно объявленному списку, который у нас есть в нашей структуре типа данных, который установлен как Word_Array
типа [] string . Мы вызываем функцию открытия с F =, потому что мы переназначаем выходные или возвращаемые значения F, чтобы снова передать их в F. Теперь это кажется немного странным, почему мы должны вызывать функции, используя структуру данных, которая снова является Struct
, хотя наша структура данных явно не определяет никакой функции внутри себя? Ну, это потому, что go заставляет вас использовать эту структуру, когда вы назначаете ее определенной функции. Это то, что технически утверждает основная книга правил го при использовании модулей.
- Чтобы запустить функцию из другого файла go, который импортируется как модуль, функция ДОЛЖНА начинаться с заглавной буквы.
- При экспорте переменных переменные ДОЛЖНЫ начинаться с заглавной буквы
- (Это общее правило) при назначении функции структуры данных функция должна вызываться с этой структурой данных.
Это похоже на кривую обучения, но на самом деле это не так, чем больше вы знакомы с go, тем больше вы обнаружите, что используете его, поскольку он требует более надежного кода. Двигаясь дальше, мы вызываем функцию Struct.Open() и снова присваиваем ее предварительно объявленной переменной F. Как только мы это сделаем, мы вызовем новую переменную с именем scanner :=
. Scanner будет переменной, которая содержит значения из пакета BUFIO от нового сканера. Функция bufio.NewScanner — это функция в стандартном пакете BUFIO, которая позволяет инициировать новый сканер из STD (Standard) IN (Input) STDIN, что позволяет нам использовать нашу переменную F
типа *os.File
в качестве входных данных для функции. После того, как мы объявим сканер, мы вызываем scan.split, который установит функцию разделения для сканера, независимо от того, разделяет ли он слова, строки или что-то из вышеперечисленного. Затем мы используем функцию bufio.ScanWords, чтобы указать сканеру разделить отсканированные слова в пределах стандартного ввода. В некотором смысле после сканирования каждого слова в этой функции наш сканер будет возвращать каждый пробел, разделяющий следующее слово в отсканированном тексте. Мы создаем простой цикл for, вызывающий нашу переменную сканера, чтобы инициировать процесс сканирования. Функция Scan()
вернет false, когда сканирование остановится, что может произойти либо из-за достижения конца ввода, либо из-за ошибки. После того, как это вернет false, метод ошибки, как и любая другая функция, вернет любую ошибку, с которой столкнулся сканер, независимо от того, является ли это io.EOF (входной вывод END OF FILE) или если это что-то еще, что могло быть, например, сломанным потоком или что-то в этом духе. В этом цикле for мы вызываем нашу переменную типа []string
из нашей структуры, используя ключевое слово append, чтобы добавить значение к себе и текст из вызываемого здесь сканера.
Struct.Word_Array = append(Struct.Word_Array, scanner.Text())
Функция scan.Text() — это функция, которая будет вызывать сканер, который мы вернули, для вывода текста заданного отсканированного слова, что в английском смысле нашего цикла for работает следующим образом.
Для каждого слова, сканируемого сканером, добавьте его или добавьте в список строк, сканируемых сканером, который читается как текст.
Мы проверяем, обнаруживает ли сканер сообщение об ошибке, используя предварительно объявленную переменную X
, если scan.Err() не равно пустому, nil, NULL или целочисленному значению 0x00, затем регистрируем ошибку как фатальную ошибку и выходим. Опять же, поскольку оператор else становится избыточным, он нам не нужен, поэтому мы продолжаем работу после проверки ошибки и используем оператор defer, чтобы окончательно закрыть файл.
- main.go
Наконец-то мы дошли до конца, вот тут-то и будет чушь. Итак, вот что странно для понимания, в отличие от большинства языков, таких как python, ruby, c, c++, c#, java, crystal, fortran и других языков из этого списка, Golang НЕ заставляет вас импортировать имя файла, поэтому имя файла var. go может быть wsdilgshfghjklsdfjklsdfjkljkdfljklsdfjldfs.go и это не имеет значения. Это связано с тем, что мы использовали ключевое слово package
для определения имени пакета или того, что будет использоваться go для распознавания этих импортированных файлов. Как вы должны знать, файл, который компилируется в вашем каталоге или вы будете запускать, ДОЛЖЕН ИМЕТЬ package main
это не может быть ничего другого (как для этого сценария). если мы переименуем наш файл main.go в что-то вроде package runner
, мы получим следующую ошибку.
package runner is not a main package
Даже если бы мы запустили команду go mod init runner
, ошибка все равно была бы той же. В нашем основном файле у нас есть следующий код
Этот код немного дурацкий! Вам может быть интересно, почему у нас есть функция с именем init(), которая вообще не вызывается, а просто запускает функции. Сначала позвольте мне объяснить это состояние кода. Наш основной файл импортирует путь к файлу модуля, помните, как в файле go.mod
вы видели что-то вроде module main
, учитывая, что мы использовали команду go mod init main
для этого примера, каждый путь к файлу, который мы импортируем, ДОЛЖЕН начинаться с основного, иначе он не будет распознан. Также имейте в виду, что для вызова этого пути к модулю и вызова функций и переменных нам нужно назначить этому импорту имя, в нашем случае это имя EX, которое вы можете изменить на любое другое. Итак, функция инициализации, что это за функция инициализации? Функция init в golang действует как подготовка к программе. Функцию инициализации можно использовать несколькими способами, и для нее установлены правила, определяющие, как вы можете и не можете ее использовать.
- Вы НЕ можете вызывать функцию инициализации, init() является собственной функцией и запускается перед основным блоком кода (функция main())
- Init НЕ может принимать никаких аргументов
- Init НЕ МОЖЕТ ВОЗВРАЩАТЬ какие-либо аргументы
- Init НЕ может быть назначен тип
- Init ДОЛЖЕН использоваться в порядке организации
- Init МОЖЕТ использоваться несколько раз
Вы используете init, когда вам нужно загрузить определенные функции до того, как основной блок будет выполнен, как мы его использовали. Мы используем init для вызова EX.S.Filename, которая является переменной имени файла в нашей структуре данных Data
строки типа данных, и присваиваем этой переменной выходные или возвращаемые значения из os.args[1]. Причина, по которой мы начинаем индексацию с единицы, заключается в том, что после запуска файла или вызова go run main.go
main.go считается 0-м аргументом в массиве аргументов os. Мы можем увидеть это, если запустим программу для печати os.Args[0]
, мы получим следующее.
/tmp/go-build3213636202/b001/exe/основной
После того, как мы присвоим переменной имя файла значение, мы продолжаем вызывать модуль, который мы импортировали, чтобы вызвать структуру для вызова функции OPEN, которая примет, как было сказано ранее, переменную имени файла, откроет ее и передаст следующей функции, которая вызывается в пределах функция инициализации с именем EX.S.Writer() или наша функция Writer()
. Init всегда будет запускаться первым перед main, если мы объявим его, и, поскольку наши функции были вызваны, если программа не выдаст ошибку, это означает, что наше имя или переменная Word_Array
в нашей структуре данных Data
были заполнены значениями. Итак, мы запускаем цикл for, говорящий golang, что для _,k в пределах диапазона каждой отдельной части этого массива нужно напечатать k. Если вы новичок, вы можете быть сбиты с толку тем, почему существует _
. Ну, это существует, потому что вместо _
в другом случае была бы переменная, принимающая целочисленное значение. В цикле golang for range у вас должны быть две переменные, одна из которых имеет целочисленный тип, а вторая — строковый тип. Мы используем значение _
, потому что нам не нужно и мы никогда не используем целочисленное значение, полученное из цикла for, который мы вызвали для диапазона нашего массива. K содержит строки или отдельные слова, которые были добавлены в массив функцией Writer
.
Запускаем нашу программу
Когда мы запускаем программу go, здесь мы можем сделать одну из трех вещей: мы можем использовать команду go build
, за которой следует main.go
, чтобы создать файл и запустить его как файл EXE или ELF, мы можем использовать go run .
или мы можем использовать go run main.go
. Поскольку мы используем файл go.mod, мы можем использовать точку для представления нашего основного пакета, который будет скомпилирован и запущен go, go run main.go просто соберет и выполнит программу, но не создаст файл EXE или ELF (в нашем случае случай ). Ради нас я буду использовать следующую команду.
go run main.go filename.txt
Имя файла.txt выглядит так
И наш вывод выглядит следующим образом, когда мы запускаем программу
Как вы можете видеть, используя модули go, мы смогли успешно распечатать содержимое файла по отдельности слово за словом!
Краткое содержание
Использование модулей go может быть утомительным, как и синтаксис go, он может быть очень разочаровывающим, а иногда и раздражающим. Однако использование модулей в Go действительно удобно, и что я считаю наиболее полезным, так это тот факт, что вы можете импортировать целые файлы сразу, этот проект может быть, скажем, калькулятором и иметь файл module
с более чем 400 файлами исходного кода go. и все они могут быть импортированы сразу с помощью файла go.mod и простого назначения переменной с последующим импортом, который позволит вам вызывать большинство, если не все функции, переменные, структуры, константы и многое другое. Модули отлично подходят для больших проектов и упрощают работу с ними, например, когда вы пытаетесь организовать свой код или пытаетесь избавиться от большого количества беспорядочного кода. Люди считают модули бесполезными, но это то, на что вам действительно нужно обратить внимание, это делает ваш проект более красивым и чистым, а также предотвращает ошибки во время разработки, когда вы можете сосредоточиться только на одном файле для каждой темы, а не на определенном номере строки.