Menu
flag Ar iconالعربيةdown icon

rune وbyte في Golang: شرح نصوص UTF-8

ما يعنيه rune وbyte في Go، وكيف تخزّن النصوص UTF-8، ولماذا تعدّ len البايتات، وكيف تفكّ range ترميز الـ runes، والتحويل بين string و[]byte و[]rune، والعمل بالحزمة bytes.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

النص في Go سلسلة من البايتات، وملفات Go المصدرية وحرفيات النصوص بترميز UTF-8. الـ byte واحد من تلك البايتات. والـ rune حرف Unicode واحد (نقطة رمز)، يخزّنه UTF-8 في بايت إلى 4 بايتات. معظم الالتباس حول النصوص في Go يأتي من الخلط بين الاثنين:

يشغل ثلاثة بايتات في UTF-8، فالنص "語Go" خمسة بايتات لكنه 3 runes. ترى حلقة الفهرس خمسة بايتات، والبايتات 0 و1 و2 بلا معنى منفردة. أما حلقة range فتفكّ ترميز UTF-8 وتعطي ثلاثة runes عند الفهارس 0 و3 و4: i دائمًا إزاحة بايت، لا عدد أحرف.

byte وrune اسمان بديلان

الاسمالنوع نفسه مثلالحرفيةالمعنى
byteuint8byte('A')، 0x41بايت واحد من البيانات
runeint32'A'، 'é'، '世'نقطة رمز Unicode واحدة

لأنهما اسمان بديلان، فالـ byte هو uint8 والـ rune هو int32؛ لا حاجة لتحويل بين الاسم البديل ونوعه الأساسي. علامتا الاقتباس المفردتان تصنعان rune، والمزدوجتان نصًا. 'ab' خطأ ترجمة، لأن حرفية الـ rune تحمل حرفًا واحدًا بالضبط.

الـ runes أرقام، فيمكنك إجراء عمليات حسابية عليها:

'7' - '0' الطريقة الكلاسيكية لتحويل حرف رقم إلى قيمته. تصنّف الحزمة unicode الـ runes (IsLetter وIsDigit وIsSpace وIsUpper) وتغيّر حالة الأحرف بشكل صحيح للأحرف غير ASCII. يطبع %c الـ rune كحرف، و%U بالشكل U+4E16، و%q كحرفية rune بين علامتي اقتباس.

كيف يخزّن UTF-8 الأحرف

نقاط الرمزالبايتاتأمثلة
من U+0000 إلى U+007F1ASCII: a، 7، {
من U+0080 إلى U+07FF2é، ß، ж، ع
من U+0800 إلى U+FFFF3، ،
U+10000 وما فوق4الرموز التعبيرية، وأحرف CJK النادرة

نص ASCII متطابق في UTF-8، ولهذا تعمل الشيفرة المبنية على البايتات غالبًا في الاختبار وتنكسر مع أول اسم فيه حرف بعلامة. تعمل الحزمة unicode/utf8 على الترميز مباشرة:

يطبع % x (مع مسافة) البايتات بالنظام الست عشري مفصولة بمسافات. وتبلغ utf8.ValidString هل النص UTF-8 صالح. يمكن لنصوص Go حمل أي بايتات، لا UTF-8 الصالح وحده؛ وعندما تصادف range بايتًا غير صالح تعطي utf8.RuneError (U+FFFD، حرف الاستبدال) وتتقدّم بايتًا واحدًا.

التحويل بين string و[]byte و[]rune

التحويلالنتيجةالكلفة
[]byte(s)بايتات UTF-8نسخ
[]rune(s)نقاط الرمز المفكوكةفكّ ترميز ونسخ (4 بايتات لكل rune)
string(b)نص من بايتاتنسخ
string(r) حيث r من النوع []runeترميز إلى UTF-8نسخ
string(r) حيث r من النوع runeنص من حرف واحدصغيرة

العكس بالبايتات سيخلط 世界 إلى UTF-8 غير صالح، ولهذا تعمل reverse على الـ runes. وحتى الـ runes ليست القصة كاملة: يمكن كتابة حرف مثل é أيضًا كـ e مع علامة مركّبة، أي runes اثنين. للأحرف المرئية للمستخدم (grapheme clusters) استخدم مكتبة مثل github.com/rivo/uniseg.

كل تحويل ينسخ، لأن النصوص غير قابلة للتعديل والشرائح قابلة. في الشيفرة الساخنة تجنّب التحويل ذهابًا وإيابًا؛ يحسّن المترجم بعض الحالات (مثل string(b) المستخدمة كمفتاح خريطة أو في مقارنة) فيتخطّى النسخ.

الفهرسة تعطي بايتًا

تعيد s[i] قيمة byte، وتقتطع s[i:j] بإزاحات البايتات:

s := "café"
fmt.Println(s[3])            // 195: the first byte of é
fmt.Println(string(s[3]))    // "Ã": that byte read as a code point
fmt.Println(s[:3])           // "caf"
fmt.Println(s[:4])           // "caf\xc3": half of é, invalid UTF-8

string(s[3]) فخ: تحويل بايت واحد إلى string يعامله كنقطة الرمز 195، أي Ã، لا البايت نفسه. لإيجاد حدود الأحرف استخدم range، أو عائلة strings.Index (التي تعيد إزاحات بايتات تقع دائمًا على الحدود)، أو utf8.DecodeRuneInString.

الحزمة bytes

تحاكي bytes الحزمة strings للنوع []byte: bytes.Contains وbytes.Split وbytes.Fields وbytes.TrimSpace وbytes.Equal وbytes.ToUpper وغيرها. استخدمها عندما تصل البيانات كبايتات (محتوى الملفات، أجسام HTTP، مخازن الشبكة)، كي لا تحوّل إلى نص ثم تعود:

bytes.Buffer مخزن بايتات قابل للنمو يطبّق io.Reader وio.Writer. وهو الأداة الصحيحة عندما تكتب البايتات وتقرؤها معًا؛ أما لبناء نص فإن strings.Builder أرخص قليلًا. قارن شرائح البايتات بـ bytes.Equal، لأن == لا تعمل مع الشرائح.

الاختيار بينها

  • النص الذي تعرضه أو تقارنه أو تبحث فيه: أبقه string، واستخدم range عندما تمشي عليه حرفًا حرفًا.
  • مواضع الأحرف، والعكس، والاقتطاع إلى n حرفًا: حوّل إلى []rune.
  • الإدخال والإخراج، والتجزئة، والترميز، والبروتوكولات الثنائية: استخدم []byte والحزمة bytes.
  • حرف واحد: rune. بايت خام واحد: byte.

الأسئلة الشائعة

ما هو الـ rune في Go؟

rune اسم بديل لـ int32 ويمثّل نقطة رمز Unicode واحدة. تُكتب حرفية الـ rune بين علامتي اقتباس مفردتين: 'a' تساوي 97، و'é' تساوي 233، و'世' تساوي 19990. المرور على نص بـ for i, r := range s يعطيك runes مفكوكة من بايتات UTF-8 للنص.

ما الفرق بين الـ rune والـ byte في Go؟

الـ byte (اسم بديل لـ uint8) ثمانية بتات من البيانات الخام. والـ rune (اسم بديل لـ int32) حرف Unicode كامل. في نص UTF-8 يشغل كل حرف ASCII بايتًا واحدًا، بينما تشغل الأحرف الأخرى من بايتين إلى أربعة لكنها تبقى rune واحدًا. تعدّ len(s) البايتات؛ وتعدّ utf8.RuneCountInString(s) الـ runes.

كيف أمرّ على أحرف نص في Go؟

استخدم for i, r := range s. كل تكرار يعطي إزاحة البايت i والـ rune r، فتُعالج الأحرف متعدّدة البايتات بشكل صحيح. أما حلقة الفهرس العادية for i := 0; i < len(s); i++ فتزور البايتات، فتقسم الأحرف غير ASCII إلى قطع.

كيف أحوّل نصًا إلى شريحة بايتات في Go؟

تنسخ b := []byte(s) بايتات النص إلى شريحة جديدة. وتحوّل string(b) بالعكس، بنسخة أيضًا. استخدم []rune(s) بدلًا من ذلك عندما تحتاج العمل بالأحرف، مثلًا لعكس نص أو أخذ أول n حرفًا منه.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن