דילוג לתוכן המרכזי

בגרות במדעי המחשב תכנות באינטרנט 899282/899372 - קיץ 2021 (נבצרים)

נבחנים אקסטרניים · שאלון 899282/899372 · 9 שאלות · 2 שעות · פתרון מלא לכל סעיף

בחינת הבגרות במדעי המחשב, תכנות באינטרנט, קיץ 2021 (נבצרים), כל 9 השאלות כמו בשאלון. לכל סעיף יש תשובה ופתרון מלא: הרעיון, הקוד או המעקב, ובדיקה קצרה. כל קוד מופיע ב-Java וב-C#. נסו לפתור לבד, ואז פתחו את הפתרון.

הוראות הבחינה (כמו בשאלון)

שים לב: בבחינה זו יש הנחיות מיוחדות.
יש לענות על השאלות על פי הנחיות אלה.

הוראות לנבחן
א. משך הבחינה: שעתיים.
ב. מבנה השאלון ומפתח ההערכה:
בשאלון זה שלושה פרקים.
יש לבחור בחמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות. סך הכול — 100 נקודות.
ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנּות.

כתוב במחברת הבחינה בלבד, בעמודים נפרדים, כל מה שברצונך לכתוב כטיוטה (ראשי פרקים וכדומה).
רישום טיוטות כלשהן על דפים מחוץ למחברת הבחינה עלול לגרום לפסילת הבחינה!
רשום "טיוטה" בראש כל עמוד טיוטה.

ההנחיות בשאלון זה מנוסחות בלשון זכר ומכוונות לנבחנות ולנבחנים כאחד.

בהצלחה!

פרק ראשון

בשאלון זה שלושה פרקים.
עליך לענות על חמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות.

שאלה 1 · שאילתת SELECT – מנויי נוער עם שיא מתחת ל־20 שניות (20 נקודות)

במסד נתונים של בריכה עירונית קיימת טבלה ששמה AllUsers ובה נתוני המנויים בבריכה .
בטבלה יש 4 עמודות:
firstName (שם פרטי), code (קוד מנוי), age (שכבת גיל: ילד/נוער/מבוגר) ו־ record (שיא שחייה בשניות).

כתוב שאילתה בשפת SQL המאחזרת את קוד המנוי של המנויים ששכבת הגיל שלהם היא "נוער" ושיא השחיה קטן מ 20 שניות.

כתוב שאילתה בשפת SQL המאחזרת את קוד המנוי של המנויים ששכבת הגיל שלהם היא "נוער" ושיא השחיה קטן מ 20 שניות.

(20 נקודות)
◂ פתרון מלא לשאלה 1

פתרון

תשובה

SELECT code FROM AllUsers WHERE age = 'נוער' AND record < 20;

מה נדרש: לאחזר רק את העמודה code של המנויים שמקיימים שני תנאים יחד: שכבת גיל "נוער" וגם שיא קטן מ־20 שניות.

רעיון:

•SELECT code – בוחרים רק את העמודה המבוקשת (ולא *).
•FROM AllUsers – הטבלה.
•WHERE עם שני תנאים המחוברים ב־AND: age = 'נוער' (מחרוזת – בתוך גרשיים) ו־record < 20 (מספר – בלי גרשיים). "קטן מ־20" הוא < ולא <=.

פתרון:

SQL
SELECT code
FROM AllUsers
WHERE age = 'נוער' AND record < 20;

בדיקה: בנינו טבלה לדוגמה עם 7 מנויים, ובהם: דנה (101, נוער, 18), יוסי (102, נוער, 20), נועה (103, נוער, 25), עומר (104, ילד, 15), רון (105, מבוגר, 19), שירה (106, נוער, 12). השאילתה החזירה את הקודים 101 ו־106 בלבד: יוסי לא נכלל כי 20 אינו קטן מ־20, ועומר ורון לא נכללו כי אינם בשכבת "נוער".

טעות נפוצה: לכתוב OR במקום AND (אז יוחזרו גם כל בני הנוער וגם כל מי ששיאו קטן מ־20), או לכתוב record < '20' עם גרשיים.

שאלה 2 · ספירת מנויים לפי שכבת גיל – GROUP BY (20 נקודות)

במסד נתונים של בריכה עירונית קיימת טבלה ששמה AllUsers ובה נתוני המנויים בבריכה .
בטבלה יש 4 עמודות:
firstName (שם פרטי), code (קוד מנוי), age (שכבת גיל: ילד/נוער/מבוגר) ו־ record (שיא שחייה בשניות).

רוצים לקבל את מספר המנויים לפי שכבת גיל.
לפניך מספר אפשרויות.
איזה מן המשפטים שלפניך נכון יחזיר את התשובה המבוקשת?

העתק למחברתך את התשובה הנכונה מבין האפשרויות א–ד שלפניך.
א. Select count(code) from AllUser order by age
ב. Select firstName, code from AllUser ordered by record group by age
ג. Select * from AllUser ordered by age ascending group by age
ד. Select count(*) from AllUser group by age

העתק למחברתך את התשובה הנכונה מבין האפשרויות א–ד שלפניך.

(20 נקודות)
◂ פתרון מלא לשאלה 2

פתרון

תשובה

ד. Select count(*) from AllUser group by age

מה נדרש: לבחור את המשפט שמחזיר כמה מנויים יש בכל שכבת גיל (תוצאה אחת לכל קבוצה).

רעיון: "מספר ... לפי ..." = פונקציית צבירה (count) יחד עם GROUP BY על העמודה שלפיה מחלקים לקבוצות. GROUP BY age מחלק את השורות לקבוצות לפי ערך age, ו־count(*) סופר את השורות בכל קבוצה.

פתרון: התשובה הנכונה היא ד:

SQL
Select count(*) from AllUser group by age

למה האחרות שגויות:

•א – אין GROUP BY, ולכן count(code) מחזיר מספר אחד – מספר כל המנויים בטבלה. order by רק ממיין ואינו מחלק לקבוצות.
•ב – ordered by אינה מילה שמורה ב־SQL (הנכון הוא order by), וגם ORDER BY צריך לבוא אחרי GROUP BY. בנוסף, אין בה ספירה, ובחירת firstName ו־code שאינן בתוך GROUP BY אינה חוקית.
•ג – אותן שגיאות תחביר (ordered by, ascending במקום ASC, סדר הפסוקיות), ו־* עם GROUP BY אינו מחזיר ספירה.

בדיקה: על טבלה לדוגמה עם 4 בני נוער, 2 מבוגרים וילד אחד, המשפט ד החזיר שלוש שורות: 1, 2, 4 (מספר אחד לכל שכבת גיל). כדי לראות לאיזו שכבה שייך כל מספר, נוח להוסיף את age לרשימת העמודות:

SQL
Select age, count(*) from AllUser group by age

שהחזיר: ילד – 1, מבוגר – 2, נוער – 4. משפט א החזיר את המספר 7 בלבד, ומשפט ב נכשל בשגיאת תחביר.

הערה: בשאלה הטבלה נקראת AllUsers ובאפשרויות נכתב AllUser – זה אינו משפיע על הבחירה.

שאלה 3 · מעקב – טבלת HTML הנבנית בשרת (20 נקודות)

העתק למחברתך את מה שיוצג בדפדפן לאחר הרצת הפונקציה Func שלפניך.

C#
public void Func ()
{
     string str = "<table border = '1'>";
     for (int m = 10; m <= 99; m++)
     {
          if (m % 10 == m / 10)
          {
               str += "<tr>";
               str += "<td>" + m + "</td>";
               str += "</tr>";
          }
     }
     str += "</table>";
     Response.Write(str);
}

העתק למחברתך את מה שיוצג בדפדפן לאחר הרצת הפונקציה Func שלפניך.

(20 נקודות)
◂ פתרון מלא לשאלה 3

פתרון

תשובה

טבלה עם מסגרת, בעמודה אחת ו־9 שורות: 11, 22, 33, 44, 55, 66, 77, 88, 99 (כל מספר בשורה משלו).

מה נדרש: לתאר מה הדפדפן מציג – כלומר את הטבלה ש־Response.Write שולח, ולא את קוד ה־HTML.

רעיון: הלולאה עוברת על כל המספרים הדו־ספרתיים 10–99. עבור m דו־ספרתי:

•m % 10 היא ספרת האחדות.
•m / 10 (חילוק שלמים) היא ספרת העשרות.

התנאי מתקיים רק כששתי הספרות שוות: 11, 22, ..., 99. לכל מספר כזה נוספת שורה (<tr>) עם תא אחד (<td>).

מעקב (חלקי):

mm % 10m / 10שווים?נוסף לטבלה
1001לא–
1111כן<tr><td>11</td></tr>
1221לא–
...
2222כן<tr><td>22</td></tr>
...
9999כן<tr><td>99</td></tr>

פתרון: המחרוזת שנשלחת לדפדפן (שבירות השורות נוספו לקריאוּת):

HTML
<table border = '1'>
<tr><td>11</td></tr>
<tr><td>22</td></tr>
<tr><td>33</td></tr>
<tr><td>44</td></tr>
<tr><td>55</td></tr>
<tr><td>66</td></tr>
<tr><td>77</td></tr>
<tr><td>88</td></tr>
<tr><td>99</td></tr>
</table>

מה יוצג בדפדפן: טבלה עם קווי מסגרת (border = '1'), בעלת עמודה אחת ו־9 שורות:

11
22
33
44
55
66
77
88
99

בדיקה: הפונקציה מפיקה בדיוק 9 שורות, 11 עד 99.

טעות נפוצה: לחשוב ש־m / 10 נותן מספר עשרוני (למשל 1.1). ב־C# חילוק של שני int הוא חילוק שלמים. טעות נוספת: לצייר את כל המספרים בשורה אחת – כל מספר נמצא בתוך <tr> משלו, ולכן בשורה נפרדת.

פרק שני

בשאלון זה שלושה פרקים.
עליך לענות על חמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות.

שאלה 4 · JavaScript – בדיקת תקינות כתובת מייל (השלמת ביטויים) (20 נקודות)

מייל של מנוי לבריכה צריך לעמוד בדרישות האלה:

•חייב להסתיים ב- .com או .co.il
•חייב להתחיל באות קטנה או גדולה.
•חייב להכיל את התו @ פעם אחת בלבד.

בטופס הרשמה לאתר כלשהו יש שדה למילוי סיסמה, והוא רשום כך:

HTML
<input type="mail" maxlength=20 id="mail" name="mail" />

בעמוד הבא מוצגת הפונקציה isCorrectMail ב־ JavaScript . הפונקציה מחזירה true אם המייל תקין, אחרת היא מחזירה false.
המשתנה num בפונקציה נועד לשמור על מספר המופעים של התו @.
בפונקציה חסרים חמישה ביטויים. הביטויים החסרים מסומנים במספרים [1]–[5] .
כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[5] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.

JavaScript
function isCorrectMail ()
{
     var mail = document.getElementById(___[1]___).value;
     var num = 0;
     if (!((mail.charAt(0) >=’a’ && mail.charAt(0)<=’z’) || (mail.charAt(0)>=’A’ && mail.charAt(0)<=’Z’)))
          return (___[2]___)
     if (! (mail.indexOf(“.com”) == (mail.length - 4))
          else (if  ! (  ___[3]___ ))
               return false;
     for (var i = 1; i < mail.length; i++)
     {
          if (___[4]___)
               num ++;
     }
     return (___[5]___);
}

כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[5] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.

(20 נקודות)
◂ פתרון מלא לשאלה 4

פתרון

תשובה

[1] "mail"
[2] false
[3] mail.indexOf(".co.il") == (mail.length - 6)
[4] mail.charAt(i) == '@'
[5] num == 1

מה נדרש: להשלים חמישה ביטויים כך שהפונקציה תחזיר true רק למייל שמתחיל באות אנגלית, מסתיים ב־.com או ב־.co.il ומכיל @ אחד בדיוק.

רעיון – ביטוי אחר ביטוי:

•[1] getElementById מקבל את ערך התכונה id של האלמנט, כמחרוזת. לשדה יש id="mail", ולכן: "mail".
•[2] אם התו הראשון אינו אות קטנה וגם אינו אות גדולה – המייל לא תקין, ולכן מחזירים false.
•[3] סיום ב־.com נבדק כך: המקום שבו מתחילה המחרוזת ".com" הוא האורך פחות 4 (האורך של ".com"). באותו אופן, ".co.il" באורך 6 תווים, ולכן: mail.indexOf(".co.il") == (mail.length - 6). הכוונה במבנה המודפס: אם המייל לא מסתיים ב־.com וגם לא מסתיים ב־.co.il – מחזירים false.
•[4] בלולאה עוברים על התווים וסופרים את התווים @: mail.charAt(i) == '@' (הלולאה מתחילה מ־1, ואין בכך בעיה – התו במקום 0 כבר נבדק שהוא אות).
•[5] המייל תקין רק אם @ מופיע פעם אחת בדיוק: num == 1 (ביטוי בוליאני – true או false).

פתרון:

מספרהביטוי החסר
[1]"mail"
[2]false
[3]mail.indexOf(".co.il") == (mail.length - 6)
[4]mail.charAt(i) == '@'
[5]num == 1

הקוד המודפס מכיל שגיאות הקלדה (מירכאות מעוצבות, סוגריים חסרים ו־else (if במקום מבנה תקין). הפונקציה המלאה בתחביר תקין, עם הביטויים שהשלמנו:

JavaScript
function isCorrectMail ()
{
     var mail = document.getElementById("mail").value;
     var num = 0;
     if (!((mail.charAt(0) >= 'a' && mail.charAt(0) <= 'z') || (mail.charAt(0) >= 'A' && mail.charAt(0) <= 'Z')))
          return (false);
     if (!(mail.indexOf(".com") == (mail.length - 4)))
          if (!(mail.indexOf(".co.il") == (mail.length - 6)))
               return false;
     for (var i = 1; i < mail.length; i++)
     {
          if (mail.charAt(i) == '@')
               num++;
     }
     return (num == 1);
}

בדיקה: מה שמוצג בדפדפן:

•dana@gmail.com, Yossi12@walla.co.il, a@b.com ← true.
•1dana@gmail.com (מתחיל בספרה), @gmail.com, dana@gmail.net, dana@gmail.comx, dana@gmail.co.il.org (סיום שגוי), dana@@gmail.com, da@na@gmail.com (שני @), danagmail.com (אין @), מחרוזת ריקה ← false.

הערה למתקדמים: במבנה הנתון יש חולשה – אם המייל קצר מ־4 תווים ואין בו ".com", אז indexOf מחזיר ‎-1, וגם mail.length - 4 שווה ‎-1, ולכן הבדיקה "עוברת". למשל a@b ו־ab@cd מתקבלים כתקינים. אפשר לתקן בתוספת בדיקה שהאורך מספיק, למשל mail.length > 4. חולשה נוספת: indexOf מחזיר את המופע הראשון, ולכן מייל כמו a.com@x.com נדחה אף שהוא מסתיים ב־.com; lastIndexOf היה פותר זאת. זה אינו נדרש בשאלה.

טעות נפוצה: לכתוב ב־[1] mail בלי מירכאות – אז זהו שם של משתנה ולא ה־id של השדה. וב־[5] לכתוב num > 0 – זה מאשר גם מייל עם שני @.

שאלה 5 · שליפה ממסד נתונים והצגה בטבלה – השלמת ביטויים (20 נקודות)

במסד נתונים של איגוד הארצי של השחיינים המקצועיים יש טבלה ששמה TableOfSwimmers, ובה נתוני השחיינים.
העמודות בטבלה הן: swimmerName (שם השחיין) מטיפוס מחרוזת , idNumber (מספר זיהוי) מטיפוס שלם, city (עיר השחיין) מטיפוס מחרוזת, gender (מין השחיין— זכר או נקבה או אחר) מטיפוס מחרוזת ו- style (סגנון שחייה) מטיפוס מחרוזת (חתירה/ חזה/ פרפר).
האיגוד רוצה להציג את נתונים של השחיינים בסגנון שחייה פרפר, בעזרת הפונקציה PrintTable .
הפונקציה PrintTable מקבלת מחרוזת התחברות למסד הנתונים — connectionString , ומציגה בטבלה את השם ,מספר זיהוי ,
מגדר ועיר מגורים של כל אחד מן השחיינים שסגנון השחייה הוא "פרפר".
דוגמה לטבלה שהפונקציה מציגה:

עיר מגוריםמגדרשםמספר זיהוי
אשקלוןזכרדוד1253
חיפהנקבהלירון5436
קרית שמונהנקבהיעל8564
תל אביבזכרתומר1547

הפונקציה PrintTable מוצגת לפניך, וחסרים בה שישה ביטויים. הביטויים החסרים מסומנים במספרים [1]–[6] .
כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[6] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.

C#
protected void PrintTable(string connectionString)
{
     DataSet ds = new DataSet();
     SqlConnection connection = new SqlConnection(___[1]___);
     SqlCommand cmd = new SqlCommand();
     cmd.CommandText = " ___[2]___ FROM ___[3]___ ";
     cmd.Connection = connection;
     connection.Open();
     SqlDataAdapter adapter = new SqlDataAdapter(cmd);
     adapter.Fill(ds) ;
     connection.Close();
     string resultStr = "<table>";
     resultStr += "<tr><th> עיר מגורים </th><th>___[4]___</th><th> שם </th><th> מספר זיהוי</th></tr>";
     foreach (___[5]___)
     {
          resultStr += "<tr>";
          resultStr += "<td>" + dr["city"].ToString() + "</td>";
          resultStr += "<td>" + dr["gender"].ToString() + "</td>";
          resultStr += "<td>" + dr["swimmerName"].ToString() + "</td>";
          resultStr += "<td>" + dr[___[6]___].ToString() + "</td>";
          resultStr += "</tr>";
     }
     resultStr += "</table>";
     Response.Write (resultStr);
}

כתוֹב במחברתך את מספרי הביטויים החסרים [1]–[6] , בסדר עולה, וליד כל מספר כתוֹב את הביטוי החסר שהוא מייצג.

(20 נקודות)
◂ פתרון מלא לשאלה 5

פתרון

תשובה

[1] connectionString
[2] SELECT swimmerName, idNumber, city, gender
[3] TableOfSwimmers WHERE style = 'פרפר'
[4] מגדר
[5] DataRow dr in ds.Tables[0].Rows
[6] "idNumber"

מה נדרש: להשלים פונקציה ששולפת מהמסד את השחיינים בסגנון פרפר ובונה מהם טבלת HTML.

רעיון – לפי סדר הפעולות בפונקציה:

•[1] יצירת החיבור דורשת את מחרוזת ההתחברות – הפרמטר של הפונקציה: connectionString.
•[2]–[3] השאילתה: בוחרים את ארבע העמודות שמוצגות, מהטבלה TableOfSwimmers, רק כש־style הוא 'פרפר'. ה־WHERE חייב לבוא אחרי שם הטבלה, ולכן הוא נכנס ל־[3].
•[4] שורת הכותרות צריכה להתאים לסדר התאים בכל שורה: city, gender, swimmerName, idNumber. העמודה השנייה היא gender, ולכן הכותרת היא מגדר.
•[5] adapter.Fill(ds) ממלא את הטבלה הראשונה ב־DataSet, ds.Tables[0]. עוברים על השורות שלה, ובגוף הלולאה משתמשים במשתנה dr, ולכן: DataRow dr in ds.Tables[0].Rows.
•[6] התא האחרון בשורה הוא מספר הזיהוי: "idNumber" (שם העמודה, כמחרוזת).

פתרון:

מספרהביטוי החסר
[1]connectionString
[2]SELECT swimmerName, idNumber, city, gender
[3]TableOfSwimmers WHERE style = 'פרפר'
[4]מגדר
[5]DataRow dr in ds.Tables[0].Rows
[6]"idNumber"

(ב־[2] אפשר גם SELECT * – הפונקציה משתמשת רק בעמודות שהיא צריכה.)

הפונקציה המלאה:

C#
protected void PrintTable(string connectionString)
{
     DataSet ds = new DataSet();
     SqlConnection connection = new SqlConnection(connectionString);
     SqlCommand cmd = new SqlCommand();
     cmd.CommandText = " SELECT swimmerName, idNumber, city, gender FROM TableOfSwimmers WHERE style = 'פרפר' ";
     cmd.Connection = connection;
     connection.Open();
     SqlDataAdapter adapter = new SqlDataAdapter(cmd);
     adapter.Fill(ds) ;
     connection.Close();
     string resultStr = "<table>";
     resultStr += "<tr><th> עיר מגורים </th><th>מגדר</th><th> שם </th><th> מספר זיהוי</th></tr>";
     foreach (DataRow dr in ds.Tables[0].Rows)
     {
          resultStr += "<tr>";
          resultStr += "<td>" + dr["city"].ToString() + "</td>";
          resultStr += "<td>" + dr["gender"].ToString() + "</td>";
          resultStr += "<td>" + dr["swimmerName"].ToString() + "</td>";
          resultStr += "<td>" + dr["idNumber"].ToString() + "</td>";
          resultStr += "</tr>";
     }
     resultStr += "</table>";
     Response.Write (resultStr);
}

השאילתה לבדה:

SQL
SELECT swimmerName, idNumber, city, gender FROM TableOfSwimmers WHERE style = 'פרפר'

בדיקה: טבלה TableOfSwimmers עם ארבעת השחיינים מהדוגמה ועוד שני שחיינים בסגנונות אחרים (חזה, חתירה). השאילתה החזירה רק את ארבעת שחייני הפרפר. הפונקציה מציגה את הטבלה:

HTML
<table>
<tr><th> עיר מגורים </th><th>מגדר</th><th> שם </th><th> מספר זיהוי</th></tr>
<tr><td>אשקלון</td><td>זכר</td><td>דוד</td><td>1253</td></tr>
<tr><td>חיפה</td><td>נקבה</td><td>לירון</td><td>5436</td></tr>
<tr><td>קרית שמונה</td><td>נקבה</td><td>יעל</td><td>8564</td></tr>
<tr><td>תל אביב</td><td>זכר</td><td>תומר</td><td>1547</td></tr>
</table>

בדיוק כמו בטבלת הדוגמה.

טעות נפוצה: לשים את WHERE ב־[2] (לפני FROM), או לשכוח את הגרשיים סביב 'פרפר'. וב־[6] לכתוב idNumber בלי מירכאות – האינדקס של dr הוא שם העמודה כמחרוזת.

הקוד המלא
C#
protected void PrintTable(string connectionString)
{
     DataSet ds = new DataSet();
     SqlConnection connection = new SqlConnection(connectionString);
     SqlCommand cmd = new SqlCommand();
     cmd.CommandText = " SELECT swimmerName, idNumber, city, gender FROM TableOfSwimmers WHERE style = 'פרפר' ";
     cmd.Connection = connection;
     connection.Open();
     SqlDataAdapter adapter = new SqlDataAdapter(cmd);
     adapter.Fill(ds) ;
     connection.Close();
     string resultStr = "<table>";
     resultStr += "<tr><th> עיר מגורים </th><th>מגדר</th><th> שם </th><th> מספר זיהוי</th></tr>";
     foreach (DataRow dr in ds.Tables[0].Rows)
     {
          resultStr += "<tr>";
          resultStr += "<td>" + dr["city"].ToString() + "</td>";
          resultStr += "<td>" + dr["gender"].ToString() + "</td>";
          resultStr += "<td>" + dr["swimmerName"].ToString() + "</td>";
          resultStr += "<td>" + dr["idNumber"].ToString() + "</td>";
          resultStr += "</tr>";
     }
     resultStr += "</table>";
     Response.Write (resultStr);
}

שאלה 6 · הפניה לדף לפי משתני Session (20 נקודות)

באגודת הספורט הארצית עבור מקצה 50 מטר הוגדרו שלושה משתני Session :
Session["age"] מחרוזת — שכבת גיל (ילד / נוער/ בוגר)
Session["style"] מחרוזת — סגנון שחייה (פרפר/ חזה/ חתירה)
Session ["record"] מספר שלם — שיא שחייה בשניות.

כתוב קטע קוד המפנה את השחיין המשתמש באתר לדף המתאים לו, על פי הוראות אלה:

•שחיין בוגר עם שיא מתחת ל- 35 שניות וסגנון שחייה חתירה יופנה לדף freeStyle.aspx
•שחיין נוער עם שיא מתחת ל- 25 שניות כולל יופנה לדף records.aspx
•כל שחיין אחר יופנה לדף olympic.aspx

כתוב קטע קוד המפנה את השחיין המשתמש באתר לדף המתאים לו, על פי ההוראות.

(20 נקודות)
◂ פתרון מלא לשאלה 6

פתרון

תשובה

if (age == "בוגר" && record < 35 && style == "חתירה") → freeStyle.aspx; else if (age == "נוער" && record <= 25) → records.aspx; else → olympic.aspx

מה נדרש: לקרוא את שלושת משתני ה־Session ולהפנות (Response.Redirect) לאחד משלושה דפים.

רעיון:

•ב־Session נשמרים ערכים מטיפוס object, ולכן ממירים: מחרוזת בעזרת ToString(), ומספר שלם בהמרה (int).
•שלושה מקרים שאינם חופפים ← מבנה if / else if / else. המקרה האחרון ("כל שחיין אחר") הוא ה־else.
•"מתחת ל־35 שניות" ← record < 35. "מתחת ל־25 שניות כולל" ← כולל 25, כלומר record <= 25.

פתרון:

C#
string age = Session["age"].ToString();
string style = Session["style"].ToString();
int record = (int)Session["record"];
if (age == "בוגר" && record < 35 && style == "חתירה")
    Response.Redirect("freeStyle.aspx");
else if (age == "נוער" && record <= 25)
    Response.Redirect("records.aspx");
else
    Response.Redirect("olympic.aspx");

בדיקה:

agestylerecordהדף
בוגרחתירה34freeStyle.aspx
בוגרחתירה35olympic.aspx
בוגרפרפר20olympic.aspx
נוערחזה25records.aspx
נוערחתירה24records.aspx
נוערפרפר26olympic.aspx
ילדחתירה10olympic.aspx

טעות נפוצה: להשוות את Session["record"] ישירות למספר (Session["record"] < 35) – זה לא עובר הידור, כי הערך הוא object; צריך המרה ל־int. טעות נוספת: לכתוב שלושה if נפרדים בלי else – אז "כל שחיין אחר" אינו מוגדר נכון.

הקוד המלא
C#
string age = Session["age"].ToString();
string style = Session["style"].ToString();
int record = (int)Session["record"];
if (age == "בוגר" && record < 35 && style == "חתירה")
    Response.Redirect("freeStyle.aspx");
else if (age == "נוער" && record <= 25)
    Response.Redirect("records.aspx");
else
    Response.Redirect("olympic.aspx");

שאלה 7 · מונה לחיצות שאינו עובד – שמירת מצב בין בקשות (20 נקודות)

בדף Default.aspx יש תוכנית שמטרתה להציג בעזרת הפונקציה Count_Page_Load את מספר הפעמים שלחצו על הכפתור – button (המספר גדל ב־ 1 בכל לחיצה). התוכנית רצה אך הפונקציה Count_Page_Load אינה מבצעת את הנדרש.
הסבר מדוע הפונקציה אינה מבצעת את הנדרש.
כתוב את הפונקציה Count_Page_Load מחדש כך שתבצע את הנדרש (אפשר לשנות ולהוסיף שורות קוד בפונקציה).

HTML
<%@ Page Language="C#" %>

<!DOCTYPE html>
<script runat="server">
     public int num;
     protected void Page_Load(object sender, EventArgs e)
     {
          num=0;
       if (Page.IsPostBack)
               num++;
     }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <input type="submit" value="Press here"/>
    </form>
    <div>
        The numbers of clicks is <%=num %>
    </div>
</body>
</html>

הסבר מדוע הפונקציה אינה מבצעת את הנדרש.
כתוב את הפונקציה Count_Page_Load מחדש כך שתבצע את הנדרש (אפשר לשנות ולהוסיף שורות קוד בפונקציה).

(20 נקודות)
◂ פתרון מלא לשאלה 7

פתרון

תשובה

בכל בקשה (כולל כל לחיצה) השרת יוצר עצם דף חדש, ו־num מתאפס ל־0; לכן מוצג 0 בטעינה הראשונה ו־1 אחרי כל לחיצה. יש לשמור את המונה בין הבקשות – ב־Session: בטעינה ראשונה Session["num"] = 0, ובכל postback מגדילים ב־1, ואז num = (int)Session["num"].

מה נדרש: (1) להסביר למה המונה לא גדל; (2) לכתוב מחדש את הפונקציה כך שתספור את הלחיצות.

הסבר: פרוטוקול HTTP הוא חסר מצב (stateless). בכל בקשה לדף – גם בכל לחיצה על הכפתור (postback) – השרת יוצר עצם חדש של הדף, מריץ את Page_Load, שולח את ה־HTML ללקוח ומשמיד את העצם. לכן המשתנה num, שהוא תכונה של עצם הדף, אינו שומר את ערכו בין הבקשות. בנוסף, השורה num=0; מאפסת אותו בכל טעינה. התוצאה: בטעינה הראשונה מוצג 0, ואחרי כל לחיצה מוצג 1 – ולא 2, 3, ...

רעיון לתיקון: לשמור את המונה במקום שנשמר בין בקשות של אותו משתמש – משתנה Session. בטעינה הראשונה (לא postback) מאתחלים ל־0; בכל postback מגדילים ב־1; ובסוף מעתיקים ל־num כדי ש־<%=num %> יציג אותו.

פתרון: (בקוד הדף הפונקציה נקראת Page_Load – זה השם ש־ASP.NET מפעיל אוטומטית בטעינת הדף, ולכן נשאיר אותו.)

C#
    public int num;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack || Session["num"] == null)
            Session["num"] = 0;
        else
            Session["num"] = (int)Session["num"] + 1;
        num = (int)Session["num"];
    }

הבדיקה Session["num"] == null מגינה מקריסה אם ה־Session פג תוקף בין לחיצות (המרה (int) של null נכשלת).

בדיקה: הדמינו טעינה ראשונה ואחריה 3 לחיצות, כשבכל בקשה נוצר עצם דף חדש: הקוד המקורי הציג 0 1 1 1, והקוד המתוקן הציג 0 1 2 3.

הערה: אפשר לשמור את המונה גם ב־ViewState. לא כדאי להשתמש ב־Application – הוא משותף לכל המשתמשים, ואז לחיצות של משתמשים שונים יצטברו יחד.

טעות נפוצה: להשאיר את num=0 ורק להעביר את num++ למקום אחר – כל עוד הערך נשמר רק בתכונה של הדף, הוא נמחק בסוף כל בקשה.

הקוד המלא
C#
    public int num;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack || Session["num"] == null)
            Session["num"] = 0;
        else
            Session["num"] = (int)Session["num"] + 1;
        num = (int)Session["num"];
    }

פרק שלישי

בשאלון זה שלושה פרקים.
עליך לענות על חמש שאלות מתוך שלושת הפרקים. לכל שאלה — 20 נקודות.

שאלה 8 · טופס כניסה – Request.Form, Session והפניות (נכון / לא נכון) (20 נקודות)

בטופס כניסה לאתר כלשהו נדרש המשתמש להכניס שם משתמש וסיסמה לשדות האלה:

HTML
<input type="text" name="id" id="id"/>
<input type="password" name="password" id="password" />

לפניך הפונקציה Direct . הפונקציה מקבלת מחרוזת — connectionString המִתקשרת לבסיס הנתונים של האתר.
בבסיס הנתונים יש טבלה ששמה Registers , ובה העמודה id מטיפוס מחרוזת והעמודה password מטיפוס מחרוזת.
הפונקציה רצה ללא שגיאות.

C#
protected void Direct(string connectionString){
     SqlConnection  connection = new SqlConnection(connectionString);
     SqlCommand  command  = new SqlCommand();
     DataSet ds = new DataSet();
     if (Request.Form["id"] != null)
     {
          command.CommandText = string.Format("Select * From Registers Where id = '{0}'",   Request.Form["id"]);
          command.Connection= connection;
          connection.Open();
          SqlDataAdapter adapter = new SqlDataAdapter(command);
          adapter.Fill(ds);
          connection.Close();
          if (ds.Tables[0].Rows.Count == 0)
          {
               Session["Key"] = 0;
               Response.Redirect("Page1.aspx");
          }
          else if (ds.Tables[0].Rows[0]["password"].Equals(Request.Form["password"]))
       {
               Session["Key"] = 2;
               Response.Redirect("Page2.aspx");
          }
          else
           Session["Key"] = 1;
     }
}

בעבור כל אחד מארבעת ההיגדים שלפניך רשוֹם אם הוא נכון או לא נכון, ונמק.

1.המשתמש לא יופנה לדף Page2.aspx אם הסיסמא שגויה.
2.המשתמש יופנה לדף Page1.aspx גם אם ה Id נכון והסיסמה שגויה.
3.Session["Key"] לא יקבל את הערך 1 כאשר הסיסמה שגויה.
4.Session["Key"] יכול למנוע כניסה ישירה לדף Page2.aspx .
1.

המשתמש לא יופנה לדף Page2.aspx אם הסיסמא שגויה.

2.

המשתמש יופנה לדף Page1.aspx גם אם ה Id נכון והסיסמה שגויה.

3.

Session["Key"] לא יקבל את הערך 1 כאשר הסיסמה שגויה.

4.

Session["Key"] יכול למנוע כניסה ישירה לדף Page2.aspx .

◂ פתרון מלא לשאלה 8

סעיף 1

תשובה

נכון. ההפניה ל־Page2.aspx מתבצעת רק כשהסיסמה במסד שווה לסיסמה שהוקלדה.

מה נדרש: לקבוע אם ייתכן מעבר ל־Page2.aspx עם סיסמה שגויה.

נימוק: Response.Redirect("Page2.aspx") נמצא רק בענף else if (ds.Tables[0].Rows[0]["password"].Equals(Request.Form["password"])), כלומר רק כשהשם קיים בטבלה וגם הסיסמה השמורה שווה לסיסמה שנשלחה. אם הסיסמה שגויה – או שהשם לא קיים (הפניה ל־Page1.aspx), או שמגיעים ל־else האחרון, שבו אין הפניה כלל.

תשובה: ההיגד נכון.

בדיקה: Direct על טבלה עם המשתמשים dana/1234 ו־yossi/abcd:

idpasswordהפניהSession["Key"]
dana1234Page2.aspx2
dana9999אין – נשאר בדף1
moshe1234Page1.aspx0
moshe0000Page1.aspx0
(הטופס לא נשלח)איןלא נקבע

סעיף 2

תשובה

לא נכון. ל־Page1.aspx מופנים רק כשה־id לא נמצא (0 שורות). אם ה־id נכון והסיסמה שגויה – Session["Key"] = 1 ואין הפניה.

נימוק: ההפניה ל־Page1.aspx מתבצעת רק בתנאי ds.Tables[0].Rows.Count == 0, כלומר כשאין בטבלה משתמש עם ה־id שהוקלד. אם ה־id נכון, השאילתה מחזירה שורה אחת, ולכן התנאי לא מתקיים. כשהסיסמה שגויה גם התנאי השני לא מתקיים, ומבוצע ה־else האחרון: Session["Key"] = 1; – בלי שום הפניה. המשתמש נשאר בדף הכניסה.

תשובה: ההיגד לא נכון.

בדיקה: dana עם סיסמה 9999 – אין הפניה ו־Key = 1.

סעיף 3

תשובה

לא נכון. כשה־id קיים והסיסמה שגויה מתבצע ה־else האחרון: Session["Key"] = 1.

נימוק: כאשר ה־id קיים בטבלה והסיסמה שגויה, שני התנאים הראשונים אינם מתקיימים, ומבוצעת השורה Session["Key"] = 1;. כלומר דווקא במקרה של סיסמה שגויה (למשתמש קיים) Key מקבל את הערך 1. (רק אם גם ה־id אינו קיים, Key מקבל 0 – אבל אז הבעיה היא שם המשתמש, לא רק הסיסמה.)

תשובה: ההיגד לא נכון.

בדיקה: dana עם סיסמה 9999 – Key = 1.

סעיף 4

תשובה

נכון. Session["Key"] = 2 נקבע רק אחרי כניסה מוצלחת; בדף Page2.aspx בודקים אותו בטעינה ומפנים החוצה אם הוא אינו 2.

נימוק: "כניסה ישירה" היא הקלדת הכתובת Page2.aspx בדפדפן בלי לעבור דרך טופס הכניסה. הערך 2 נשמר ב־Session["Key"] של המשתמש רק אחרי שם משתמש וסיסמה נכונים, וה־Session נשמר בשרת לכל משתמש בנפרד – המשתמש אינו יכול לשנות אותו. לכן אם בדף Page2.aspx בודקים את הערך בטעינת הדף, אפשר לחסום מי שלא עבר כניסה:

C#
protected void Page_Load(object sender, EventArgs e)
{
    if (Session["Key"] == null || (int)Session["Key"] != 2)
        Response.Redirect("Login.aspx");
}

(Login.aspx – שם הדף שבו נמצא טופס הכניסה; השם לא ניתן בשאלה.)

תשובה: ההיגד נכון.

בדיקה: כניסה ל־Page2 בלי Key ← הפניה ל־Login.aspx; עם Key = 1 ← הפניה ל־Login.aspx; עם Key = 2 ← הדף מוצג.

טעות נפוצה: לחשוב שהמשתנה עצמו "חוסם" – Session רק שומר מידע. החסימה נעשית על ידי קוד הבדיקה בדף Page2.aspx.

הקוד המלא
C#
protected void Page_Load(object sender, EventArgs e)
{
    if (Session["Key"] == null || (int)Session["Key"] != 2)
        Response.Redirect("Login.aspx");
}

שאלה 9 · מונה משתמשים ב־Application והודעת ברוכים הבאים (20 נקודות)

לפניך הפונקציה Page_Load :

C#
protected void Page_Load(object sender, EventArgs e)
{
         if (Session["firstlog"] == null)
         {
              Application.Lock();
              if (Application["myCount"] == null)
                  Application["myCount"] = 0;
              Application["myCount"] = (int)Application["myCount"]+ 1;
              Application.UnLock();
              Session["firstlog"] = "true";
         }
}

הוסף בפונקציה קטע קוד המציג באמצעות העצם (האובייקט Response ) בעבור כל המשתמשים את ההודעה שלפניך:
ברוך הבא. הינך המשתמש ה – x (מספר המשתמש).
לדוגמה,
בעבור המשתמש ה־ 1 תוצג ההודעה: ברוך הבא. הינך המשתמש ה־1 .
בעבור המשתמש ה־ 2 תוצג ההודעה: ברוך הבא. הינך המשתמש ה־2.
וכך הלאה, בפונק' נפלה שגיאה ולכן לא מוצגת ההודעה הנכונה.
כתוב במחברתך את הפונקציה המתוקנת.

הוסף בפונקציה קטע קוד המציג באמצעות העצם Response בעבור כל המשתמשים את ההודעה "ברוך הבא. הינך המשתמש ה – x". כתוב במחברתך את הפונקציה המתוקנת.

(20 נקודות)
◂ פתרון מלא לשאלה 9

פתרון

תשובה

תשובה בסיסית: בתוך הנעילה, אחרי ההגדלה: Response.Write("ברוך הבא. הינך המשתמש ה־" + Application["myCount"] + "."); תשובה מלאה יותר: בתוך הנעילה שומרים את מספר המשתמש ב־Session: Session["userNum"] = (int)Application["myCount"]; ומחוץ ל־if (כך שיוצג בכל טעינה): Response.Write("ברוך הבא. הינך המשתמש ה־" + Session["userNum"] + ".");

מה נדרש: להציג לכל משתמש הודעה עם המספר הסידורי שלו – המשתמש הראשון שנכנס לאתר הוא 1, השני 2 וכן הלאה.

ניתוח הקוד הנתון: Application["myCount"] משותף לכל המשתמשים וסופר כמה משתמשים נכנסו. Session["firstlog"] מבטיח שכל משתמש ייספר פעם אחת בלבד (גם אם ירענן את הדף). אבל הפונקציה אינה כותבת דבר ל־Response, ולכן ההודעה אינה מוצגת.

רעיון:

•אי אפשר פשוט להציג את Application["myCount"] – זה מספר המשתמשים הכולל עד עכשיו. אם משתמש 1 ירענן את הדף אחרי שנכנסו עוד שניים, הוא יראה 3. לכן ברגע שהמשתמש נספר, שומרים את המספר שלו ב־Session שלו (Session["userNum"]).
•את השמירה עושים לפני UnLock – אחרת משתמש אחר עלול להגדיל את המונה בין ההגדלה לבין הקריאה.
•את Response.Write כותבים אחרי ה־if, כדי שההודעה תוצג בכל טעינה של הדף – גם למשתמש שכבר נספר.

פתרון בסיסי (זה מה שהשאלה דורשת בעיקר – להוסיף כתיבה ל־Response עם הערך של המונה, מיד אחרי ההגדלה ועוד לפני UnLock):

C#
protected void Page_Load(object sender, EventArgs e)
{
         if (Session["firstlog"] == null)
         {
              Application.Lock();
              if (Application["myCount"] == null)
                  Application["myCount"] = 0;
              Application["myCount"] = (int)Application["myCount"]+ 1;
              Response.Write("ברוך הבא. הינך המשתמש ה־" + Application["myCount"] + ".");
              Application.UnLock();
              Session["firstlog"] = "true";
         }
}

כך כל משתמש חדש רואה בכניסתו הראשונה את המספר שלו. עם ארבעה ביקורים (משתמש 1, 2, שוב 1, 3) מתקבלות ההודעות "המשתמש ה־1", "המשתמש ה־2", (אין הודעה – משתמש 1 כבר נספר), "המשתמש ה־3". החיסרון: ברענון הדף ההודעה כבר לא מוצגת.

פתרון מלא יותר – שומרים את המספר ב־Session ומציגים בכל טעינה:

C#
protected void Page_Load(object sender, EventArgs e)
{
         if (Session["firstlog"] == null)
         {
              Application.Lock();
              if (Application["myCount"] == null)
                  Application["myCount"] = 0;
              Application["myCount"] = (int)Application["myCount"]+ 1;
              Session["userNum"] = (int)Application["myCount"];
              Application.UnLock();
              Session["firstlog"] = "true";
         }
         Response.Write("ברוך הבא. הינך המשתמש ה־" + Session["userNum"] + ".");
}

בדיקה: הדמינו שלושה משתמשים בסדר הביקורים: 1, 2, 1, 3, 2, 1 (כל אחד עם Session משלו ו־Application משותף). ההודעות שהתקבלו: המשתמש ה־1, ה־2, ה־1, ה־3, ה־2, ה־1 – כל משתמש רואה תמיד את המספר שלו, ו־myCount = 3 בסוף.

טעות נפוצה: לכתוב Response.Write(... + Application["myCount"]) מחוץ ל־if – אז משתמש שחוזר לדף יראה את מספר המשתמשים הכולל ולא את המספר שלו. וגם לקרוא את המונה אחרי UnLock – משתמש אחר עלול להגדיל אותו בינתיים.

הקוד המלא
C#
protected void Page_Load(object sender, EventArgs e)
{
         if (Session["firstlog"] == null)
         {
              Application.Lock();
              if (Application["myCount"] == null)
                  Application["myCount"] = 0;
              Application["myCount"] = (int)Application["myCount"]+ 1;
              Session["userNum"] = (int)Application["myCount"];
              Application.UnLock();
              Session["firstlog"] = "true";
         }
         Response.Write("ברוך הבא. הינך המשתמש ה־" + Session["userNum"] + ".");
}
טבלת התשובות
שאלהסעיףתשובה
1-

SELECT code FROM AllUsers WHERE age = 'נוער' AND record < 20;

2-

ד. Select count(*) from AllUser group by age

3-

טבלה עם מסגרת, בעמודה אחת ו־9 שורות: 11, 22, 33, 44, 55, 66, 77, 88, 99 (כל מספר בשורה משלו).

4-

[1] "mail"
[2] false
[3] mail.indexOf(".co.il") == (mail.length - 6)
[4] mail.charAt(i) == '@'
[5] num == 1

5-

[1] connectionString
[2] SELECT swimmerName, idNumber, city, gender
[3] TableOfSwimmers WHERE style = 'פרפר'
[4] מגדר
[5] DataRow dr in ds.Tables[0].Rows
[6] "idNumber"

6-

if (age == "בוגר" && record < 35 && style == "חתירה") → freeStyle.aspx; else if (age == "נוער" && record <= 25) → records.aspx; else → olympic.aspx

7-

בכל בקשה (כולל כל לחיצה) השרת יוצר עצם דף חדש, ו־num מתאפס ל־0; לכן מוצג 0 בטעינה הראשונה ו־1 אחרי כל לחיצה. יש לשמור את המונה בין הבקשות – ב־Session: בטעינה ראשונה Session["num"] = 0, ובכל postback מגדילים ב־1, ואז num = (int)Session["num"].

81

נכון. ההפניה ל־Page2.aspx מתבצעת רק כשהסיסמה במסד שווה לסיסמה שהוקלדה.

2

לא נכון. ל־Page1.aspx מופנים רק כשה־id לא נמצא (0 שורות). אם ה־id נכון והסיסמה שגויה – Session["Key"] = 1 ואין הפניה.

3

לא נכון. כשה־id קיים והסיסמה שגויה מתבצע ה־else האחרון: Session["Key"] = 1.

4

נכון. Session["Key"] = 2 נקבע רק אחרי כניסה מוצלחת; בדף Page2.aspx בודקים אותו בטעינה ומפנים החוצה אם הוא אינו 2.

9-

תשובה בסיסית: בתוך הנעילה, אחרי ההגדלה: Response.Write("ברוך הבא. הינך המשתמש ה־" + Application["myCount"] + "."); תשובה מלאה יותר: בתוך הנעילה שומרים את מספר המשתמש ב־Session: Session["userNum"] = (int)Application["myCount"]; ומחוץ ל־if (כך שיוצג בכל טעינה): Response.Write("ברוך הבא. הינך המשתמש ה־" + Session["userNum"] + ".");

כל האתר במבט אחד

קיצורי דרך לכל הכלים, הבגרויות והקורסים

GuyAI הוא המורה הפרטי המתקדם בישראל, המבוסס על מודלי הבינה המלאכותית החזקים בעולם בשילוב הידע הפדגוגי של GOOL. המערכת מספקת פתרונות לכל המקצועות ולכל הרמות - מיסודי ועד אקדמיה, וממתמטיקה ועד היסטוריה.