שיעור בתכנות מערכות בשפת C מיום א` (2.11)

Download Report

Transcript שיעור בתכנות מערכות בשפת C מיום א` (2.11)

‫מכללת אורט כפר‪-‬סבא‬
‫תכנות מערכות‬
‫בשפת ‪C‬‬
‫מחרוזות‬
‫‪02.11.14‬‬
‫אורי וולטמן‬
‫‪[email protected]‬‬
‫חידה לחימום‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫נתונות שתי ערימות של קוביות‪ ,‬כשבכל ערימה מספר הקוביות ההתחלתי‬
‫הוא אקראי‪.‬‬
‫שני שחקנים משחקים משחק של הסרת קוביות מהערימות‪ .‬כל שחקן‪ ,‬בתורו‪,‬‬
‫מסיר כמה קוביות שהוא רוצה (לפחות אחת) מאחת הערימות (שחקן יכול‬
‫להסיר קוביות מערימה אחת בתור אחד ומן הערימה השנייה בתור שאחריו)‪.‬‬
‫השחקן שמסיר את הקוביה האחרונה‪ ,‬מנצח במשחק‪.‬‬
‫פתחו אלגוריתם המקבל כקלט שני מספרים טבעיים המייצגים את מספר‬
‫הקוביות בשתי הערימות‪.‬‬
‫האלגוריתם יבחר האם ברצונו לשחק ראשון או שני‪ ,‬וישחק כך שהוא מנצח‬
‫תמיד‪.‬‬
‫חיפוש תו במחרוזת‬
‫ מאפשרת לחפש תו מסוים בתוך‬,string.h-‫ המוגדרת ב‬,strchr ‫הפונקציה התקנית‬
:‫ כותרת הפונקציה היא‬.‫מחרוזת‬

char *strchr (const char *s, const char c)
NULL ‫ או מחזירה‬,s ‫ במחרוזת‬c ‫הפונקציה מחזירה מצביע למופע הראשון של התו‬
.‫אם התו לא מופיע במחרוזת‬
?‫מה יהיה הפלט של קטע הקוד הבא‬
char str[] = “This is a sample string";
char *ptr;
printf(“Looking for the 's' character\n”);
ptr = strchr(str,’s’);
while (ptr != NULL) {
printf("found at %d\n",ptr-str);
ptr = strchr(ptr+1,’s’);
}


‫חיפוש תו במחרוזת‬
‫‪‬‬
‫נממש את הפונקציה ‪:strchr‬‬
‫)‪char *strchr (const char *s, const char c‬‬
‫{‬
‫)‪while (*s != ‘\0’ && *s != c‬‬
‫;‪s++‬‬
‫;)‪return ((*s == c) ? s : NULL‬‬
‫}‬
‫‪‬‬
‫סטודנט א' וסטודנט ב' מציעים לעשות שינוי בלולאת ה‪ while-‬שמופיעה במימוש‬
‫הנ"ל‪.‬‬
‫‪‬‬
‫ההצעה של סטודנט א'‪:‬‬
‫‪‬‬
‫ההצעה של סטודנט ב'‪:‬‬
‫; )‪while (*s++ != ‘\0’ && *s != c‬‬
‫; )‪while (*s != ‘\0’ && *s++ != c‬‬
‫‪‬‬
‫האם צריך המרצה לתת ציון שלילי לשני הסטודנטים? או רק לאחד מהם? או לאף‬
‫אחד מהם?‬
‫חיפוש תת‪-‬מחרוזת‬
‫‪‬‬
‫הפונקציה התקנית ‪ ,strstr‬המוגדרת ב‪ ,string.h-‬מאפשרת לחפש תת‪-‬מחרוזת בתוך‬
‫מחרוזת מסוימת‪ .‬כותרת הפונקציה היא‪:‬‬
‫)‪char *strstr (const char *haystack, const char *needle‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫הפונקציה מחזירה מצביע למופע הראשון של ‪ needle‬ב‪ ,haystack-‬או מחזירה‬
‫‪ NULL‬אם ‪ needle‬אינה תת‪-‬מחרוזת של ‪ .haystack‬אם ‪ needle‬היא מחרוזת‬
‫ריקה‪ ,‬הפונקציה מחזירה מצביע לתחילת ‪.haystack‬‬
‫מה יהיה הפלט של קטע התכנית הבא?‬
‫;"‪char s1[] = “This is a sample string‬‬
‫;))”‪puts(strstr(s1,”is‬‬
‫איזה שינוי יש לערוך בקטע התכנית‪ ,‬על מנת שהפלט יהיה "‪?"a sample string‬‬
‫האם יש יותר מתשובה אחת לשאלה הקודמת?‬
‫העתקת מחרוזות‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫לפעמים היינו רוצים להשתמש ב‪ ,strcpy(to,from)-‬אבל להעתיק רק מספר מסוים‬
‫של תווים מ‪ ,from-‬ולא את ‪ from‬כולו‪.‬‬
‫לצורך כך קיימת ב‪ string.h-‬פונקציה תקנית בשם ‪ ,strncpy‬המתנהגת כמו ‪,strcpy‬‬
‫למעט זה שהיא מקבלת פרמטר שלישי ‪ ,n‬הקובע את מספר התווים יועתקו‪.‬‬
‫כותרת הפונקציה היא‪:‬‬
‫)‪char *strncpy (char *to, const char *from, unsigned int n‬‬
‫‪‬‬
‫דוגמא לשימוש בפונקציה‪:‬‬
‫;"‪char str[] ="This is a simple string‬‬
‫;)"‪char *pch = strstr(str,"simple‬‬
‫;)‪strncpy(pch,"sample",6‬‬
‫;)‪puts(str‬‬
‫‪‬‬
‫‪‬‬
‫אם ‪ from‬קצרה מ‪ n-‬בתים‪ ,‬אז יועתקו ’‪-‘\0‬ים (על מנת להשלים ל‪ n-‬תווים)‪.‬‬
‫במידה ו‪ to-‬ו‪ from-‬נמצאים באזורים חופפים (ולו חלקית) בזיכרון‪ ,‬אז התנהגות‬
‫הפונקציה אינה מוגדרת‪.‬‬
‫השוואת מחרוזות‬
‫‪‬‬
‫הכרנו קודם (ואף מימשנו) את הפונקציה ‪ strcmp‬המשווה בין שתי‬
‫מחרוזות‪ ,‬שכותרתה‪:‬‬
‫)‪int strcmp (const char *s1, const char *s2‬‬
‫‪‬‬
‫‪‬‬
‫הפונקציה מקבלת שתי מחרוזות‪ ,‬משווה ביניהן‪ ,‬ומחזירה ‪ 0‬אם שתי‬
‫המחרוזות שוות‪ ,‬מס' שלילי אם ‪ ,s2 < s1‬ומס' חיובי אם ‪.s2 > s1‬‬
‫השוואת מחרוזות נעשית לפי סדר מילוני )‪:(lexicographic order‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫משווים בין האות הראשונה של שתי המחרוזות‪ .‬אם האות הראשונה של ‪ s1‬קודמת‬
‫באלפבית לאות הראשונה של ‪ ,s2‬אזי ‪ .s1 < s2‬ואם להיפך – אז ‪.s1 > s2‬‬
‫אם לשתי המחרוזות אותה האות הראשונה‪ ,‬אז משווים בין האות השנייה‪ ,‬באופן דומה‪.‬‬
‫לאחר מכן‪ ,‬אם עדיין מדובר באותיות שוות‪ ,‬ממשיכים באותו אופן לאות השלישית‪ ,‬וכו'‪.‬‬
‫אם אחת המחרוזות נגמרה‪ ,‬בשעה שבמחרוזות השנייה עוד יש אותיות – אז המחרוזות‬
‫השנייה גדולה מהראשונה‪.‬‬
‫אם שתי המחרוזות נגמרו באותו זמן‪ ,‬מבלי שהתהליך נעצר קודם לכן‪ ,‬אזי המחרוזות‬
‫שוות‪.‬‬
‫השוואת מחרוזות‬
‫‪‬‬
‫מה תחזיר הפונקציה ‪ strcmp‬עבור הזימונים הבאים‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪< 0‬‬
‫)”‪strcmp(“hello”,”world‬‬
‫‪0‬‬
‫)”‪strcmp(“hello”,”hello‬‬
‫‪> 0‬‬
‫)”‪strcmp(“hello”,”Hello‬‬
‫הסיבה שעבור הזימון האחרון לא החזירה הפונקציה‬
‫‪ strcmp‬את הערך ‪ ,0‬היא שהתו '‪ 'H‬והתו '‪ 'h‬הם שני‬
‫תווים שונים לגמרי מבחינתה (הם ממוקמים במקומות‬
‫שונים בטבלת ‪.)ASCII‬‬
‫אם נרצה שהפונקציה ‪ strcmp‬תחזיר ‪ 0‬עבור זימון‬
‫זה‪ ,‬כלומר – שהיא לא תהיה רגישה לגודל אות‬
‫(‪ ,)case insensitive‬נשתמשו במקומה בפונקציה‬
‫‪.stricmp‬‬
‫השוואת מחרוזות‬
‫‪‬‬
‫מוגדרת הפונקציה‪:‬‬
‫)‪int stricmp (const char *s1, const char *s2‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫הפונקציה מתנהגת ממש כמו ‪ ,strcmp‬כלומר‪ :‬היא מקבלת שתי‬
‫מחרוזות‪ ,‬משווה ביניהן‪ ,‬ומחזירה ‪ 0‬אם שתי המחרוזות שוות‪ ,‬מס'‬
‫שלילי אם ‪ ,s2 < s1‬ומס' חיובי אם ‪.s2 > s1‬‬
‫ההבדל היחיד הוא שהפונקציה אינה מבדילה בין אותיות קטנות‬
‫ואותיות גדולות ('‪ 'A‬מבחינתה זהה לגמרי ל‪ 'B' ,'a'-‬זהה ל‪ ,'b'-‬וכו')‪.‬‬
‫איך נממש את הפונקציה?‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫נקצה זיכרון (ע"י ‪ malloc‬שב‪ )stdlib.h-‬לשתי מחרוזות זמניות ‪ t1‬ו‪t2-‬‬
‫נעתיק את ‪ s1‬ל‪ ,t1-‬ואת ‪ s2‬ל‪ ,t2-‬אבל נהפוך כל אות לאות קטנה על‪-‬ידי‬
‫שימוש בפונקציה הסטנדרטית )(‪ ,tolower‬המוגדרת ב‪.ctype.h-‬‬
‫כעת אנחנו בטוחים ש‪ t1-‬ו‪ t2-‬מכילות רק אותיות קטנות‪.‬‬
‫נחזיר את הערך של )‪.strcmp(t1,t2‬‬
‫השוואת מחרוזות‬
int stricmp (const char *s1, const char *s2)
{
char *t1,*t2;
?malloc-‫ב‬
'+1' ‫מדוע מופיע‬
‫מדוע לא מכפילים את הביטוי שמופיע‬
?sizeof(char) ‫ בביטוי‬malloc-‫בתוך ה‬
t1 = malloc(strlen(s1) + 1);
?‫איזו שגיאה יש בפונקציה‬
while (*s1) *t1++ = tolower(*s1++);
*t1 = '\0';
t2 = malloc(strlen(s2) + 1);
while (*s2) *t2++ = tolower(*s2++);
*t2 = '\0';
return strcmp(t1,t2);
}



‫השוואת מחרוזות‬
int stricmp (const char *s1, const char *s2)
{
‫שאנחנו מזמנים כאן את‬
char *t1,*t2;
‫אבל מבלי לבדוק האם הקצאת‬
int returnvalue;
‫נשים לב‬
,malloc
‫הזיכרון הצליחה (אולי הפונקציה‬
.)?‫ כי אין זיכרון פנוי‬NULL ‫החזירה‬
t1 = malloc(strlen(s1) + 1);
while (*s1) *t1++ = tolower(*s1++); t2-‫ ו‬t1 ‫ המצביעים‬:‫יתרה מכך‬
*t1 = '\0';
‫ ולא על‬,‫מצביעים על סוף המחרוזת‬
t2 = malloc(strlen(s2) + 1); ‫ איך ניתן לפתור‬.‫תחילתה‬
while (*s2) *t2++ = tolower(*s2++);
?‫את הבעיה‬
*t2 = '\0';
return
strcmp(t1,t2);
returnvalue
= strcmp(t1,t2);
free(t1); free(t2);
return returnvalue;
}


‫השוואת מחרוזות‬
‫‪‬‬
‫פונקציה נוספת המשווה שתי מחרוזות לפי סדר לקסיקוגרפי היא‬
‫הפונקציה ‪ ,strncmp‬שכותרתה‪:‬‬
‫)‪int strncmp (const char *s1, const char *s2, unsigned n‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫הפונקציה מקבלת שתי מחרוזות‪ ,‬ומשווה רק בין ‪ n‬התווים הראשונים‬
‫של כל מחרוזת‪ ,‬ומחזירה ‪ 0‬אם שתי המחרוזות שוות‪ ,‬מס' שלילי אם‬
‫‪ ,s2 < s1‬ומס' חיובי אם ‪.s2 > s1‬‬
‫אם במחרוזת מסוימת יש פחות מ‪ n-‬תווים‪ ,‬אז מתייחסים רק לתווים‬
‫שעד ל‪.null terminator-‬‬
‫אם בשתי המחרוזות יש פחות מ‪ n-‬תווים‪ ,‬אין הבדל בין ‪ strncmp‬ל‪-‬‬
‫‪.strcmp‬‬
‫)‪strncmp(“helloa”,”hellob”,5‬‬
‫‪0‬‬
‫‪< 0‬‬
‫)‪strncmp(“helloa”,”hellob”,6‬‬
‫‪< 0‬‬
‫)‪strncmp(“helloa”,”hellob”,9‬‬
‫השוואת מחרוזות‬
int strncmp (const char *s1, const char *s2, unsigned int n)
{
char *t1, *t2;
...‫ הפונקציה לא תעבוד כשורה‬
int returnvalue;
t1 = malloc(strlen(s1) + 1);
strncpy(t1,s1,n);
t1[n] = '\0';
t2 = malloc(strlen(s2) + 1);
strncpy(t2,s2,n);
t2[n] = '\0';
returnvalue = strcmp(t1,t2);
free(t1); free(t2);
return returnvalue;
}
?‫מה חסר‬
‫גם כאן צריך היה לוודא‬
...‫שהקצאת הזיכרון הצליחה‬

‫חיפוש תווים במחרוזת‬
‫‪‬‬
‫ב‪ string.h-‬מוגדרת הפונקציה התקנית‪:‬‬
‫)‪char *strpbrk (const char *str1, const char *str2‬‬
‫‪‬‬
‫הפונקציה מחזירה מצביע למופע הראשון ב‪ str1-‬של תו‬
‫מתוך ‪ .str2‬אם הפונקציה סרקה את ‪ ,str1‬והגיעה עד ל‪-‬‬
‫‪ ,null terminator‬מבלי שמצאה אף אחד מהתווים שבתוך‬
‫‪ ,str2‬אז היא תחזיר ‪.NULL‬‬
#include <stdio.h>
#include <string.h>
‫חיפוש תווים במחרוזת‬
int main()
{
char str[] = "This is a sample string";
char key[] = "aeiou";
char *pch;
printf ("Vowels in '%s': ",str);
pch = strpbrk (str, key);
while (pch != NULL) {
printf ("%c " , *pch);
pch = strpbrk (pch+1,key);
}
printf ("\n");
return 0;
}
?‫מה יהיה הפלט‬

‫מציאת רישא מותנית‬
:‫ היא‬,string.h-‫פונקציה המוגדרת גם היא ב‬

unsigned int strspn (const char *str1, const char *str2)
,str1 ‫) המקסימלית של‬prefix( ‫הפונקציה מחזירה את אורך הרישא‬
.str2-‫המורכבת כולה מתווים שנמצאים ב‬

#include <stdio.h>
#include <string.h>
int main()
{
char str[] = “125th”, cset[] = "1234567890";
int i = strspn (str,cset);
printf ("The length of the number is %d.\n",i);
return 0;
}
.0 ‫ אז הפונקציה תחזיר‬,str2-‫ אינו שייך ל‬str1 ‫אם התו הראשון של‬

‫מציאת רישא מותנית‬
:‫ היא‬,string.h-‫ המוגדרת גם היא ב‬,strspn-‫פונקציה הפוכה ל‬

unsigned int strcspn (const char *str1, const char *str2)
,str1 ‫) המקסימלית של‬prefix( ‫הפונקציה מחזירה את אורך הרישא‬
.str2-‫המורכבת כולה מתווים שאינם נמצאים ב‬

#include <stdio.h>
#include <string.h>
int main()
{
char str[] = “fcba73”, cset[] = "1234567890";
int i = strcspn (str,cset);
printf ("The first number is at position %d.\n",i+1);
return 0;
}
‫ אם‬str1 ‫ ולכן היא תחזיר את האורך של‬,‘\0’-‫הפונקציה סורקת גם את ה‬
.str1-‫ לא נמצאים ב‬str2 ‫אף אחד מהתווים של‬
