Menu

מחרוזות ב-C++: יסודות std::string, מתודות ומלכודות

איך משתמשים ב-std::string ב-C++: בנייה, חיבור, חיפוש וחיתוך של טקסט בבטחה, ולמה כמעט אף פעם לא תרצו char* גולמי לעבודה אמיתית.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

std::string: טקסט שמנהל את עצמו

בעמוד הקודם ראיתם איך מצביעים חכמים מאפשרים לאובייקט להחזיק במשאב ולנקות אותו אוטומטית. std::string הוא אותו רעיון שמיושם על טקסט: הוא מחזיק חוצץ של תווים, מגדיל אותו כשמוסיפים, ומשחרר אותו כשהמחרוזת יוצאת מהטווח. לעולם לא קוראים ל-new, לא סופרים בתים ולא דואגים לתו null מסיים שחסר.

כדי להשתמש בו, כללו את <string>. המחלקה נמצאת במרחב השמות std.

האופרטור + הזה עושה עבודה של ממש: הוא מקצה חוצץ חדש שגדול מספיק לשני החלקים ומעתיק אותם לתוכו. עם char* גולמי, מצביע פשוט לתוך חוצץ תווים, הייתם משתמשים ב-strcpy וב-strcat ומקווים שהחוצץ שלכם גדול מספיק. std::string מעלים את כל סוג הטעויות הזה.

בנייה וחיבור של מחרוזות

אפשר לשרשר עם +, אבל סוס העבודה להגדלת מחרוזת במקומה הוא +=. הוא מוסיף לחוצץ הקיים במקום לייצר מחרוזת חדשה לגמרי בכל פעם, וזה חשוב בתוך לולאות.

הפתעה נפוצה: + צריך שלפחות אחד האופרנדים כבר יהיה std::string. שני ליטרלים של מחרוזת הם בסך הכול const char*, ולכן "a" + "b" לא מתקמפל: אין operator+ לשני מצביעים גולמיים. הפכו קודם אחד מהם למחרוזת:

string s = "a" + "b";              // error: can't add two const char*
string s = string("a") + "b";      // fine - left side is a std::string
string s = "a"s + "b";             // fine in C++14+, the "s" literal suffix

שימו לב שהצורה האחרונה משתמשת בסיומת s מ-<string> (using namespace std::string_literals;), שהופכת ליטרל ישירות ל-std::string.

גישה לתוך מחרוזת

std::string מתנהג כמו מכולה של char, ולכן אפשר לגשת אליו לפי אינדקס, לעבור עליו בלולאה ולבקש ממנו חלקים.

substr(pos, len) מחזיר מחרוזת חדשה לגמרי שמועתקת מהמקורית, והוא לא משנה את המקור. שימו לב לגבולות: word[10] על מחרוזת בת 5 תווים הוא התנהגות לא מוגדרת: הוא לא יזרוק חריגה, הוא פשוט יקרא זבל. אם רוצים גישה עם בדיקה שזורקת std::out_of_range, השתמשו ב-word.at(10) במקום ב-word[10].

מלכודת קלאסית נוספת: .size() מחזיר טיפוס ללא סימן (size_t). לולאה לאחור כמו for (size_t i = word.size() - 1; i >= 0; --i) לעולם לא מסתיימת, כי i ללא סימן אף פעם לא יכול לרדת מתחת לאפס: הוא מתגלגל למספר עצום. השתמשו באינדקס עם סימן או בנו מחדש את התנאי כשצריך לעבור על מחרוזת בסדר הפוך.

חיפוש והחלפה

find מאתר תת-מחרוזת או תו ומחזיר את אינדקס ההתחלה. כשהיעד לא קיים, הוא מחזיר את הקבוע המיוחד std::string::npos: השוו תמיד מולו, במקום להניח ש-find מחזיר -1.

כדי לשנות טקסט במקומו, replace(pos, len, text) מחליף קטע בתוכן חדש (שיכול להיות באורך שונה), ו-insert/erase מוסיפים או מסירים חלקים:

מחרוזות ומספרים

טקסט ומספרים לא מומרים מעצמם: "42" הוא שלושה תווים, לא המספר השלם 42. הספרייה הסטנדרטית נותנת לכם פונקציות המרה לשני הכיוונים. השתמשו ב-stoi, ב-stod ובחבריהם כדי לפענח טקסט למספרים, וב-to_string לכיוון ההפוך.

שתי מלכודות כאן. הראשונה: stoi("abc") זורק std::invalid_argument, ולכן הגנו על קלט לא אמין עם בלוק try/catch. השנייה: to_string(3.99) נותן את 3.990000 המלא: אם צריך שליטה על דיוק ועיצוב, זו עבודה ל-string streams, ולשם בדיוק אנחנו ממשיכים.

try {
    int n = std::stoi(userInput);
} catch (const std::invalid_argument&) {
    std::cout << "That wasn't a number.\n";
}

הבא בתור: קלט ופלט

כל הזמן הדפסתם מחרוזות עם cout, אבל לקריאה שלהן בחזרה מהמשתמש יש הפתעות משלה: cin >> name עוצר ברווח הראשון, ולכן שורה מלאה כמו "Ada Lovelace" צריכה במקום זאת את std::getline. העמוד הבא מסביר כמו שצריך קלט ופלט ב-C++: אופרטורי stream, קריאת שורות שלמות, ושילוב >> עם getline בלי למעוד על שורות חדשות שנשארו.

שאלות נפוצות

מה ההבדל בין std::string ל-char* ב-C++?

std::string היא מחלקה שמחזיקה בזיכרון שלה ומנהלת אותו, גדלה לפי הצורך ומנקה אחריה אוטומטית. char* הוא בסך הכול מצביע גולמי לתווים עם '\0' מסיים: את החוצץ, את האורך ואת אורך החיים אתם מנהלים בעצמכם. העדיפו std::string כמעט לכל דבר: הוא מסלק קטגוריות שלמות של באגים של גלישת חוצץ ומצביעים תלויים.

איך מקבלים את האורך של מחרוזת ב-C++?

קראו ל-.size() או לשם החלופי שלו .length() על std::string: name.size() מחזיר את מספר התווים כ-size_t. שניהם מחזירים את אותו ערך, ו-size() הוא הסגנון הנפוץ יותר כי הוא תואם לכל שאר המכולות ב-STL.

איך ממירים מחרוזת למספר ב-C++?

השתמשו ב-std::stoi עבור int, ב-std::stod עבור double, ובדומים להם (stol, stof). דוגמה: int n = std::stoi("42");. הם זורקים std::invalid_argument אם הטקסט אינו מספר, ולכן עטפו אותם ב-try/catch כשהקלט אינו אמין.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל