|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
קורס:"שפת C"
מתוך הספר: C - מדריך מקצועי
הגדרת מחרוזתבשפת C לא קיים טיפוס מחרוזת טבעי. מחרוזת מיוצגת ע"י מערך תווים המסתיים בתו מסיים מחרוזת, לדוגמא: char home[] = "www.mh2000.co.il"; /* home page of this book */ home היא מחרוזת בת 16 תווים + תו מסיים מחרוזת (בלתי נראה):
התו '\0' הוא תו סוף מחרוזת, שערך ה- ASCII שלו הוא 0 (אפס). גודל המערך חייב להיות כמספר התווים המרביים שהמחרוזת אמורה להכיל + 1. ערך קבוע (ליטרל) של מחרוזת מצוין ע"י גרשיים משני צידיה. דוגמאות: "hello" "1" "" יש לשים לב ש- "1" היא מחרוזת ואילו '1' הוא תו - מלבד העובדה ששני נתונים אלו מאוחסנים בזיכרון בגודל שונה (מדוע?) הם משתייכים לטיפוסים שונים. המחרוזת האחרונה, "", היא מחרוזת ריקה, כלומר היא מכילה תו יחיד - התו '\0' - תו סוף מחרוזת. כללים
1. char str[] = "hello"; 2. char str[6] = "hello"; כמו כן, ניתן להתייחס אליה כאל מצביע, לדוגמא: char *str = "hello";
char str[6]; str = "hello"; /* Error! */ כמו כן לא ניתן להציב מחרוזת אחת לשנייה, לדוגמא: char str1[6] = "hello"; char str2[6]; str2 = str1; /* Error! */ כפי שנראה בהמשך, קיימת פונקציה המטפלת בהעתקת מחרוזות.
if(str1 == "hello") /* Error */ ... הוא ביטוי לא חוקי. גם לכך קיימת פונקצית ספריה שנכיר בהמשך.
char str[6]; ... str[0] = 'h'; str[1] = 'e'; str[2] = 'l'; str[3] = 'l'; str[4] = 'o'; str[5] = '\0'; str[6] = 'x'; /* Runtime error: Array out-of-range ! */
1. str[5] = '\0'; 2. str[5] = 0;
char *p = NULL;
typedef char String[256]; כעת ניתן להגדיר משתני מחרוזת מסוג String בצורה נוחה:
String str1 = "hello";
החסרון בשיטה זו הוא בזבוז המקום בזיכרון במקרה של מחרוזות קטנות, אולם בתכניות רבות שיקול זה זניח ביחס לכמות הזכרון במחשב וביחס לנוחות שבהגדרה זו.
1) char * str = "first " "second " "third";/* str="first second third" */ 2) printf("hello " "world"); /* output: hello world */ קלט / פלט מחרוזותראינו שניתן לקרוא ולהדפיס מחרוזות ע"י הפונקציות printf ו- scanf באמצעות מציין הטיפוס %s. לדוגמא, בכדי להדפיס את המחרוזת שהגדרנו קודם char str1[6] = "hello"; נבצע printf("The string is %s", str1); יודפס: The string is hello כמו כן, ניתן להדפיס את המחרוזת כמחרוזת הבקרה: printf(str1); יודפס: hello באופן דומה, scanf מבצעת קריאה מהקלט של מחרוזת: char str1[6]; scanf("%s", str1); כפי שכבר ראינו, scanf - בשונה מ- printf - מקבלת ברשימת הפרמטרים מצביעים למשתנים. במקרה של מחרוזת, הואיל והיא בעצם מערך, שמה הוא כתובתה. scanf() ו- printf() מבצעות קלט/פלט מחרוזות מהקלט התקני לפלט התקני:
הפונקציות sscanf ו- sprintfעד עתה ביצענו פעולות קלט / פלט תקני - מקור הקלט היה המקלדת, ופלט התכנית היה המסך. בשפת C קיימת אפשרות להתייחס למחרוזות כאל מקורות קלט ויעדי פלט ע"י שימוש בפונקציות sscanf ו- sprintf. הפונקציה sscanf משמשת לקריאת קלט ממחרוזת עפ"י פורמט נתון. לדוגמא: typedef char String[256]; String str; int num1; float num2; gets(str); /* read the whole line as string*/ sscanf(str, "%d %f", &num1, &num2); /* read numbers from string */ printf("num1=%d, num2=%.2f", num1, num2); הסבר: פעולת הקריאה ממחרוזת מבוצעת ע"י sscanf עפ"י התחביר sscanf( <string> , <format>, parameter1, parameter2, ...); הקריאה מבוצעת בדומה לקריאת קלט ע"י scanf ולפי כללי מחרוזת הפורמט ורשימת הפרמטרים שהכרנו: sscanf(str, "%d %f", &num1, &num2);
בדומה, ניתן לכתוב משתנים עפ"י פורמט לתוך מחרוזת ע"י sprintf : String str; int num1 = 3; float num2 = 54.8; sprintf(str, "num1=%d, num2=%.2f", num1, num2); puts(str); יודפס: num1=3, num2=54.80
פעולות על מחרוזותכפי שכבר ראינו, מחרוזת בשפת C היא מערך, ולא טיפוס בסיסי. לכן אין אפשרות לבצע עליה פעולות כגון:
לצורך ביצוע הפעולות הנ"ל קיימות פונקציות בספרייה string.h. כדי להבין איך הפונקציות עובדות, ננסה תחילה לחקות את פעולתן. בכל הדוגמאות, נתייחס לטיפוס מחרוזת String המוגדר כמו קודם: typedef char String[256]; בעמודים 220-226 מובאות פונקציות חיקוי לפונקציות הספרייה:
עיין/י בספר בסעיף זה ובצע/י את התרגילים המצורפים. פונקציות הספרייה string.hהפונקציות שממשנו לעיל הן פונקציות תקניות הקיימות בספרייה string.h של שפת C. הספרייה כוללת פונקציות רבות לטיפול במחרוזות - השכיחות שבהן:
int strcmp(const char *s1, const char *s2);
int stricmp(const char *s1, const char *s2);
char * strcpy(char *s1, const char *s2);
size_t strlen(const char *s);
char * strcat(char *dest, const char *src); לפונקציות strcmp, strcpy ו- strcat יש פונקציות מקבילות הפועלות על מספר נתון של תווים: strncmp משווה בין 2 מחרוזות מספר נתון של תווים לכל היותר, strncpy מעתיקה מספר נתון של תווים לכל היותר ממחרוזת אחת לשנייה, ו- strncat משרשרת מספר נתון של תווים לכל היותר ממחרוזת אחת לשנייה. תכנית דוגמא: העתקה ושרשורהפונקציה strcat() מקבלת כפרמטרים שתי מחרוזות str1,str2 ומוסיפה את התווים של מחרוזת str2 לקצה המחרוזת str1 , ותו סיום המחרוזת עובר לקצה המחרוזת החדשה שנוצרה. לדוגמא, אם נתונות שתי המחרוזות: '\0' ' ' o l l e H str1: '\0' d l r o w str2: לאחר קריאה לפונקציה strcat(str1, str2) יהיה ערכה של str1: str1: H e l l o ' ' w o r l d '\0' הערות:
התכנית הבאה מבצעת העתקה ושרשור של מחרוזות ע"י פונקציות הספרייה strcpy ו- strcat : /* file: concat.c */ #include <string.h> #include <stdio.h> typedef char String[256]; void main( void ) { String str; strcpy( str, "Hello " ); strcat( str, "from " ); strcat( str, "www.MH2000.co.il !" ); /* home page of this book */ puts(str); } והפלט: Hello from www.MH2000.co.il !
פונקציות לפעולות חיפוש טקסטקיימות פונקציות ספרייה נוספות ב- string.h לביצוע פעולות חיפוש בטקסט:
char * strchr(const char *s, int c);
char * strrchr(const char *s, int c);
char * strstr(const char *s, const char *sub_str);
char * strtok(char *str, const char *delimit);
size_t strspn(const char *str, const char *delimit);
char * strpbrk(const char *str, const char *sub_str); תכנית דוגמא: ניתוח טקסט ע"י strtok()התכנית הבאה מנתחת ומפרקת שורת טקסט על פי סימני פיסוק תוך שימוש בפונקציה strtok המוכרזת כך: char *strtok( char *str, const char *delimit ); הפרמטר הראשון הוא מחרוזת והפרמטר השני הוא אוסף תווים (מחרוזת) המהווים סימני פיסוק במחרוזת הראשונה. פונקציה זו פועלת בשני אופנים:
/* file: strtok.c */ #include <stdio.h> #include <string.h> typedef char String[256]; void main() { String line = " First, second;third ? fourth"; char *ptr = strtok(line, " ,;?"); while(ptr != NULL) { printf("%s\n", ptr); ptr = strtok(NULL, " ,;?"); } } פלט התכנית: First second third fourth
הסבר: השורה line נראית כך בזיכרון לפני הניתוח -
בפעם הראשונה נקראת הפונקציה strtok עם הפרמטר line ומחרוזת תווי הפיסוק: char *ptr = strtok(line, " ,;?"); המצביע המוחזר ע"י strtok ומוצב ל- ptr הוא מצביע למילה הראשונה שהתגלתה עפ"י סימני הפיסוק:
strtok() החליפה את התו "," בתו מסיים המחרוזת. לכן בהדפסת ptr מודפס First
בקריאה הבאה ל- strtok() (בתוך הלולאה) מועבר כפרמטר ראשון NULL המציין עבורה המשך סריקה של המחרוזת האחרונה שהועברה לה (line): ptr = strtok(NULL, " ,;?");
הפונקציה מחזירה שוב מצביע לתת-המחרוזת שנמצאה, "second":
ולכן מודפס second
באופן דומה, לאחר הקריאה הבאה ל- strtok() מוחזרת תת-המחרוזת "third"
ומודפסת. לבסוף מוחזרת ומודפסת תת-המחרוזת "fourth":
בקריאה הבאה ל- strtok() מוחזר הערך NULL, ולכן הלולאה מסתיימת: while(ptr != NULL) הערות: 1. יש לשים לב ש- strtok() משנה את המחרוזת המקורית המועברת לה ע"י החלפת סימן הפיסוק המתאים בתו מסיים מחרוזת. 2. לצורך שמירת המקום האחרון בו הפסיקה, strtok() מגדירה משתנה מקומי סטטי ש"זוכר" את כתובת התו האחרון שנסרק במחרוזת. 3. במידה ויש מספר תווי פיסוק עוקבים, strtok() מזהה זאת ומדלגת עליהם, עד למציאת תת-מחרוזת המכילה לפחות תו אחד שאינו תו פיסוק. פונקציות לבדיקת סוג התוקימות פונקציות לבדיקת סוג התו המוגדרות בקובץ ctype.h. הפונקציות מקבלות כפרמטר תו ומחזירות ערך בוליאני המציין האם התו שייך לקטגוריה מסוימת. לדוגמא, הפונקציה isalpha() מחזירה ערך "אמת" (שונה מ- 0) אם תו נתון הוא אות אנגלית ו- 0 אחרת. היא מוכרזת כך: int isalpha( int c ); שימוש לדוגמא בפונקציה: int ch = getchar(); if(isalpha(ch)) printf("The char %c is a letter from a-z or A-Z", ch); הפונקציה isupper() בודקת ומחזירה ערך אמת (שונה מ- 0) אם התו הוא אות אנגלית גדולה: int ch = getchar(); if( isalpha(ch)) if( isupper(ch)) printf("The char %c is a letter from A-Z", ch); else printf("The char %c is a letter from a-z", ch); רשימת הפונקציות המלאה לבדיקת סוג תו מופיעה בטבלה בעמ' 231. תרגיליםקרא/י סעיף זה בספר ובצע/י את תר' 1-4 שבעמ' 232. מערכי מחרוזותמערך מחרוזות הוא מערך שכל כניסה בו היא מחרוזת. מגדירים מערך מחרוזות כך: char strings[6][15]; או ע"י שימוש בטיפוס מחרוזת המוגדר ע"י typedef: typedef char String[15]; String strings[6]; זוהי הגדרה של מערך בן 6 מחרוזות שארכה של כל אחת בו היא עד 15 תווים. ניתן למשל להעתיק מחרוזות לאיברי המערך ע"י strcpy strcpy(strings[0], "first"); strcpy(strings[1], "second"); strcpy(strings[2], strings[0]); ולשרשר מחרוזת אחת לשנייה: strcat(strings[2], strings[1]); תרשים המערך :
תכנית הדוגמא שבעמ' 233 קוראת מהקלט 8 שורות לתוך מערך מחרוזות ולאחר מכן מדפיסה אותן לפלט. מערך מצביעים לעומת מערך של מערכיםמבחינים בין שני סוגי מערכי מחרוזות:
char colors1[8][15] = {"red", "black", "white", "purple", "yellow", "pink", "green", "blue" }; בהגדרה כזו מוקצה עבור כל מחרוזת זיכרון עבור מלוא המערך, גם אם רק חלקו מנוצל. תמונת מערך המחרוזות בזיכרון:
char* colors2[8] = {"red", "black", "white", "purple", "yellow", "pink", "green", "blue" }; במקרה זה המימד השני של מערך התווים הדו-ממדי הוא אינו קבוע: לכל מחרוזת מוקצה מקום בהתאם לארכה:
כפי שניתן לראות, המערך הראשון הוא בזבזני יותר הן מבחינת הקצאת הזיכרון והן מבחינת אתחול המערך הכולל (מקומות ריקים במערך מאותחלים ל- 0). איזו שיטה עדיפה? רצוי להשתמש במערכי מערכים בייצוג של טקסט קבוע, כשלא אמורים לשנות את המחרוזות או את מיקומן. לעומת זאת, כאשר נדרשות פעולות מניפולציה על המחרוזות - כגון מיון - רצוי להגדיר מערכי מצביעים. הבעיה בשיטה 2 היא שכרגע איננו יודעים להקצות זיכרון מפורש עבור מחרוזת - דבר שנכיר רק בפרק 12. לעת עתה, ניתן להשתמש בשיטת מערכי מצביעים רק עבור מחרוזות המאותחלות בזמן הגדרתן. מיון מערך מחרוזותנכתוב תכנית שממיינת מערך מחרוזות. מערך המחרוזות יוגדר כמערך של מצביעים למחרוזת, תוך שימוש בטיפוס String המוגדר הפעם כ- char *. נשתמש בשיטת "מיון בועות" שראינו בפרק 7, "מערכים". מיון המערך מתבצע כך שהמצביעים למחרוזות מוחלפים. לדוגמא, אם שתי המחרוזות "white" ו- "purple" ממוקמות כך לפני ההחלפה ביניהן:
לאחר ההחלפה המערך ייראה כך:
קוד התכנית מובא בעמ' 235-236. פרמטרים לתכנית : argc, argv ו- envתכניות רבות מאפשרות למשתמש להפעיל אותן עם אופציות שונות. לדוגמא, תכנית המבצעת העתקת קובץ אחד לשני מאפשרת להעביר את שמות הקבצים כפרמטרים:
copy file1 file2
cp file1 file2 יתרה מזאת, הפונקציות מאפשרות להוסיף מציינים נוספים כדי לבחור את אופן הפעולה. לדוגמא, בתכנית ההעתקה הנ"ל, אם קובץ המקור הוא ספרייה קיימת אופציה להעתקת תתי-הספריות באופן רקורסיבי. כיצד ניתן להעביר פרמטרים לתכנית בשפת C ? הפונקציה main, שעד כה הגדרנו אותה כפונקציה שלא מקבלת פרמטרים, יכולה להיות מוכרזת גם כך: int main( int argc, char *argv[ ]); הפונקציה מקבלת 2 פרמטרים :
המחרוזת הראשונה ב- argv היא שם התכנית. לדוגמא, אם שם התכנית הוא prog.c, והיא הודרה לקובץ ביצוע בשם prog.exe, הפעלתה עם הפרמטרים הבאים prog hello 22 0.5 תגרום לכך ש- argc ו- argv ייראו כך:
כלומר, הפרמטרים מועברים לתכנית כמחרוזות. אם התכנית מעונינת לשלוף את המספרים מהמחרוזות היא יכולה לבצע זאת ע"י sscanf, לדוגמא: sscanf(argv[2], "%d" , &num1); /* num1 = 22 */ sscanf(argv[3], "%f" , &num2); /* num2 = 0.5 */ תכנית דוגמא: FTPבעמ' 239 מובאת כדוגמא תכנית FTP (File Transfer Protocol) שהיא תכנית נפוצה להעברת קבצים בין מחשבים מרוחקים דרך האינטרנט. התכנית מופעלת ב- 2 צורות עיקריות:
תרגולקרא/י סעיף זה בספר ובצע/י את התרגיל שבעמ' 241.
תמיכה בשפות בינלאומיות ו- Unicodeעיין/י בסעיף זה להרחבה בנושא Unicode ותמיכה בשפות בינלאומיות. סיכום
int main( int argc, char *argv[ ], char *env[]);
תרגיל מסכםבצע/י את התרגיל המסכם שבעמ' 245.
|