С
Моє домашнє завдання - взяти рядок і розділити його на частини на кожному новому рядку. Я поняття не маю, що робити! Будь ласка, допоможіть!
Хитра проблема для початкового класу програмування на C! Спочатку ви повинні зрозуміти кілька основ цього складного предмета.
Рядок - це послідовність, що складається лише з символів . Це означає, що для того, щоб програмісти вказали на "невидиму" річ (це не пробіл, який вважається символом), ви повинні якось використовувати спеціальну послідовність символів, щоб означати цю невидиму річ.
У Windows новий рядок - це послідовність двох символів у рядку: зворотна косої риски та n (або рядок "\n"
)
У Linux або OS / X Macs - це послідовність з чотирьох символів: зворотна косої риски, n, зворотна косої риси, а потім r: (або "\n\r"
).
(Цікава історична примітка: на старих Macintoshes це була інша послідовність з чотирьох символів: "\ r \ n" ... повністю назад від того, як Unix робив справи! Історія веде дивні дороги.)
Може здатися, що Linux є більш марнотратним, ніж Windows, але насправді краща ідея використовувати довшу послідовність. Оскільки Windows використовує таку коротку послідовність, мова виконання C не може роздрукувати фактичні літери \n
без використання спеціальних системних викликів. Зазвичай ви можете це робити в Linux без системного дзвінка (він може навіть друкувати \n\
або \n\q
... все, окрім \n\r
). Але оскільки C має бути крос-платформою, він застосовує найнижчий спільний знаменник. Тож ви завжди будете бачити \n
у своїй книзі.
(Примітка. Якщо вам цікаво, про що ми говоримо, \n
не отримуючи нові рядки кожного разу, StackOverflow майже повністю пишеться в HTML ... звертаючись до речей, про які ви могли чути, наприклад, CLANG та LLVM.)
Але повернемося до того, над чим ми працюємо. Давайте уявимо рядок з трьох творів та двох нових рядків, наприклад:
"foo\nbaz\nbar"
Ви можете бачити, що довжина цього рядка дорівнює 3 + 2 + 3 + 2 + 3 = 13. Тому для нього потрібно зробити буфер довжиною 13, а програмісти C завжди додають його до розміру своїх масивів, щоб бути безпечним. Тому зробіть свій буфер і скопіюйте рядок у нього:
/* REMEMBER: always add one to your array sizes in C, for safety! */
char buffer[14];
strcpy(buffer, "foo\nbaz\nbar");
Тепер, що вам потрібно зробити, це шукати той двосимвольний візерунок, який представляє новий рядок. Вам заборонено шукати лише зворотний кут нахилу. Оскільки C використовується для розбиття рядків досить багато, це призведе до помилки, якщо ви спробуєте. Це можна побачити, якщо спробувати написати:
char pattern[2];
strcpy(pattern, "\");
(Примітка. У компіляторі є налаштування, якщо ви пишете програму, яка просто шукає косої риски. Але це вкрай рідко; косоокі риси дуже рідко використовуються, тому вони були обрані для цієї мети. Ми не будемо цього повертати ввімкнути.)
Тож давайте зробимо шаблон, який ми дійсно хочемо, ось такий:
char pattern[3];
strcpy(pattern, "\n");
Коли ми хочемо порівняти два рядки, які мають певну довжину, ми використовуємо strncmp
. Він порівнює певну кількість символів потенційно більшого рядка та повідомляє, чи відповідають вони чи ні. Так strncmp("\nA", "\nB", 2)
повертається 1 (вірно). Це навіть незважаючи на те, що рядки не є цілком рівними довжиною трьох ... а тому, що потрібно лише два символи.
Тож давайте переглянемо наш буфер, по одному символу, шукаючи, щоб два символи відповідали нашому шаблону. Кожен раз, коли ми знаходимо двосимвольну послідовність зворотної косої риски, за якою йде n, ми будемо використовувати особливий системний виклик (або "syscall"), putc
щоб вивести спеціальний тип символу: ASCII код 10 , щоб отримати фізичний новий рядок .
#include "stdio.h"
#include "string.h"
char buffer[14]; /* actual length 13 */
char pattern[3]; /* actual length 2 */
int i = 0;
int main(int argc, char* argv[]) {
strcpy(buffer, "foo\nbar\nbaz");
strcpy(pattern, "\n");
while (i < strlen(buffer)) {
if (1 == strncmp(buffer + i, pattern, 2)) {
/* We matched a backslash char followed by n */
/* Use syscall for output ASCII 10 */
putc(10, stdout);
/* bump index by 2 to skip both backslash and n */
i += 2;
} else {
/* This position didn't match the pattern for a newline */
/* Print character with printf */
printf("%c", buffer[i]);
/* bump index by 1 to go to next matchable position */
i += 1;
}
}
/* final newline and return 1 for success! */
putc(10, stdout);
return 1;
}
Вихід цієї програми - бажаний результат ... струна розділена!
foo
baz
bar
\t
призначений для \ тролінгу ...
Абсолютно неправильно зверху вниз. І все-таки наповнений правдоподібною дурницею, яка змалкувала інформацію, як, наприклад, у підручнику чи Вікіпедії. Логіка програми виглядає прозорою в контексті дезінформації, але є повністю оманливою. Навіть глобальні змінні та повернення коду помилки, на добру міру ...
...
Зрозуміло, у представленні рядків С дворядного послідовності джерела є лише один символ \n
. Але збільшити розмір буфера - це нешкідливо, якщо strlen()
він використовується для отримання фактичної довжини.
...
Ми намагаємось переконати читача, що strncmp
це булева операція, яка або відповідає (1), або не відповідає (0). Але він фактично має три значення повернення (-1 відповідність менше, 0 для рівних, 1 для відповідності більше) . Наші два символьні "візерунки", що порівнюються, це не [ \
, n
], а скоріше [ \n
, \0
] ... підбираючи неявний нульовий термінатор. Оскільки ця послідовність ковзає по рядку, вона ніколи не буде більшою, ніж двосимвольна послідовність, порівняно з ... в кращому випадку вона буде нульовою, якщо у вхідному рядку буде завершується новий рядок.
...
Отже, все це - провести цикл через рядок і друкувати його один за одним. Верхня гілка ніколи не працює. (Хоча ви могли б отримати це, якби у вашій рядку були \n
коди нижче, ніж скажімо, вкладка ..., яка може бути використана для загадкового опускання символів з виводу :-P)