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

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

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

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

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

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

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

כתוב במחברת הבחינה בלבד. רשוֹם "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.

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

בהצלחה!

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

פרק ראשון

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

שאלה 1 · שאילתת SQL עם LIKE ו־OR (20 נקודות)

במסד נתונים של בית ספר יש טבלה ששמה AllStudents , ובה נתוני התלמידים.
בטבלה יש 4 עמודות:
firstName (שם פרטי), lastName (שם משפחה), age (גיל) ו־ phoneNumber (מספר טלפון).
כתוב שאילתה בשפת SQL המאחזרת את מספרי הטלפון של התלמידים שהשם הפרטי שלהם מתחיל באות "A" או ששם המשפחה שלהם מתחיל באות "B".

כתוב שאילתה בשפת SQL המאחזרת את מספרי הטלפון של התלמידים שהשם הפרטי שלהם מתחיל באות "A" או ששם המשפחה שלהם מתחיל באות "B".

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

פתרון

תשובה

SELECT phoneNumber FROM AllStudents WHERE firstName LIKE 'A%' OR lastName LIKE 'B%';

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

רעיון:

•בוחרים רק את העמודה המבוקשת: SELECT phoneNumber.
•"מתחיל באות A" נכתב בעזרת LIKE ותבנית: 'A%' – הסימן % מייצג רצף כלשהו של תווים (גם ריק) אחרי ה־A.
•"או" בין התנאים – האופרטור OR: מספיק שאחד התנאים יתקיים.

פתרון:

SQL
SELECT phoneNumber
FROM AllStudents
WHERE firstName LIKE 'A%' OR lastName LIKE 'B%';

בדיקה: טבלה עם 5 תלמידים:

firstNamelastNamephoneNumberנבחר?
AviCohen050-1111111כן (A)
DanaBarak050-2222222כן (B)
AmitBen-David050-3333333כן (שני התנאים – מופיע פעם אחת)
MosheLevi050-4444444לא
BellaAbutbul050-5555555לא (B בשם הפרטי ו־A בשם המשפחה – הפוך מהנדרש)

השאילתה החזירה בדיוק שלושה מספרים: 050-1111111, 050-2222222, 050-3333333.

הערה על הניב: ב־SQL Server התו הכללי הוא %. במסד Access (בתוך Access עצמו) התו הכללי הוא *: LIKE 'A*'. בשאילתה שנשלחת מקוד ASP.NET עם ADO.NET משתמשים ב־%. ב־SQL Server (בהגדרת ברירת המחדל) ההשוואה ב־LIKE אינה תלויה בגודל האות, ולכן גם שם כמו avi ייבחר.

טעות נפוצה: לכתוב AND במקום OR – אז יוחזרו רק תלמידים שמקיימים את שני התנאים יחד. טעות נוספת: firstName = 'A' – זו השוואה לשם שהוא בדיוק האות A, ולא "מתחיל ב־A".

שאלה 2 · העצם Application (20 נקודות)

איזה מן המשפטים שלפניך נכון בנוגע לעצם (יישום) Application ?
העתק למחברתך את התשובה הנכונה מבין האפשרויות א–ד שלפניך.

א. Application נועד לשמור על נתונים של משתמש מסוים.
ב. Application שומר על הנתונים כל עוד היישום פתוח אצל משתמש אחד לפחות.
ג. Application נועד לשמור על נתונים במצבים שבהם השרת נופל.
ד. Application מאפשר שליחה ישירה של נתונים ללא שימוש ב־ form .

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

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

פתרון

תשובה

ב. Application שומר על הנתונים כל עוד היישום פתוח אצל משתמש אחד לפחות.

מה נדרש: לבחור את המשפט הנכון על העצם Application.

רעיון: בשרת ASP.NET יש שני "מחסנים" עיקריים לשמירת נתונים בין בקשות:

•Session – נפרד לכל משתמש (לכל גלישה). מתאים לנתונים אישיים: שם המשתמש המחובר, סל קניות.
•Application – אחד ומשותף לכל המשתמשים של האתר. הוא נוצר כשהיישום (האתר) עולה בשרת, וקיים כל עוד היישום פועל. מתאים לנתונים משותפים: מונה מבקרים, מספר המשתמשים המחוברים.

הנתונים שב־Application נשמרים בזיכרון של השרת.

בדיקת האפשרויות:

אפשרותנכון?הסבר
אלאנתונים של משתמש מסוים נשמרים ב־Session. Application משותף לכולם.
בכןApplication הוא משותף ונשאר בחיים כל עוד היישום פעיל בשרת – הוא לא נמחק כשמשתמש מסוים עוזב, כל עוד האתר "חי" (יש בו משתמשים). בשרת IIS, אם אין באתר משתמשים זמן מה, היישום נסגר והנתונים שב־Application נמחקים.
גלאApplication נשמר בזיכרון השרת; אם השרת נופל או מופעל מחדש – הנתונים אובדים. לשמירה קבועה משתמשים במסד נתונים.
דלאApplication הוא אחסון בצד השרת; הוא לא שולח נתונים מהדפדפן לשרת. נתונים מהמשתמש נשלחים בטופס (form) או בכתובת (QueryString).

תשובה: ב.

הערה: בדיוק רב יותר – Application קיים כל עוד היישום פועל בשרת (מרגע עלייתו ועד שהוא נסגר או מופעל מחדש), והוא אינו תלוי במשתמש מסוים. מבין ארבע האפשרויות, ב היא היחידה שמתארת נכון את המשותפוּת שלו ואת אורך חייו.

טעות נפוצה: לבלבל בין Application ל־Session (אפשרות א).

שאלה 3 · פלט HTML של לולאה – טבלה (20 נקודות)

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

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

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

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

פתרון

תשובה

טבלה עם מסגרת, עמודה אחת ו־7 שורות: 7, 14, 21, 28, 35, 42, 49 (כל מספר בשורה משלו).

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

רעיון: הלולאה עוברת על i מ־1 עד 50. רק כאשר i % 7 == 0 (i מתחלק ב־7) נוספת למחרוזת שורה חדשה (<tr>) עם תא אחד (<td>) שבו הערך של i. לפני הלולאה נפתחת טבלה עם מסגרת (border = '1') ואחריה הטבלה נסגרת.

מעקב: המספרים בין 1 ל־50 שמתחלקים ב־7:

ii % 7נוסף לטבלה
1–6≠ 0–
70<tr><td>7</td></tr>
140<tr><td>14</td></tr>
210<tr><td>21</td></tr>
280<tr><td>28</td></tr>
350<tr><td>35</td></tr>
420<tr><td>42</td></tr>
490<tr><td>49</td></tr>
501– (והלולאה מסתיימת)

הפלט שנשלח לדפדפן:

HTML
<table border = '1'><tr><td>7</td></tr><tr><td>14</td></tr><tr><td>21</td></tr><tr><td>28</td></tr><tr><td>35</td></tr><tr><td>42</td></tr><tr><td>49</td></tr></table>

מה יוצג בדפדפן: טבלה עם קווי מסגרת, עמודה אחת ו־7 שורות, בכל שורה מספר אחד:

+----+
| 7  |
+----+
| 14 |
+----+
| 21 |
+----+
| 28 |
+----+
| 35 |
+----+
| 42 |
+----+
| 49 |
+----+

טעות נפוצה: לצייר את כל המספרים באותה שורה – אבל <tr> נפתח מחדש בכל מספר, ולכן כל מספר בשורה נפרדת. טעות נוספת: לכלול את 50 או את 0 – הלולאה מתחילה ב־1, ו־50 אינו מתחלק ב־7.

פרק שני

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

שאלה 4 · בדיקת סיסמה חזקה ב־JavaScript (20 נקודות)

כדי שסיסמה תיחשב לסיסמה חזקה עליה לעמוד בדרישות האלה:

•הסיסמה חייבת להכיל 8 תווים לפחות.
•על התווים בסיסמה להיות אותיות באנגלית וספרות בלבד.
•הסיסמה חייבת להכיל ספרה אחת לפחות.
•הסיסמה חייבת להכיל אות קטנה אחת לפחות.
•הסיסמה חייבת להכיל אות גדולה אחת לפחות.

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

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

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

JavaScript
function isStrongPassword ()
{
    var password = document.getElementById(___[1]___).value;
    if (password.length < 8)
        return false;
    var lowerCaseLetters = 0;
    var upperCaseLetters = 0;
    var digits = 0;
    for (var i = 0; i < password.length; i++)
    {
        if (___[2]___)
            digits ++;
        else if (password.charAt(i) >= 'a' && password.charAt(i) <= 'z')
            ___[3]___
        else if  (___[4]___)
            upperCaseLetters ++;
        else
            return ___[5]___;
    }
    return digits > 0 && lowerCaseLetters > 0 && upperCaseLetters > 0;
}

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

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

פתרון

תשובה

[1] "password" [2] password.charAt(i) >= '0' && password.charAt(i) <= '9' [3] lowerCaseLetters ++; [4] password.charAt(i) >= 'A' && password.charAt(i) <= 'Z' [5] false

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

רעיון:

•[1] את השדה מוצאים לפי ה־id שלו בטופס: id="password". הפרמטר של getElementById הוא מחרוזת, ולכן צריך מירכאות.
•בלולאה עוברים על כל תו ומסווגים אותו לאחד מארבעה סוגים: ספרה, אות קטנה, אות גדולה, או "תו אחר".
•[2] ספרה היא תו בין '0' ל־'9' (השוואת תווים לפי הסדר שלהם בטבלת התווים).
•[3] אם התו הוא אות קטנה – מגדילים את המונה lowerCaseLetters (זו פקודה שלמה, כולל ;).
•[4] אות גדולה היא תו בין 'A' ל־'Z'.
•[5] כל תו אחר (רווח, סימן, אות עברית...) הופך את הסיסמה ללא חוקית מיד – מחזירים false.
•בסוף הפונקציה (כבר מודפס) נבדק שיש לפחות ספרה אחת, אות קטנה אחת ואות גדולה אחת.

פתרון:

מספרהביטוי החסר
[1]"password"
[2]password.charAt(i) >= '0' && password.charAt(i) <= '9'
[3]lowerCaseLetters ++;
[4]password.charAt(i) >= 'A' && password.charAt(i) <= 'Z'
[5]false

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

JavaScript
function isStrongPassword ()
{
    var password = document.getElementById("password").value;
    if (password.length < 8)
        return false;
    var lowerCaseLetters = 0;
    var upperCaseLetters = 0;
    var digits = 0;
    for (var i = 0; i < password.length; i++)
    {
        if (password.charAt(i) >= '0' && password.charAt(i) <= '9')
            digits ++;
        else if (password.charAt(i) >= 'a' && password.charAt(i) <= 'z')
            lowerCaseLetters ++;
        else if (password.charAt(i) >= 'A' && password.charAt(i) <= 'Z')
            upperCaseLetters ++;
        else
            return false;
    }
    return digits > 0 && lowerCaseLetters > 0 && upperCaseLetters > 0;
}

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

סיסמהתוצאהסיבה
Abcdefg1true8 תווים, יש ספרה, קטנה וגדולה
Abcdef1falseרק 7 תווים
abcdefg1falseאין אות גדולה
ABCDEFG1falseאין אות קטנה
Abcdefghfalseאין ספרה
Abcd efg1falseרווח – תו אסור ([5])
Abcdefg1!false! – תו אסור ([5])
12345678aBtrue

טעות נפוצה: לכתוב ב־[1] password בלי מירכאות (זה שם של משתנה שעדיין לא קיים, ולא ה־id של השדה). טעות נוספת: ב־[2] להשוות למספרים >= 0 && <= 9 במקום לתווים '0' ו־'9'.

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

במסד נתונים של מרפאה וטרינרית יש טבלה ששמה TableOfDogs , ובה נתוני הכלבים המטופלים במרפאה.
העמודות בטבלה הן: dogName (שם הכלב) מטיפוס מחרוזת, age (גיל הכלב) מטיפוס שלם, gender (מין הכלב — זכר או נקבה) מטיפוס מחרוזת, breed (גזע הכלב) מטיפוס מחרוזת ו־ idNumber (מספר הזיהוי של הכלב) מטיפוס שלם.
המרפאה רוצה להציג את הנתונים של כלבים ממין זכר בלבד, בעזרת הפונקציה PrintTable .
הפונקציה מקבלת מחרוזת התחברות למסד הנתונים — connectionString , ומציגה בטבלה את השם, הגיל, הגזע ומספר הזיהוי של כל אחד מן הכלבים שממין זכר.
דוגמה לטבלה שהפונקציה מציגה:

שם הכלבגילגזעמספר זיהוי
שוקו6אמסטף1253
ספייקי8פינצ'ר5436
לאסי5קולי8564

הפונקציה 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["dogName"] + "</td>";
        resultStr += "<td>" + dr["age"] + "</td>";
        resultStr += "<td>" + dr["breed"] + "</td>";
        resultStr += "<td>" + dr[___[6]___] + "</td>";
        resultStr += "</tr>";
    }
    resultStr += "</table>";
    Response.Write (resultStr);
}

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

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

פתרון

תשובה

[1] connectionString [2] SELECT dogName, age, breed, idNumber [3] TableOfDogs WHERE gender = 'זכר' [4] גיל [5] DataRow dr in ds.Tables[0].Rows [6] "idNumber"

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

רעיון:

•[1] יוצרים חיבור למסד בעזרת מחרוזת ההתחברות שהפונקציה קיבלה כפרמטר.
•[2]+[3] השאילתה: בוחרים את ארבע העמודות שמציגים, מהטבלה TableOfDogs, ומסננים רק את הכלבים שבהם gender הוא "זכר". שימו לב: התנאי WHERE בא אחרי שם הטבלה, ולכן הוא חלק מ־[3].
•[4] שורת הכותרות של הטבלה: העמודה השנייה היא "גיל" (לפי הדוגמה ולפי dr["age"] שבתא השני).
•[5] עוברים בלולאה על כל השורות שהתקבלו: ds.Tables[0].Rows, כשכל שורה היא DataRow בשם dr (השם dr משמש בגוף הלולאה).
•[6] בתא הרביעי מציגים את מספר הזיהוי – העמודה "idNumber" (שם עמודה הוא מחרוזת, ולכן במירכאות).

פתרון:

מספרהביטוי החסר
[1]connectionString
[2]SELECT dogName, age, breed, idNumber
[3]TableOfDogs WHERE gender = 'זכר'
[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 dogName, age, breed, idNumber FROM TableOfDogs WHERE gender = 'זכר' ";
    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["dogName"] + "</td>";
        resultStr += "<td>" + dr["age"] + "</td>";
        resultStr += "<td>" + dr["breed"] + "</td>";
        resultStr += "<td>" + dr["idNumber"] + "</td>";
        resultStr += "</tr>";
    }
    resultStr += "</table>";
    Response.Write (resultStr);
}

בדיקה: טבלה עם 5 כלבים (3 זכרים מהדוגמה ו־2 נקבות). הפונקציה הציגה בדיוק את שלוש השורות שבדוגמה (שוקו, ספייקי, לאסי) ולא הציגה את הנקבות:

HTML
<tr><td>שוקו</td><td>6</td><td>אמסטף</td><td>1253</td></tr>
<tr><td>ספייקי</td><td>8</td><td>פינצ'ר</td><td>5436</td></tr>
<tr><td>לאסי</td><td>5</td><td>קולי</td><td>8564</td></tr>

הנחה: הערך שנשמר בעמודה gender לכלב זכר הוא המחרוזת 'זכר' (השאלה אומרת "זכר או נקבה").

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

הקוד המלא
C#
protected void PrintTable(string connectionString)
{
    DataSet ds = new DataSet();
    SqlConnection connection = new SqlConnection(connectionString);
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = " SELECT dogName, age, breed, idNumber FROM TableOfDogs WHERE gender = 'זכר' ";
    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["dogName"] + "</td>";
        resultStr += "<td>" + dr["age"] + "</td>";
        resultStr += "<td>" + dr["breed"] + "</td>";
        resultStr += "<td>" + dr["idNumber"] + "</td>";
        resultStr += "</tr>";
    }
    resultStr += "</table>";
    Response.Write (resultStr);
}

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

באתר משרד הבריאות הוגדרו שלושה משתני Session :
Session["age"] מספר שלם — גיל האדם, Session["wasSick"] בוליאני — היה חולה,
ו־ Session["wasVaccinated"] בוליאני — חוּסן.
כתוב קטע קוד המפנה את האדם המשתמש באתר לדף המתאים לו, על פי הוראות אלה:

•אדם בן 16 ומעלה שלא היה חולה ולא חוּסן יופנה לדף getVaccine.aspx .
•אדם שהיה חולה או חוּסן יופנה לדף greenCard.aspx .
•כל אדם אחר יופנה לדף covidInstructions.aspx .

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

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

פתרון

תשובה

if (wasSick || wasVaccinated) → greenCard.aspx; else if (age >= 16) → getVaccine.aspx; else → covidInstructions.aspx

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

רעיון:

•ב־Session הערכים שמורים כ־object, ולכן צריך המרה (casting) לטיפוס המקורי: (int) לגיל, (bool) לשני הדגלים.
•סדר הבדיקות:
1.אם היה חולה או חוסן – greenCard.aspx (בלי קשר לגיל).
2.אחרת (כלומר לא היה חולה וגם לא חוסן) – אם הגיל 16 ומעלה (>= 16) – getVaccine.aspx.
3.אחרת – covidInstructions.aspx (מתחת לגיל 16, לא חולה ולא מחוסן).
•בזכות ה־else if, בבדיקה השנייה כבר ידוע שהאדם לא היה חולה ולא חוסן, ולכן אין צורך לבדוק זאת שוב.

פתרון:

C#
int age = (int)Session["age"];
bool wasSick = (bool)Session["wasSick"];
bool wasVaccinated = (bool)Session["wasVaccinated"];
if (wasSick || wasVaccinated)
    Response.Redirect("greenCard.aspx");
else if (age >= 16)
    Response.Redirect("getVaccine.aspx");
else
    Response.Redirect("covidInstructions.aspx");

בדיקה: כל הצירופים של גיל (10, 15, 16, 40) × היה חולה (כן/לא) × חוסן (כן/לא) – 16 מקרים. לדוגמה:

agewasSickwasVaccinatedהדף
40falsefalsegetVaccine.aspx
16falsefalsegetVaccine.aspx (16 כלול – "בן 16 ומעלה")
15falsefalsecovidInstructions.aspx
10falsetruegreenCard.aspx
40truefalsegreenCard.aspx

כל 16 המקרים התאימו להוראות.

טעות נפוצה: לכתוב age > 16 – אז בן 16 בדיוק יופנה בטעות ל־covidInstructions.aspx. טעות נוספת: להשוות את ערכי ה־Session בלי המרה, למשל Session["age"] >= 16 – זו שגיאת קומפילציה, כי object אינו מספר.

הקוד המלא
C#
int age = (int)Session["age"];
bool wasSick = (bool)Session["wasSick"];
bool wasVaccinated = (bool)Session["wasVaccinated"];
if (wasSick || wasVaccinated)
    Response.Redirect("greenCard.aspx");
else if (age >= 16)
    Response.Redirect("getVaccine.aspx");
else
    Response.Redirect("covidInstructions.aspx");

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

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

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

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

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
    <form id="form1" action="Default.aspx" method="get" runat="server">
        <input name="button" type="submit" value="press here" />
    </form>
    <div>
        The number of clicks is <% =num %>
    </div>
</body>
</html>

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

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

פתרון

תשובה

בכל בקשה לשרת נוצר עצם דף חדש ו־num מתחיל שוב מ־0, ולכן אחרי כל לחיצה מוצג 1. יש לשמור את המונה ב־Session (או ViewState) בין הבקשות.

מה נדרש: (1) להסביר למה המונה לא עולה; (2) לתקן את Page_Load.

הסבר – למה זה לא עובד:

•פרוטוקול HTTP הוא חסר מצב (stateless): כל לחיצה על הכפתור שולחת בקשה חדשה לשרת, והשרת יוצר בשבילה עצם חדש של הדף, מריץ אותו, שולח את ה־HTML לדפדפן – ומשמיד את העצם.
•לכן המשתנה num (שדה של הדף) אינו זוכר את ערכו מהבקשה הקודמת: בכל בקשה הוא נוצר מחדש עם הערך 0.
•בטעינה הראשונה IsPostBack הוא false ⇒ num=0. בכל לחיצה IsPostBack הוא true ⇒ num++ מגדיל את 0 ל־1.
•התוצאה: אחרי כל לחיצה יוצג תמיד "The number of clicks is 1" – המספר לא ממשיך לגדול.

רעיון לתיקון: לשמור את המונה במקום שנשמר בין בקשות של אותו משתמש – ב־Session. בכל לחיצה קוראים את הערך הקודם מה־Session, מגדילים ב־1, ושומרים בחזרה. ב־num נשאר הערך להצגה (<% =num %>).

פתרון:

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

בדיקה: סימלצנו רצף בקשות (כל בקשה = עצם דף חדש, אותו Session): טעינה, 3 לחיצות, טעינה מחדש, לחיצה.

בקשהIsPostBackהקוד המקורי מציגהקוד המתוקן מציג
טעינה ראשונהfalse00
לחיצה 1true11
לחיצה 2true12
לחיצה 3true13
טעינה חדשה (לא לחיצה)false00
לחיצהtrue11

הערות:

•אפשר לשמור את המונה גם ב־ViewState["num"] (נשמר בתוך הדף עצמו בין הלחיצות) – גם זה פתרון נכון.
•אין להשתמש ב־Application: הוא משותף לכל המשתמשים, ואז לחיצות של משתמשים שונים יתחברו למונה אחד.
•הטופס נשלח בשיטה get, אבל זה לא משנה את ההסבר: טופס עם runat="server" מוסיף לדף שדות נסתרים (כמו __VIEWSTATE), ובשיטה get הם נשלחים בכתובת (QueryString). לפיהם השרת מזהה שהבקשה היא PostBack, ולכן IsPostBack אכן true בלחיצות.

טעות נפוצה: להחליף את public int num; ב־public int num = 0; או להעביר את האתחול – זה לא עוזר, כי הבעיה היא שהעצם כולו נוצר מחדש בכל בקשה.

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

פרק שלישי

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

שאלה 8 · טופס כניסה – 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 אם הסיסמה שגויה.
3.Session["Key"] יקבל את הערך 1 כאשר הסיסמה נכונה.
4.Session["Key"] יכול למנוע כניסה ישירה לדף Page2.aspx .

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

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

פתרון

תשובה

1 – לא נכון; 2 – לא נכון; 3 – לא נכון; 4 – נכון.

מה נדרש: להבין מה הפונקציה עושה בכל מקרה, ולקבוע לכל היגד אם הוא נכון.

מה הפונקציה עושה: אם נשלח טופס (Request.Form["id"] != null), היא שולפת מהטבלה Registers את השורה שבה id שווה לשם המשתמש שהוקלד, ואז:

מקרהSession["Key"]הפניה
שם המשתמש לא קיים בטבלה (0 שורות)0Page1.aspx
המשתמש קיים והסיסמה נכונה2Page2.aspx
המשתמש קיים והסיסמה שגויה1אין הפניה – נשארים בדף

ההיגדים:

1.לא נכון. כאשר שם המשתמש קיים בטבלה והסיסמה שהוקלדה שווה לסיסמה שבטבלה, התנאי ds.Tables[0].Rows[0]["password"].Equals(Request.Form["password"]) מתקיים, והמשתמש מופנה ל־Page2.aspx. (הערך Rows[0]["password"] הוא מטיפוס object, אבל בתוכו יש מחרוזת, ולכן Equals משווה את תוכן המחרוזות.)
2.לא נכון. ל־Page1.aspx מופנים רק כאשר שם המשתמש לא נמצא בטבלה (Rows.Count == 0). אם הסיסמה שגויה (והמשתמש קיים) – מתבצע ה־else: Session["Key"] = 1 ואין שום הפניה; המשתמש נשאר בדף הנוכחי.
3.לא נכון. כאשר הסיסמה נכונה Session["Key"] מקבל את הערך 2. הערך 1 ניתן כשהסיסמה שגויה.
4.נכון. אפשר לגלוש ל־Page2.aspx ישירות (להקליד את הכתובת בדפדפן) בלי לעבור בטופס הכניסה. אבל Session["Key"] מקבל 2 רק אחרי כניסה מוצלחת, וה־Session נשמר לכל משתמש בנפרד. לכן ב־Page2.aspx אפשר לבדוק אותו ולהחזיר כל מי שלא נכנס כראוי, למשל (שם דף הכניסה – Login.aspx – הוא לדוגמה):
C#
protected void Page_Load(object sender, EventArgs e)
{
    if (Session["Key"] == null || (int)Session["Key"] != 2)
        Response.Redirect("Login.aspx");
}

בדיקה: הפונקציה Direct כפי שהיא מודפסת, עם טבלת Registers (dana / Dana2021):

idpasswordתוצאה
danaDana2021הפניה ל־Page2.aspx, Key=2
danawrongנשאר בדף, Key=1
noonexהפניה ל־Page1.aspx, Key=0
(טופס לא נשלח)נשאר בדף, Key לא נקבע

ובדיקת השמירה על Page2.aspx: בלי Key, או עם Key=1 – הפניה ל־Login.aspx; עם Key=2 – הכניסה מותרת.

טעות נפוצה: לחשוב שהמשתמש מופנה ל־Page1.aspx כשהסיסמה שגויה – כדאי לעקוב בזהירות אחרי שלושת הענפים של ה־if.

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

שאלה 9 · הודעה לכל משתמש אלפי – Application ו־Session (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 בעבור המשתמש ה־ 1,000 , ה־ 2,000 , ה־ 3,000 וכן הלאה (כל 1,000 משתמשים נוספים) את ההודעה שלפניך: ברוך הבא המשתמש ה־ x (x הוא מספר המשתמש).
לדוגמה, בעבור המשתמש ה־ 1,000 תוצג ההודעה: ברוך הבא המשתמש ה־ 1,000 .
כתוב במחברתך את הפונקציה המתוקנת.

כתוב במחברתך את הפונקציה המתוקנת.

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

פתרון

תשובה

בתוך ה־Lock שומרים int x = (int)Application["myCount"]; ואחרי ה־UnLock: if (x % 1000 == 0) Response.Write("ברוך הבא המשתמש ה־ " + x);

מה נדרש: להוסיף לפונקציה הצגת הודעה למשתמש שמספרו הסידורי הוא כפולה של 1,000.

מה הפונקציה עושה כבר: Application["myCount"] הוא מונה משותף לכל המשתמשים. Session["firstlog"] הוא סימון אישי לכל משתמש – בפעם הראשונה שהמשתמש מגיע הוא null, ואז המונה גדל ב־1 והמשתמש מסומן, כך שכל משתמש נספר פעם אחת בלבד. לכן הערך של המונה מיד אחרי ההגדלה הוא מספר המשתמש הנוכחי.

רעיון:

•מיד אחרי ההגדלה, עוד לפני UnLock, שומרים את ערך המונה במשתנה מקומי x. כך מובטח ש־x הוא המספר של המשתמש הזה – משתמש אחר לא יכול לשנות את המונה בין ההגדלה לקריאה.
•המשתמש הוא "אלפי" אם x % 1000 == 0 (1000, 2000, 3000, ...).
•במקרה כזה כותבים את ההודעה עם Response.Write.
•הבדיקה נמצאת בתוך ה־if של firstlog, כי רק שם המשתמש מקבל את מספרו.

פתרון:

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;
        int x = (int)Application["myCount"];
        Application.UnLock();
        Session["firstlog"] = "true";
        if (x % 1000 == 0)
            Response.Write("ברוך הבא המשתמש ה־ " + x);
    }
}

בדיקה: סימלצנו 3,500 משתמשים שונים, וכל אחד טוען את הדף פעמיים. ההודעה הוצגה בדיוק 3 פעמים – למשתמשים 1000, 2000 ו־3000, בטעינה הראשונה שלהם – ובסוף המונה היה 3500 (הטעינה השנייה של כל משתמש לא נספרה).

הערות:

•ההודעה מוצגת למשתמש ברגע שהוא נספר (בכניסתו הראשונה). אם רוצים להציג אותה בכל טעינה של הדף אצל אותו משתמש, אפשר לשמור את x ב־Session (למשל Session["userNum"] = x;) ולבדוק אותו מחוץ ל־if.
•הפסיק במספר 1,000 בהודעה הוא רק עיצוב הדפסה; הקוד מציג 1000.

טעות נפוצה: לבדוק (int)Application["myCount"] % 1000 == 0 אחרי UnLock – בין השחרור לבדיקה משתמש אחר עלול להגדיל את המונה, וההודעה תוצג למשתמש הלא נכון (או לא תוצג כלל). טעות נוספת: לשים את הבדיקה מחוץ ל־if – אז משתמש שמרענן את הדף יקבל את ההודעה של מישהו אחר.

הקוד המלא
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;
        int x = (int)Application["myCount"];
        Application.UnLock();
        Session["firstlog"] = "true";
        if (x % 1000 == 0)
            Response.Write("ברוך הבא המשתמש ה־ " + x);
    }
}
טבלת התשובות
שאלהסעיףתשובה
1-

SELECT phoneNumber FROM AllStudents WHERE firstName LIKE 'A%' OR lastName LIKE 'B%';

2-

ב. Application שומר על הנתונים כל עוד היישום פתוח אצל משתמש אחד לפחות.

3-

טבלה עם מסגרת, עמודה אחת ו־7 שורות: 7, 14, 21, 28, 35, 42, 49 (כל מספר בשורה משלו).

4-

[1] "password" [2] password.charAt(i) >= '0' && password.charAt(i) <= '9' [3] lowerCaseLetters ++; [4] password.charAt(i) >= 'A' && password.charAt(i) <= 'Z' [5] false

5-

[1] connectionString [2] SELECT dogName, age, breed, idNumber [3] TableOfDogs WHERE gender = 'זכר' [4] גיל [5] DataRow dr in ds.Tables[0].Rows [6] "idNumber"

6-

if (wasSick || wasVaccinated) → greenCard.aspx; else if (age >= 16) → getVaccine.aspx; else → covidInstructions.aspx

7-

בכל בקשה לשרת נוצר עצם דף חדש ו־num מתחיל שוב מ־0, ולכן אחרי כל לחיצה מוצג 1. יש לשמור את המונה ב־Session (או ViewState) בין הבקשות.

8-

1 – לא נכון; 2 – לא נכון; 3 – לא נכון; 4 – נכון.

9-

בתוך ה־Lock שומרים int x = (int)Application["myCount"]; ואחרי ה־UnLock: if (x % 1000 == 0) Response.Write("ברוך הבא המשתמש ה־ " + x);

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

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

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