Рядок (програмування): відмінності між версіями

[перевірена версія][неперевірена версія]
Вилучено вміст Додано вміст
Rudnytskyi (обговорення | внесок)
Виправлення неоднозначності
Немає опису редагування
Рядок 1:
{{Otheruses|Рядок (значення)}}
'''Рядок''' або '''строковий'''(англ. String «нитка, низка») '''тип данних''' — це тип даних, значеннями якого є довільна послідовність (рядок) символів алфавіту. Кожна змінна такого типу (строкова змінна) може бути представлена фіксованою кількістю байтів або мати довільну довжину.
'''Рядок''' — скінченна [[послідовність]] [[символ]]ів з [[алфавіт (інформатика)|алфавіту]].
''Довжина рядка'' ''s'' дорівнює кількості символів у ''s'' і звичайно позначається <math>|s|</math>. Порожній рядок є особливим рядком нульової довжини і позначається як <math>\epsilon</math>
 
== Внутрішнє представлення рядка в пам'яті ==
Деякі мови програмування накладають обмеження на максимальну довжину рядка, але в більшості мов подібні обмеження відсутні. При використанні Unicode кожен символ строкового типу може вимагати двох або навіть чотирьох байтів для свого представлення.
[[символьний тип даних|Символи]], що входять до рядка, як правило, зберігаються в вигляді [[Масив (структура даних)|масиву]]. При цьому, або довжина рядка вказується в окремому числовому елементі, або рядок обмежується символом кінця рядка (здебільшого він має код 0).
 
Основні проблеми в машинному поданні строкового типу:
== Виділення фрагментів рядка ==
* рядки можуть мати досить істотний розмір (до декількох десятків мегабайтів);
* змінюється з часом розмір - виникають труднощі з додаванням і видаленням символів.
У поданні рядків в пам'яті комп'ютера існує два принципово різних підходи.
 
=== Подання масивом символів ===
Для частин рядка вживаються такі терміни:
У цьому підході рядки представляються масивом символів; при цьому розмір масиву зберігається в окремій (службової) області. Від назви мови Pascal, де цей метод був вперше реалізований, даний метод отримав назву Pascal strings.
 
Злегка оптимізованим варіантом цього методу є т. Н. формат c-addr u (від англ. character-aligned address + unsigned number), застосовуваний в Форте. На відміну від Pascal strings, тут розмір масиву зберігається не спільно із строковими даними, а є частиною покажчика на рядок.
* ''Префікс'' рядка ''s'' (prefix)&nbsp;— рядок, одержаний вилученням нуля чи декількох ''останніх'' символів рядка ''s''
 
* ''Суфікс'' рядка ''s'' (suffix)&nbsp;— рядок, одержаний вилученням нуля чи декількох ''перших'' символів рядка ''s''
==== Переваги ====
* ''[[Підрядок]]'' рядка ''s'' (substring)&nbsp;— рядок, одержаний вилученням префікса і суфікса рядка ''s''
* програма в кожен момент часу містить відомості про розмір рядка, тому операції додавання символів в кінець, копіювання рядка і власне отримання розміру рядка виконуються досить швидко;
* ''Правильні'' префікс, суфікс і підрядок рядка ''s'' (proper …)&nbsp;— непорожній рядок, який є відповідно префіксом, суфіксом, підрядком рядка ''s'' і не дорівнює рядку ''s''.
* рядок може містити будь-які дані;
* можливо на програмному рівні стежити за виходом за межі рядка при її обробці;
* можливо швидке виконання операції виду «взяття N-ого символу з кінця рядка».
 
==== Недоліки ====
* проблеми зі зберіганням і обробкою символів довільної довжини;
* збільшення витрат на зберігання рядків - значення «довжина рядка» також займає місце і в разі великої кількості рядків маленького розміру може істотно збільшити вимоги алгоритму до оперативної пам'яті;
* обмеження максимального розміру рядка. У сучасних мовах програмування це обмеження скоріше теоретичне, так як зазвичай розмір рядка зберігається в 32-бітовому полі, що дає максимальний розмір рядка в 4 294 967 295 байт (4 гігабайти);
 
* при використанні алфавіту зі змінним розміром символу (наприклад, UTF-8), в розмірі зберігається не кількість символів, а саме розмір рядка в байтах, тому кількість символів необхідно вважати окремо.
 
=== Метод «завершального байту» ===
Другий метод полягає в використанні «завершального байту». Одне з можливих значень символів алфавіту (як правило, це символ з кодом 0) вибирається як ознака кінця рядка, і рядок зберігається як послідовність байтів від початку до кінця. Є системи, в яких в якості ознаки кінця рядка використовується не символ 0, а байт 0xFF (255) або код символу «$».
 
Метод має три назви - ASCIIZ (або asciz, символи в кодуванні ASCII з нульовим завершальним байтом), C-strings (найбільшого поширення метод отримав саме в мові Сі).
 
==== Переваги ====
* відсутність додаткової службової інформації про рядку (крім завершального байта);
* можливість подання рядки без створення окремого типу даних;
* відсутність обмеження на максимальний розмір рядка;
* економне використання пам'яті;
* простота отримання суфікса рядки;
* простота передачі рядків у функції (передається покажчик на перший символ);
 
==== Недоліки ====
* довгий виконання операцій отримання довжини і конкатенації рядків;
* відсутність засобів контролю за виходом за межі рядка, в разі пошкодження завершального байта можливість пошкодження великих областей пам'яті, що може привести до непередбачуваних наслідків - втрати даних, краху програми і навіть всієї системи;
* неможливість використовувати символ завершального байта в якості елемента рядка.
* неможливість використовувати деякі кодування з розміром символу в кілька байт (наприклад, UTF-16), тому що у багатьох таких символах, наприклад Ā (0x0100), один з байтів дорівнює нулю (в той же час, кодування UTF-8 вільна від цього недоліку).
 
=== Використання обох методів ===
У таких мовах, як, наприклад, Оберон, рядок розміщується в масиві символів певної довжини, причому її кінець позначається нульовим символом. За замовчуванням, весь масив заповнений нульовими символами. Такий спосіб дозволяє об'єднати багато переваг обох підходів, а також уникнути більшість їх недоліків.
 
=== Подання у вигляді списку ===
Мови Erlang, Haskell, використовують для строкового типу список символів. Цей метод робить мову більш «теоретично елегантним» за рахунок дотримання ортогональности в системі типів, але приносить суттєві втрати швидкодії.
 
== Реалізація в мовах програмування ==
* У перших мовах програмування взагалі не було строкового типу; програміст повинен був сам будувати функції для роботи з рядками того чи іншого типу.
 
* У Сі використовуються нуль-терминировать рядки з повним ручним контролем з боку програміста.
 
* У стандартному Паскалі рядок виглядає як масив з 256 байтів; перший байт зберігав довжину рядка, в інших зберігається її тіло. Таким чином, довжина рядка не може перевищувати 255 символів. У Borland Pascal 7.0 також з'явилися рядки «а-ля Сі» - очевидно, через те, що в число підтримуваних платформ увійшла Windows.
 
* У Object Pascal і C ++ STL рядок є «чорним ящиком», в якому виділення / вивільнення пам'яті відбувається автоматично - без участі програміста. При створенні рядка пам'ять виділяється автоматично; як тільки на рядок не залишиться жодного посилання, пам'ять повертається системі. Перевага цього методу в тому, що програміст не замислюється над роботою рядків. З іншого боку, програміст має недостатній контроль над роботою програми в критичних до швидкості ділянках; також важко реалізується передача таких рядків як параметр в DLL. Також Object Pascal автоматично стежить, щоб в кінці рядка був символ з кодом 0. Тому якщо функція вимагає на вході нуль-терминировать рядок, для конвертації треба просто написати PAnsiChar (строковая_переменная) або PWideChar (строковая_переменная) (для Pascal), переменная.c_str ( ) (для Builder / STL).
 
* У C # та іншими мовами із збіркою сміття рядок є незмінним об'єктом; якщо рядок потрібно модифікувати, створюється інший об'єкт. Цей метод повільний і витрачає чимало тимчасової пам'яті, але добре поєднується з концепцією збірки сміття. Перевага цього методу в тому, що присвоювання відбувається швидко і без дублювання рядків. Також є деякий ручний контроль над конструюванням рядків (в Java, наприклад, через класи StringBuffer і StringBuilder) - це дозволяє зменшити кількість виділень і вивільнень пам'яті і, відповідно, збільшити швидкість.
 
* У деяких мовах (наприклад, Standard ML) крім цього, є додатковий модуль для забезпечення ще більшої ефективності - «подстрока» (SubString). Його використання дозволяє виконувати операції над рядками без копіювання їхніх тіл за допомогою маніпулювання індексами початку і кінця підстроками; фізичне копіювання відбувається лише при необхідності перетворення подстрок в рядки.
 
== Операції ==
 
=== Найпростіші операції з рядками ===
* Отримання символу за номером позиції (індексу) - в більшості мов це тривіальна операція;
* Конкатенація (з'єднання) рядків.
 
=== Похідні операції ===
* Отримання підрядка за індексами початку і кінця;
* Перевірка входження одного рядка в іншу (пошук підрядка в рядку);
* Перевірка на збіг рядків (з урахуванням або без урахування регістру символів);
* Отримання довжини рядка;
* Заміна підрядка в рядку.
 
=== Операції при трактуванні рядків як списків ===
* Згортка;
* Відображення одного списку на інший;
 
* Фільтрація списку за критерієм.
 
=== Більш складні операції ===
* Знаходження мінімальної надстрокі, що містить всі зазначені рядки;
* Пошук в двох масивах рядків збігаються послідовностей (завдання про плагіат).
 
=== Можливі завдання для рядків на природній мові ===
* Порівняння на близькість зазначених рядків по заданому критерію;
 
* Визначення мови і кодування тексту на підставі ймовірностей символів і складів.
 
== Подання символів рядка ==
До останнього часу один символ завжди кодувався одним байтом (8 двійкових бітів; застосовувалися також кодування з 7 битами на символ), що дозволяло представляти 256 (128 при семібітной кодуванні) можливих значень. Однак для повноцінного представлення символів алфавітів кількох мов (багатомовних документів, друкарських символів - кілька видів лапок, тире, кількох видів прогалин і для написання текстів на ієрогліфічних мовах - китайською, японською та корейською) 256 символів недостатньо. Для вирішення цієї проблеми існує кілька методів:
* Перемикання мови керуючими кодами. Метод не стандартизований і позбавляє текст самостійності (тобто послідовність символів без керуючого коду на початку втрачає сенс); використовувався в деяких ранніх русифікації ZX-Spectrum і БК.
 
* Використання двох або більше байт для представлення кожного символу (UTF-16, UTF-32). Головним недоліком цього методу є втрата сумісності з попередніми бібліотеками для роботи з текстом при поданні рядка як ASCIIZ. Наприклад, кінцем рядка повинен вважатися вже не байт із значенням 0, а два або чотири поспіль нульових байта, в той час як одиночний байт «0» може зустрічатися в середині рядка, що збиває бібліотеку «з пантелику».
 
* Використання кодування зі змінним розміром символу. Наприклад, в UTF-8 частина символів представляється одним байтом, частина двома, трьома або чотирма. Цей метод дозволяє зберегти часткову сумісність зі старими бібліотеками (немає символів 0 всередині рядка і тому 0 можна використовувати як ознака кінця рядка), але призводить до неможливості прямої адресації символу в пам'яті за номером його позиції в рядку.
 
== Див. також ==
Рядок 19 ⟶ 106:
* [[Регулярний вислів]]
* [[Список структур даних]]
* [[Частота кадрів]]
* [[Чорний список]]
 
{{Compu-stub}}