مهندس نرم افزار، نویسنده، شاعر و خیال پرداز
کتابخانه های استاندارد در Go (بخش اول)
اهمیت کتابخانه های استاندارد در Go
کتابخانه های استاندارد Go مجموعه ای از بسته های مرکزی هستند که زبان را گسترش داده و بهبود می بخشند. این بسته ها به برنامه نویسان این امکان را می دهند که در توسعه نرم افزار خود نگران حل مسائل دیگری غیر از هدف برنامه خود نباشند. از آنجا که این بسته ها جزئی از زبان هستند برخی ویژگی ها در موردشان تضمین شده است:
- برای هر نسخه از زبان وجود خواهند داشت
- همیشه با نسخه های پیشین سازگار خواهند بود
- توسط توسعه دهندگان اصلی زبان نگهداری و بازبینی می شوند
- با هر نسخه از زبان مورد تست و ارزیابی قرار گرفته اند
این ویژگی ها باعث می شوند که کتابخانه های استاندارد زبان به بخش ویژه ای از آن تبدیل شده و شما را به استفاده بدون دردسر از آنها ترغیب کنند.
دسته بندی کتابخانه های استاندارد
کتابخانه های استاندارد Go شامل تعداد زیادی بسته هستند و پوشش کامل آنها در قالب یک پست امکان پذیر نخواهد بود. در حال حاضر بیش از 100 بسته که در 38 دسته مرتب شده اند، در زبان وجود دارند.
archive, debug, hash ,mime, sort, time, bufio, encoding,
html, net, strconv, unicode, bytes, errors, image, os, strings,
unsafe, compress, expvar, index, path, sync, container, flag,
io, reflect, syscall, crypto, fmt, log, regexp, testing, database,
go, math, runtime, text
برخی از دسته های ذکر شده در بالا خود، بسته های زبان هستند. برای توضیحات دقیق و جزئی تر می توانید مستندات زبان را بررسی کنید. این مستندات در قالب godoc تهیه شده و هر بسته را به تفکیک انواع و متدها تشریح می کنند. به عنوان نمونه Int از بسته rand (از زیر مجموعه های بسته math) به صورت زیر تشریح شده است:
func Int
func Int() int
Int returns a non-negative pseudo-random int from the default Source.
صرف نظر از نحوه نصب Go تمام کد کتابخانه های استاندارد در پوشه $GOROOT /src/pkg قابل دسترسی خواهند بود. در ادامه به معرفی چند بسته کاربردی و برخی از توابع آنها می پردازیم. در بخش دوم این پست نیز به بررسی کاربردی بسته io خواهیم پرداخت.
بسته strings
بسته strings توابع ساده ای را برای دستکاری رشته های UTF-8 پیاده سازی کرده است. برای مطالعه دقیق تر در مورد رشته های UTF-8 در Go این پست را بخوانید. در ادامه به معرفی و کاربرد برخی از توابع این بسته می پردازیم.
تابع Compare
این تابع با امضای Compare(a, b string) int به مقایسه دو رشته به صورت الفبایی پرداخته و مقدار 0 را در صورت a == b، مقدار 1 را در صورت a > b و مقدار -1 را در صورت a < b برمی گرداند. این تابع برای ایجاد تشابه با بسته bytes پیاده سازی شده است و استفاده از اپراتورهای ==، < و > بسیار سریع تر از این تابع هستند.
تابع Contains
این تابع با امضای Contains(s, substr string) bool بررسی می کند که آیا substr در s وجود دارد و مقداری بولین برمی گرداند.
تابع Index
این تابع با امضای Index(s, substr string) int ایندکس مربوط به اولین رخداد substr در s را برمی گرداند.
تابع Join
این تابع با امضای Join(elems []string, sep string) string مقادیر موجود در آرگومان اول را با جداساز (آرگومان دوم) به هم چسبانده و یک رشته واحد برمی گرداند.
تابع Replace
این تابع با امضای Replace(s, old, new string, n int) string مقدار old موجود در s را با مقدار new جایگزین کرده و رشته جدید را بر می گرداند. مقدار n تعداد تکرار این عملیات را مشخص می کند. اگر n کوچکتر از 0 باشد این عملیات برای همه رخدادها تکرار می شود.
تابع Split
این تابع با امضای Split(s, sep string) []string، یک رشته و یک جداساز را دریافت کرده و رشته را بر اساس وقوع جداساز به یک لیست از رشته ها تبدیل کرده و مقدار لیست را برمی گرداند.
بسته strconv
این بسته تبدیل انواع داده ها به رشته و برعکس را پیاده سازی کرده است. در ادامه به معرفی برخی از توابع این بسته می پردازیم.
توابع Atoi و Ltoa
تبدیل یک رشته به مقدار صحیح و برعکس توسط این توابع انجام می گردد.
i, err := strconv.Atoi("-42")
s := strconv.Itoa(-42)
توابع دسته Parse
این توابع تبدیل از رشته به مقادیر بولین، اعداد صحیح یا اعشاری را بر عهده دارند.
b, err := strconv.ParseBool("true")
f, err := strconv.ParseFloat("3.1415", 64)
i, err := strconv.ParseInt("-42", 10, 64)
u, err := strconv.ParseUint("42", 10, 64)
توابع دسته Format
این توابع تبدیل از مقادیر بولین، اعداد صحیح یا اعشاری به رشته را بر عهده دارند.
s := strconv.FormatBool(true)
s := strconv.FormatFloat(3.1415, 'E', -1, 64)
s := strconv.FormatInt(-42, 16)
s := strconv.FormatUint(42, 16)
بسته math
این بسته تابث ها و توابع ریاضی را در خود جای داده است. (مشکل اعداد اعشاری در زبان های برنامه نویسی در این بسته نیز پابرجا است.) در ادامه به معرفی برخی ثابت ها و توابع این بسته می پردازیم.
ثابت های ریاضی
const (
E = 2.71828182845904523536028747135266249775724709369995957496696763 // https://oeis.org/A001113
Pi = 3.14159265358979323846264338327950288419716939937510582097494459 // https://oeis.org/A000796
Phi = 1.61803398874989484820458683436563811772030917980576286213544862 // https://oeis.org/A001622
Sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974 // https://oeis.org/A002193
SqrtE = 1.64872127070012814684865078781416357165377610071014801157507931 // https://oeis.org/A019774
SqrtPi = 1.77245385090551602729816748334114518279754945612238712821380779 // https://oeis.org/A002161
SqrtPhi = 1.27201964951406896425242246173749149171560804184009624861664038 // https://oeis.org/A139339
Ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 // https://oeis.org/A002162
Log2E = 1 / Ln2
Ln10 = 2.30258509299404568401799145468436420760110148862877297603332790 // https://oeis.org/A002392
Log10E = 1 / Ln10
)
تابع Abs
این تابع با امضای Abs(x float64) float64 قدرمطلق مقدار x را برمی گرداند.
تابع Ceil
این تابع با امضای Ceil(x float64) float64 نزدیک ترین عدد صحیح بزرگتر از مقدار x را برمی گرداند.
تابع Floor
این تابع با امضای Floor(x float64) float64 نزدیک ترین عدد صحیح کوچکتر از مقدار x را برمی گرداند.
تابع Inf
این تابع با امضای Inf(sign int) float64 مقدار مثبت یا منفی بی نهایت را با توجه به مقدار sign برمی گرداند.
تابع Max
این تابع با امضای Max(x, y float64) float64 ماکزیمم دو عدد x و y را برمی گرداند.
تابع Min
این تابع با امضای Min(x, y float64) float64 مینیمم دو عدد x و y را برمی گرداند.
تابع Pow
این تابع با امضای Pow(x, y float64) float64 عدد x را به توان y رسانده و حاصل را برمی گرداند.
تابع Sqrt
این تابع با امضای Sqrt(x float64) float64 جذر عدد x را برمی گرداند.
بسته errors
این بسته برای مدیریت خطاها پیاده سازی شده است. تابع New برای ساخت یک خطای جدید مورد استفاده قرار می گیرد و تنها یک رشته را به عنوان ورودی می پذیرد.
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
}
بسته fmt
این بسته برای توابع ورودی و خروجی فرمت شده را پیاده سازی کرده است. عملکرد توابع این بسته مشابه printf و scanf از زبان C است. کلمات فرمت با الهام از زبان C ولی ساده تر از آن گسترش داده شده اند. برخی از کلمات چاپ عبارت اند از:
%v مقدار در فرمت پیشفرض
%#v مقدار در فرمت زبان
%T نوع مقدار
%t مقدار صحیح
%b عدد در مبنای دو
%d عدد در مبنای ده
%o عدد در مبنای هشت
%f عدد اعشاری
%s مقدار رشته
%p آدرس مقدار
در ادامه به بررسی برخی از توابع این بسته می پردازیم.
تابع Fscanf
این تابع با امضای Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) رشته ورودی را اسکن کرده و مقادیر تعیین شده در رشته فرمت شده را در مقادیر جای می دهد.
r := strings.NewReader("5 true gophers")
n, err := fmt.Fscanf(r, "%d %t %s", &i, &b, &s)
if err != nil {
fmt.Fprintf(os.Stderr, "Fscanf: %v\n", err)
}
fmt.Println(i, b, s)
fmt.Println(n)
تابع Sprintf
این تابع با امضای Sprintf(format string, a ...interface{}) string یک رشته فرمت شده به همراه متغیرهای ورودی را دریافت کرده و با جایگزین کردن مقدار متغیرها در رشته، رشته حاصل را بر می گرداند.
s := fmt.Sprintf("%s is %d years old.\n", "Mehrdad", 22)
بسته time
این بسته تسهیلاتی برای اندازه گیری و نمایش زمان را پیاده سازی کرد است. محاسبات بر اساس تاریخ میلادی هستند. برخی ثابت های بسته عبارت اند از:
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
در ادامه به معرفی برخی توابع این بسته می پردازیم.
تابع Sleep
این تابع با امضای Sleep(d Duration) اجرای goroutine فعلی را به اندازه d نگه داشته و سپس به کار خود ادامه می دهد. d باید مقداری از ثابت های بسته time باشد.
time.Sleep(100 * time.Millisecond)
تابع ParseDuration
این تابع با امضای ParseDuration(s string) (Duration, error) رشته ای را دریافت کرده و معادل آن را در قالب بسته time برمی گرداند.
hours, _ := time.ParseDuration("10h")
micro, _ := time.ParseDuration("1µs")
تابع Date
این تابع با امضای Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time مقادیر سال، ماه، روز و ... را دریافت کرده و زمان متناظر با آن را برمی گرداند.
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
تابع Now
این تابع با امضای Now() Time زمان محلی کنونی را برمی گرداند.
تابع Parse
این تابع با امضای Parse(layout, value string) (Time, error) یک رشته فرمت شده را به زمان تجزیه کرده و نتیجه را برمی گرداند.
const longForm = "Jan 2, 2006 at 3:04pm (MST)"
t, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)")
تابع Unix
این تابع با امضای Unix(sec int64, nsec int64) Time زمان محلی کنونی را در فرمت یونیکس برمی گرداند.
بسته json
این بسته انکد و دیکد کردن JSON طبق تعریف RFC 7159 را پیاده سازی کرده است. در ادامه دو تابع پرکاربرد این بسته که برای دیکد و انکد کردن JSON مورد استفاده قرار می گیرند را معرفی می کنیم.
تابع Marshal
این تابع با امضای Marshal(v interface{}) ([]byte, error) ورودی را به لیستی از بایت ها در فرمت JSON تبدیل می کند.
func main() { type ColorGroup struct {
ID int
Name string
Colors []string
}
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
b, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
}
تابع Unmarshal
این تابع با امضای Unmarshal(data []byte, v interface{}) error عکس عمل Marshal را یعنی تبدیل لیستی از بایت های در قالب JSON به متغیری از جنس زبان Go را انجام می دهد.
func main() {
var jsonBlob = []byte(`[
{"Name": "Platypus", "Order": "Monotremata"},
{"Name": "Quoll", "Order": "Dasyuromorphia"}
]`)
type Animal struct {
Name string
Order string
}
var animals []Animal
err := json.Unmarshal(jsonBlob, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
}
بسته sha512
این بسته الگوریتم های درهم سازی SHA-384, SHA-512, SHA-512/224, و SHA-512/256 را که در FIPS 180-4 تعریف شده اند را پیاده سازی کرده است. ثابت های تعریف شده در این بسته اندازه checksum خروجی در بایت هستند.
Size = 64
Size224 = 28
Size256 = 32
Size384 = 48
BlockSize = 128
در ادامه به معرفی برخی توابع این بسته می پردازیم.
تابع New
این تابع با امضای New() hash.Hash مقدار SHA-512 checksum جدیدی را برمی گرداند.
تابع Sum512
این تابع با امضای Sum512(data []byte) [Size]byte مقدار SHA512 checksum مقدار data را برمی گرداند.
بسته zlib
این بسته امکان خواندن و نوشتن در فرمت zlib که در RFC 1950 تعریف شده است را پیاده سازی کرده است. در ادامه دو تابع که برای خواندن و نوشتن مورد استفاده قرار می گیرند را معرفی می کنیم.
تابع NewReader
این تابع با امضای NewReader(r io.Reader) (io.ReadCloser, error) مقداری را از r خوانده و از حالت فشرده خارج می کند.
buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207, 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
b := bytes.NewReader(buff)
r, err := zlib.NewReader(b)
if err != nil {
panic(err)
}
io.Copy(os.Stdout, r)
r.Close()
تابع NewWriter
این تابع با امضای NewWriter(w io.Writer) *Writer نویسنده ای برای فشرده سازی بافر مهیا می کند. بستن این نویسنده به عهده فراخواننده آن خواهد بود.
var b bytes.Buffer
w := zlib.NewWriter(&b)
w.Write([]byte("hello, world\n"))
w.Close()
fmt.Println(b.Bytes())
نتیجه گیری
توابع و ثابت های بسیار زیادی در قالب کتابخانه های استاندارد Go پیاده سازی شده اند. مرور و مطالعه این کتابخانه ها به شما این امکان را می دهد که با نحوه پیاده سازی و ساختار زبان آشنا شده و چرخ های موجود را دوباره اختراع نکنید (!). استفاده از کتابخانه های استاندارد علاوه بر خوانا کردن کد به شما امکان پشتیبانی از نسخه های پیشین را نیز می دهند.
مطلبی دیگر از این انتشارات
کتاب فارسی Go Succinctly
مطلبی دیگر از این انتشارات
ساخت پروژه با معماری مایکروسرویس، زبان گولنگ، اندپوینت رست، کوبرنتیز و... (قسمت سوم)
مطلبی دیگر از این انتشارات
الگوهای طراحی در زبان گو (Design patterns in go)-مقدمه