programowanieProgramista Go

Jakie cechy pracy z ciągami (string) w Go należy znać, aby uniknąć nieprzewidzianych błędów podczas programowania?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania: W Go ciągi (string) to podstawowy i często używany typ, aktywnie stosowany do wymiany danych, logowania, parsowania itp. Różnicą w Go jest to, że ciągi są niemutowalne, składają się z niezmiennej sekwencji bajtów i mogą zawierać dane w UTF-8.

Problem: Mieszanie pracy z ciągami i fragmentami bajtów ([]byte) prowadzi do błędów podczas modyfikacji stringa, przy rozdzielaniu według run i przy próbach pracy z wielobajtowymi symbolami (na przykład cyrylica, emoji).

Rozwiązanie:

Ciąg jest niemutowalny, nie można zmieniać jego elementów bezpośrednio — próba zmiany s[0] jest niepoprawna. Ciąg kodowany jest w UTF-8, co oznacza, że jeden symbol (runa) może być szerszy niż jeden bajt. Praca z []byte jest tańsza, ale wymaga ręcznej kontroli. Konwersja string <-> []byte zawsze tworzy kopię.

Przykład kodu:

s := "cześć" fmt.Println(len(s)) // 12 bajtów (cyrylica: po 2 bajty) fmt.Println(len([]rune(s))) // 6 run, tyle liter fmt.Println(string([]byte{228, 189, 160, 229, 165, 189})) // chińskie znaki

Kluczowe cechy:

  • Ciąg jest niemutowalny, nie można zmieniać przez indeks.
  • Ciąg kodowany jest w UTF-8, nie zawsze 1 symbol = 1 bajt.
  • Konwersja string <-> []byte tworzy kopię.

Pytania z pułapkami.

1. Czy można zmienić pojedynczy znak ciągu przez indeks (na przykład, s[1] = 'a')?

Odpowiedź: Nie. Ciągi są niemutowalne, kompilator zgłosi błąd. Należy utworzyć nowy fragment []rune lub []byte, zmienić, a następnie przekształcić z powrotem w ciąg.

2. Dlaczego len(str) nie zawsze zgadza się z liczbą symboli w ciągu?

Odpowiedź: len(str) to liczba bajtów, a nie run (symboli). W przypadku cyrylicy lub emoji długi ciąg może dawać intuicyjnie nieoczekiwaną wartość. Aby uzyskać liczbę symboli, użyj []rune:

s := "świat 😀" fmt.Println(len(s)) // 7 fmt.Println(len([]rune(s))) // 5

3. Czy ciąg jest przekazywany przez referencję czy przez wartość do funkcji?

Odpowiedź: Przez wartość, ale fizycznie wewnątrz przechowywany jest wskaźnik do pamięci i długość. Po przekazaniu dwie zmienne "wskazują" na ten sam tekst, kopia nie jest tworzona automatycznie. Rzeczywista kopia pamięci powstaje podczas konwersji do []byte lub z []byte na string.

Typowe błędy i antywzorce

  • Próba bezpośredniej zmiany ciągu przez indeksację.
  • Porównywanie długości ciągu przez len — błędy z Unicode.
  • Użycie []byte do serializacji, ale zapominanie o kodowaniu.
  • Zapominanie, że string i []byte to różne obiekty w pamięci.

Przykład z życia

Negatywny przypadek

Programista ma ciąg z rosyjskimi znakami, bierze pierwsze 4 bajty i oczekuje uzyskać pierwszą literę, ale otrzymuje tylko połowę znaku — powstają "zepsute" znaki.

Zalety: Szybko i krótko. Wady: Niekorektna obsługa danych Unicode, "zepsute" ciągi, panika przy próbie sparsowania takich ciągów w innych miejscach.

Pozytywny przypadek

Ciągi są przekształcane w []rune do pracy z symbolami, po wykonaniu potrzebnych działań ciąg jest ponownie zbierany przez string(). Praca z []byte wykonywana jest tylko dla niskopoziomowej serializacji, z uwzględnieniem kodowania.

Zalety: Poprawna obsługa Unicode, niezawodność funkcji. Wady: Nieco wolniej, wymaga więcej pamięci, ale jest bezpieczne dla wszelkich ciągów.