אובייקטי stream
בשפת C++
משתמשים באובייקטי Stream
על מנת לקרוא ולכתוב מידע לקבצים.
כל מתכנת יודע את הדוגמא הקלאסית לכתיבה לתצוגה:
1 | std::cout << "Hello world\n"; |
בעזרת סטרימים אנחנו יכולים לקרוא ולכתוב מידע בעזרת האופרטור <<
או >>
.
אובייקטי fstream
האובייקטים מחולקים ל-input
ו-output
.
ולכן יש 3 אובייקטים:
std::fstream
.std::ifstream
.std::ifstream
.
בכללי יש לנו הרבה אובייקטי IO
ממליץ לקרוא על האדרים :
https://en.cppreference.com/w/cpp/io
איך הסרטימים בנויים?
מאחורי הקלעים הם משתמשים במחלקה אשר מממשת את הצורה הכי בסיסית בלקרוא ולכתוב מידע.
ה-std::filebuf
.
הבאפר הנ”ל מממש את פונקציות הכתיבה והקריאה וההזזה של המצביע לקובץ בסטרים.
האובייקט std::fstream
מממש גם כתיבה וגם קריאה.
וכן שני האובייקטים הנותרים מממשים כתיבה וקריאה בלבד בהתאמה: std::ofstream
, std::ifstream
.
כל פתיחה של קובץ מאלצת אותנו לבחור כיצד יש לפתוח את הקובץ, על פי איזה מצב.
המצב מתואר כאן:
https://en.cppreference.com/w/cpp/io/ios_base/openmode
מצב | הסבר |
---|---|
app | לפני כתיבה מחפש את הנקודה האחרונה בקובץ כדי לוודא שכותבים לסוף הקובץ ולא לאמצע |
binary | פותח קובץ בינארי |
in | פותח לכתיבה |
out | פותח לקריאה |
trunc | מוחק את התוכן של הסטרים לפני פתיחה |
ate | מחפש את סוף הטרים בפתיחה |
noreplace | לא מוחק קובץ קיים, אם קיים תיווצר שגיאה |
אז מהי הבעיה הנפוצה בטיפול בקבצים בינאריים?
דרך נפוצה לקרוא קובץ:
1 | std::fstream file{ "file.bin", std::fstream::in }; |
שימו לב לפי הטבלה צריך לשים פרמטר הנקרא binary
אך בדוגמת הקוד הזו - שכחנו!
תוכן הקובץ:
1 | 01 03 04 00 02 04 05 00 0D 0A 03 04 00 |
נניח שהפרוטוקול שלנו אומר שכל 4 בתים זה מוצר.
הבייט הראשון הוא המזהה שלו,
הבייט השני הכמות,
הבייט השלישי המחיר,
הבייט הרביעי אם חסר מלאי.
שימו לב לקובץ שהוא מכיל 0D 0A
בתור מזהה וכמות.
ב-ASCII
התווים האלו אומרים \r\n
שזה:
1 | \r - Carriage Return |
קבצים טקסטואליים מכילים שורות אשר מסתיימות בתווים הנ”ל ואף פרוטוקולים טקסטואליים מגדירים את השימוש בתווים האלו לחציצה בין תווים.
למשל בפרוטוקול Http
.
הצרה כאן, שאם נשכח לפתוח את הקובץ בצורה בינארית או להתייחס למידע כבינארי הקוד ייחשוב שאלו תווים של שורה חדשה.
נוכל בתמונה הבאה לראות איך הבאפר נראה מצד שמאל כאשר אנחנו פותחים אותו כטקסט ומצד ימין כאשר אנחנו מוסיפים הגדרה בינארית:
כדי לפתוח אותו באופן בינארי נוסיף את ההגדרה המתאימה לבנאי:
1 | std::fstream file{ "file.bin", std::fstream::in | std::fstream::binary}; |
וזהו!
כל פרוטוקול אמור להיות מוגדר בתווים שלנו במיוחד אם אנחנו עובדים בצורה שהיא יותר low level
.
מצד אחד יש גמישות רבה אך מצד שני צריך להיזהר בטעויות קריטיות בזיהוי התווים.
תודה על הקריאה!