الإدخال والإخراج في Go
package main
import "fmt"
func main() {
fmt.Println("Hello 世界!")
}أول مثال للدخول في هذا الموقع هو إخراج سلسلة نصية، في هذا القسم سنتحدث عن كيفية القيام بالإدخال والإخراج في Go.
واصفات الملفات
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)في حزمة os هناك ثلاثة واصفات ملفات مكشوفة، نوعها جميعاً *os.File، وهي:
os.Stdin- الإدخال القياسيos.Stdout- الإخراج القياسيos.Stderr- الخطأ القياسي
الإدخال والإخراج في Go يعتمدان عليهما.
الإخراج
في Go هناك طرق عديدة للإخراج، فيما يلي بعض الطرق الشائعة:
stdout
لأن الإخراج القياسي هو في حد ذاته ملف، يمكنك كتابة سلسلة نصية مباشرة إلى الإخراج القياسي:
package main
import "os"
func main() {
os.Stdout.WriteString("hello world!")
}print
لدى Go دالتان مدمجتان print و println، يُخرجان المعاملات إلى الخطأ القياسي، للاختبار فقط، ولا يُنصح باستخدامهما عموماً:
package main
func main() {
print("hello world!\n")
println("hello world")
}fmt
الاستخدام الأكثر شيوعاً هو استخدام حزمة fmt، التي توفر دالة fmt.Println، والتي تُخرج المعاملات إلى الإخراج القياسي افتراضياً:
package main
import "fmt"
func main() {
fmt.Println("hello world!")
}تقبل معاملاتها أي نوع، وإذا كان النوع يُنفذ واجهة String سيتم استدعاء طريقة String للحصول على شكلها النصي، لذا محتوى مُخرجاتها قابل للقراءة بشكل كبير، ومناسب لمعظم الحالات، لكن لأنها تستخدم الانعكاس داخلياً، لا يُنصح باستخدامها بكثرة في السيناريوهات الحساسة للأداء.
bufio
توفر bufio طرق إخراج ذات تخزين مؤقت، حيث تُكتب البيانات أولاً إلى الذاكرة، وعند الوصول لحد معين تُخرج إلى Writer المحدد، وحجم المخزن المؤقت الافتراضي هو 4KB. يُنصح باستخدام هذه الحزمة في عمليات إدخال/إخراج الملفات والشبكة:
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
writer.WriteString("hello world!")
}يمكنك أيضاً استخدامها مع حزمة fmt:
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
fmt.Fprintln(writer, "hello world!")
}التنسيق
وظيفة تنسيق الإخراج في Go يوفرها بشكل أساسي fmt.Printf، إذا تعلمت لغات من عائلة C، ستجدها مألوفة، فيما يلي مثال بسيط:
func main() {
fmt.Printf("hello world, %s!", "jack")
}فيما يلي جميع رموز التنسيق الحالية في Go:
| 0 | التنسيق | الوصف | الأنواع المقبولة |
|---|---|---|---|
| 1 | %% | إخراج علامة النسبة % | أي |
| 2 | %s | إخراج قيمة string/[] byte | string,[] byte |
| 3 | %q | تنسيق السلسلة، السلسلة المُخرجة لها علامتي اقتباس مزدوجتين "" | string,[] byte |
| 4 | %d | إخراج قيمة عدد صحيح بالنظام العشري | أعداد صحيحة |
| 5 | %f | إخراج عدد فاصلة عائمة | فاصلة عائمة |
| 6 | %e | إخراج بالصيغة العلمية، يمكن أيضاً للأعداد المركبة | فاصلة عائمة |
| 7 | %E | مماثل لـ %e | فاصلة عائمة |
| 8 | %g | يقرر وفقاً للحالة الفعلية إخراج %f أو %e، يزيل الأصفار الزائدة | فاصلة عائمة |
| 9 | %b | إخراج الشكل الثنائي لعدد صحيح | أرقام |
| 10 | %#b | إخراج الشكل الثنائي الكامل | أرقام |
| 11 | %o | إخراج الشكل الثماني لعدد صحيح | أعداد صحيحة |
| 12 | %#o | إخراج الشكل الثماني الكامل لعدد صحيح | أعداد صحيحة |
| 13 | %x | إخراج الشكل السداسي عشري الصغير لعدد صحيح | أرقام |
| 14 | %#x | إخراج الشكل السداسي عشري الصغير الكامل لعدد صحيح | أرقام |
| 15 | %X | إخراج الشكل السداسي عشري الكبير لعدد صحيح | أرقام |
| 16 | %#X | إخراج الشكل السداسي عشري الكبير الكامل لعدد صحيح | أرقام |
| 17 | %v | إخراج الشكل الأصلي للقيمة، يُستخدم كثيراً لإخراج هياكل البيانات | أي |
| 18 | %+v | عند إخراج هيكل سيتم إضافة أسماء الحقول | أي |
| 19 | %#v | إخراج القيمة بصيغة Go الكاملة | أي |
| 20 | %t | إخراج قيمة منطقية | منطقي |
| 21 | %T | إخراج نوع Go للقيمة | أي |
| 22 | %c | إخراج الحرف المقابل لرمز Unicode | int32 |
| 23 | %U | إخراج رمز Unicode المقابل للحرف | rune,byte |
| 24 | %p | إخراج العنوان الذي يشير إليه المؤشر | مؤشر |
استخدم fmt.Sprintf أو fmt.Printf لتنسيق السلسلة أو إخراج السلسلة المنسقة، انظر بعض الأمثلة:
fmt.Printf("%%%s\n", "hello world")
fmt.Printf("%s\n", "hello world")
fmt.Printf("%q\n", "hello world")
fmt.Printf("%d\n", 2<<7-1)
fmt.Printf("%f\n", 1e2)
fmt.Printf("%e\n", 1e2)
fmt.Printf("%E\n", 1e2)
fmt.Printf("%g\n", 1e2)
fmt.Printf("%b\n", 2<<7-1)
fmt.Printf("%#b\n", 2<<7-1)
fmt.Printf("%o\n", 2<<7-1)
fmt.Printf("%#o\n", 2<<7-1)
fmt.Printf("%x\n", 2<<7-1)
fmt.Printf("%#x\n", 2<<7-1)
fmt.Printf("%X\n", 2<<7-1)
fmt.Printf("%#X\n", 2<<7-1)
type person struct {
name string
age int
address string
}
fmt.Printf("%v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%+v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%#v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%t\n", true)
fmt.Printf("%T\n", person{})
fmt.Printf("%c%c\n", 20050, 20051)
fmt.Printf("%U\n", '码')
fmt.Printf("%p\n", &person{})عند استخدام أنظمة أخرى، إضافة مسافة بين % ورمز التنسيق يمكن أن تعطي تأثير الفاصل، مثلاً:
func main() {
str := "abcdefg"
fmt.Printf("%x\n", str)
fmt.Printf("% x\n", str)
}نتيجة هذا المثال:
61626364656667
61 62 63 64 65 66 67عند استخدام الأرقام، يمكن أيضاً إضافة أصفار تلقائياً. مثلاً:
fmt.Printf("%09d", 1)
// 000000001وكذلك للنظام الثنائي:
fmt.Printf("%09b", 1<<3)
// 000001000حالات الخطأ
عدد رموز التنسيق < عدد المعاملات
fmt.Printf("", "") //%!(EXTRA string=)عدد رموز التنسيق > عدد المعاملات
fmt.Printf("%s%s", "") //%!s(MISSING)عدم تطابق النوع
fmt.Printf("%s", 1) //%!s(int=1)عدم وجود رمز تنسيق
fmt.Printf("%", 1) // %!(NOVERB)%!(EXTRA int=1)الإدخال
فيما يلي طرق الإدخال الشائعة:
read
يمكنك قراءة محتوى الإدخال مثل قراءة ملف مباشرة، كالتالي:
func main() {
var buf [1024]byte
n, _ := os.Stdin.Read(buf[:])
os.Stdout.Write(buf[:n])
}استخدامها بهذه الطريقة مُعقد جداً، ولا يُنصح به عموماً.
fmt
يمكننا استخدام عدة دوال توفرها حزمة fmt، واستخدامها مشابه لـ C تقريباً:
// تفحص النص المقروء من os.Stdin، وتقسم حسب المسافات، والسطر الجديد يُعامل كمسافة
func Scan(a ...any) (n int, err error)
// مشابه لـ Scan، لكن يتوقف عند السطر الجديد
func Scanln(a ...any) (n int, err error)
// تفحص وفقاً لسلسلة منسقة
func Scanf(format string, a ...any) (n int, err error)قراءة رقمين:
func main() {
var a, b int
fmt.Scanln(&a, &b)
fmt.Printf("%d + %d = %d\n", a, b, a+b)
}قراءة مصفوفة بطول ثابت:
func main() {
n := 10
s := make([]int, n)
for i := range n {
fmt.Scan(&s[i])
}
fmt.Println(s)
}1 2 3 4 5 6 7 8 9 10
[1 2 3 4 5 6 7 8 9 10]bufio
عند وجود كمية كبيرة من الإدخال تحتاج للقراءة، يُنصح باستخدام bufio.Reader لقراءة المحتوى:
func main() {
reader := bufio.NewReader(os.Stdin)
var a, b int
fmt.Fscanln(reader, &a, &b)
fmt.Printf("%d + %d = %d\n", a, b, a+b)
}scanner
bufio.Scanner مشابه لـ bufio.Reader، لكنه يقرأ سطراً بسطر:
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if line == "exit" {
break
}
fmt.Println("scan", line)
}
}النتيجة:
first line
scan first line
second line
scan second line
third line
scan third line
exitTIP
فيما يتعلق بالإدخال والإخراج، إذا أردت التدريب، يمكنك الذهاب إلى 洛谷 وحل بعض مسائل الخوارزميات البسيطة بنمط ACM للتدرب.
