שפת C/מחרוזות
מחרוזת היא רצף של תווים. מחרוזות משמשות על פי רוב בתכנות ליצוג מילים ומשפטים.
מעט לגבי תווים
עריכהכבר ראינו שתו (char) הוא טיפוס בעל בית יחיד, המשמש לתווי א"ב או ערכים מספריים קטנים. אנו יכולים להחליט שלמספרים כלשהם יש משמעות מיוחדת מבחינתנו, אך את המחשב מעניינים רק הערכים המספריים של תו. במחשב כלשהו, לדוגמה, ערך התו 'a' יכול להיות 97. מבחינת מחשב זה, על כן, אין הבדל בין 'a' לבין 97.
בשפת C יש מוסכמה שעל פיה התו בעל הערך המספרי 0 הוא התו הריק. בפרק זה נראה שיש משמעות מיוחדת לתו הריק.
שימו לב: התו הריק 0 אינו תו הרווח ' '. |
הגדרת מחרוזת
עריכההמחרוזת כמערך תווים
עריכהמחרוזת ב-C מוגדרת על ידי מערך של משתנים מסוג char. לדוגמה:
char my_string[5];
היא הכרזה על מערך בשם my_string בעל מקום ל-5 תווים.
עם זאת, מחרוזת היא יותר מאשר סתם מערך תווים. שאר הנושא מדבר על כך.
חשיבות התו הריק
עריכהקיימת מוסכמה שמחרוזת היא יותר מאשר מערך של תווים. המוסכמה אומרת שתווי המחרוזת מתחלקים לשלושה חלקים:
- התווים עד לתו הריק הראשון
- התו הריק הראשון מסמן את סוף התווים "הנחשבים" במחרוזת
- מהתו הריק הראשון ועד סוף המערך, מתעלמים מתווי המחרוזת
(על פי המוסכמה, אם המערך אינו מכיל את התו הריק, אז המחרוזת אינה חוקית).
מיד בהמשך נראה דוגמה לכך.
אורך מחרוזת
עריכהלפי המוסכמה, התו הריק הראשון מסמן את סוף התווים "הנחשבים" במחרוזת. לכן, אורכה של מחרוזת נחשב מספר התווים מתחילת המערך ועד (לא כולל) התו הריק הראשון במערך.
נשים לב לכן, שלמחרוזת יש שתי תכונות גודל:
- למחרוזת, כמו לכל מערך אחר, יש גודל, והוא קובע את המספר המקסימלי של התווים שהמחרוזת יכולה להחזיק.
- למחרוזת יש גם אורך, שהוא מספר התווים עד לתו הריק הראשון, והוא קובע את אורך המחרוזת בפועל עבור תוכנה הנוכחי.
דוגמה
עריכהלהלן מערך של 14 תווים, המורכב מ-3 חלקים:
- תחילה מופיע הרצף "Shalom olam".
- לאחר מכן מופיע התו הריק.
- לבסוף, לאחר התו הריק מופיעים עוד תווים, שלפי המוסכמה אינם נחשבים לתוכן המחרוזת.
עכשיו תורכם: מה גודל המערך הנ"ל, ומה אורך המחרוזת? |
במערך יש 14 תאים, ולכן גודלו 14. במחרוזת יש 11 תווים עד התו הריק, ולכן אורכה 11.
אתחול מחרוזת
עריכההיות שמחרוזת היא מערך, ניתן לאתחל אותה כפי שמאתחלים כל מערך, או, לחלופין, אפשר לשים ערכים לאיבריה ממש כמו לכל מערך אחר. בשני המקרים יש לאתחל או לשים גם את התו הריק.
לדוגמה, אם נרצה לאתחל את המחרוזת my_string ל-"wiki", אפשר לאתחל אותה כך:
char my_string[5] = {'w', 'i', 'k', 'i', 0};
או, לחלופין, אפשר לשים ערכים לאיבריה כך:
my_string[0] = 'w';
my_string[1] = 'i';
my_string[2] = 'k';
my_string[3] = 'i';
my_string[4] = 0;
השפה גם מאפשרת צורת אתחול מיוחדת למחרוזות, הקריאה יותר משתי האפשרויות הקודמות:
char my_string[] = "wiki";
נשים לב שבשיטה האחרונה אין צורך לציין את גודל המחרוזת (הוא מחושב לבד על ידי המהדר, אם כי זו לא טעות לכתוב אותו), וכן לא כותבים מפורשות את התו 0 (המהדר לוקח אותו בחשבון).
נזכור, לכן, שבקטעי קוד מהצורה:
char my_string_1[10] = {'w', 'i', 'k', 'i', 0};
char my_string_2[] = "wiki";
תמיד יש לחשוב מהו גודל המערך ומה אורך המחרוזת.
עכשיו תורכם: מהם גודלי המערכים ואורכי המחרוזות בקטע הקוד? |
my_string_1 הוא מערך בגודל 10. לאחר האתחול כאן, הוא מכיל 5 תווים: 4 תווי תוכן, והתו הריק; לכן ארכו 4. my_string_2 הוא מערך בגודל 5: המהדר מחשב שיש צורך ב-4 תווי תוכן ובתו ריק, ומקצה גודל מתאים לכך. לאחר האתחול כאן, הוא מכיל 5 תווים: 4 תווי תוכן, והתו הריק; לכן ארכו 4.
פונקציות בסיסיות לטיפול במחרוזות
עריכהעבודה עם מחרוזות כוללת לרוב את אותן המטלות. ספריות המערכת כוללות פונקציות שימושיות למטלות נפוצות. כדי להשתמש בפונקציות אלו, יש להוסיף לתחילת הקובץ את השורה:
#include <string.h>
מציאת אורך מחרוזת
עריכההפונקציה strlen מחזירה את אורכה של מחרוזת נתונה. לדוגמה:
char str[15] = "Boker Tov!";
int length = strlen( str );
printf("%d\n", length);
ידפיס את המספר 10, שכן ישנם עשרה תווים עד לתו הריק הראשון.
העתקת מחרוזת
עריכההיות שמחרוזת היא מערך, אי אפשר להעתיק מחרוזת על ידי השמה פשוטה כמו במשתנים רגילים. קטע הקוד הבא, לדוגמה, אינו חוקי:
char source[] = "Shalom";
char dest[] = source; /* ERROR! */
כדי להעתיק מחרוזת, יש להעתיק תו אחר תו. הפונקציה strcpy עושה זאת. לדוגמה:
char source[] = "Shalom";
char dest[15];
strcpy( dest, source );
printf("%s\n%s\n", source, dest);
ידפיס
Shalom
Shalom
strcpy מקבלת שתי מחרוזות, ומעתיקה את השנייה לתוך הראשונה, כולל תו סיום המחרוזת.
שרשור מחרוזות
עריכההפונקציה strcat משרשרת מחרוזת אחת לסוף של מחרוזת אחרת:
char source[] = "olam";
char dest[30] = "Shalom ";
strcat( dest, source );
printf("%s\n%s\n", source, dest);
קוד זה ידפיס:
olam
Shalom olam
האיור הבא מראה את המחרוזות לפני ואחרי פעולת השרשור:
שים לב במיוחד לתווים הריקים.
פלט וקלט
עריכהפלט
עריכההפונקציה printf
עריכהבפלט וקלט הכרנו את הפונקציה printf המאפשרת להציג ערכי משתנים על המסך. אפשר להשתמש בפונקציה זו לפלט מחרוזות. לציון משתנה מסוג מחרוזת בפקודת קלט/פלט משתמשים ב-%s. לדוגמה:
char name[] = "Moshe";
printf("My name is %s\n", name);
קוד זה יציג על המסך את הפלט:
My name is Moshe
פונקציות אחרות
עריכההספריה הסטנדרטית כוללת פונקציות נוספות לפלט מחרוזות, לדוגמה puts.
קלט
עריכההפונקציה scanf
עריכהבפלט וקלט ראינו את הפונקציה scanf המאפשרת לקלוט ערכים למשתנים. אפשר להשתמש בפונקציה זו לקלט מחרוזות. לציון משתנה מסוג מחרוזת בפקודת קלט/פלט משתמשים ב-%s. לדוגמה:
char name[10];
printf("Please enter your name:\n");
scanf("%9s", name);
קוד זה יציג על המסך:
Please enter your name:
ויחכה לקלט מהמשתמש. עד 9 תווים ייקלטו למשתנה name, או, אם 9 התווים הראשונים כוללים רווח, רק התווים עד הרווח הראשון.
נשים לב שכאשר קולטים מחרוזת בעזרת scanf אין כותבים & לפני שם המשתנה. הסיבה לכך תתברר לאחר לימוד של נושא המצביעים.
כדאי לדעת: נשים לב לקובע הרוחב 9 ב"%9s". גם אם המשתמש יקליד יותר תווים, הקלט לא ייגלוש מתחום המחרוזת name. נדבר על כך עוד בהבעייתיות המיוחדת בפונקציות קלט. |
הפונקציה fgets
עריכההפונקציה scanf מאפשרת לקלוט מחרוזות עד הרווח הראשון. לפעמים רוצים לקלוט שורה שלמה, גם אם היא כוללת רווחים. לצורך כך אפשר להשתמש ב-fgets, כך:
char a[50];
fgets(a, 50, stdin);
הקריאה:
fgets(a, 50, stdin);
תקלוט עד 49 תווים. אם ב-49 התווים הראשונים יש מעבר לשורה חדשה (על ידי Enter), ייקלטו רק התווים עד שם.
כדאי לדעת: לעת עתה נוכל להתעלם מהארגמונט השלישי של הפונקציה, stdin, נעסוק בו בפלט וקלט קבצים. |
פונקציות אחרות
עריכההספריה הסטנדרטית כוללת פונקציות נוספות לקלט מחרוזות, לדוגמה gets. לפונקציה gets אין קובע רוחב. הדבר עלול להוביל לגלישת חוצץ, ולכן לפני השימוש בה, מומלץ לקרוא על הבעייתיות המיוחדת בפונקציות קלט.
זהירות בטיפול במחרוזות
עריכהגישה שגוייה לאיברי המחרוזת כמערך
עריכהכבר ראינו שבשפת C, אם מערך הוא בגודל 20, אז גישה לאיבר מעבר לכך, נניח 33, גולשת מתחום המערך, ויכולה לגרום לבעיות. חשוב להבין שקל מאד ליפול לבעיות אלו דווקא בעבודה עם מחרוזות. נתבונן, לדוגמה, בקטע הקוד הבא:
char src[] = "Hello, world!";
char dest[4];
strcpy(dest, src);
קטע קוד זה מכיל שגיאה, מפני שבמערך dest אין מספיק מקום להעתיק את תווי "Hello, world!".
המסקנה, על כן, היא שבעבודה עם מחרוזות, ובעיקר עם פונקציות המעתיקות דברים למחרוזות (לדוגמה, strcpy, strcat, או פונקציות הקלט שראינו) - יש לנהוג בזהירות רבה.
הבעייתיות המיוחדת בפונקציות קלט
עריכהעם תשומת לב מספיקה, אפשר להבטיח שמרבית הקריאות המטפלות במחרוזות יעבדו בצורה בטוחה. כך, לדוגמה, לפני כל קריאה ל-strcpy, אפשר לבדוק את אורכי המחרוזות, ולבצע את הפעולה רק אם אין סכנת גלישה. ישנה בעייה מיוחדת בקלט, מפני שאין דרך לדעת מראש כמה תווים יקליד המשתמש. קטע הקוד הבא, לדוגמה, משתמש ב-scanf ללא קובע רוחב:
char name[10];
printf("Please enter your name:\n");
scanf("%s", name);
חשוב להבין שזהו קטע קוד בעייתי ביותר. בכלל, כשעוסקים בקלט מחרוזות (אולי על ידי שימוש בפונקציות ספריה אחרות), חשוב לשים לב לנקודה.
הפרק הקודם: מערכים |
מחרוזות תרגילים |
הפרק הבא: מצביעים |