Menu

fopen() ב-PHP: פתיחה, קריאה וכתיבה של קבצים (המצבים)

fopen('data.txt', 'r') פותחת קובץ ומחזירה handle, או false בכישלון. קוראים מה-handle עם fgets() או fread(), כותבים עם fwrite() וסוגרים עם fclose(). המצב ('r', 'w', 'a', 'x', 'c' וצורות ה-+ שלהם) קובע מה מותר לעשות.

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

fopen('data.txt', 'r') פותחת קובץ ומחזירה handle, משאב stream שפונקציות הקבצים האחרות מקבלות כארגומנט הראשון שלהן. קוראים עם fgets() (שורה אחת) או fread() (מספר בתים), כותבים עם fwrite(), וסוגרים עם fclose(). הארגומנט השני הוא המצב: 'r' לקריאה, 'w' לכתיבה, 'a' להוספה.

פלט:

Got: apple
Got: banana
Got: cherry

fopen() מחזירה false כשהיא לא יכולה לפתוח את הקובץ (הוא לא קיים במצב r, תיקייה חסרה, אין הרשאה), ו-PHP מדפיסה אזהרה כמו:

Warning: fopen(missing.txt): Failed to open stream: No such file or directory in /home/index.php on line 2

לכן בדקו false לפני שמשתמשים ב-handle. לקריאה או כתיבה של קובץ קטן שלם בקריאה אחת, file_get_contents() ו-file_put_contents() קצרות יותר; fopen() נועדה לקבצים שקוראים חלק אחר חלק או משאירים פתוחים.

מצבי fopen

מצבקריאהכתיבהאם הקובץ קייםאם הוא לא קייםמתחיל ב
rכןלאפותח אותונכשלהתחלה
r+כןכןפותח אותו, שומר על התוכןנכשלהתחלה
wלאכןמרוקן אותויוצר אותוהתחלה
w+כןכןמרוקן אותויוצר אותוהתחלה
aלאכןשומר על התוכןיוצר אותוסוף, כל כתיבה מוסיפה
a+כןכןשומר על התוכןיוצר אותוסוף, כל כתיבה מוסיפה
xלאכןנכשליוצר אותוהתחלה
x+כןכןנכשליוצר אותוהתחלה
cלאכןשומר על התוכןיוצר אותוהתחלה
c+כןכןשומר על התוכןיוצר אותוהתחלה

שתי שורות מפתיעות אנשים. r+ כותב על הבתים הקיימים במקומם, בלי להכניס, ו-a+ תמיד כותב בסוף, גם אחרי שמזיזים את המצביע עם fseek() (ההזזה משפיעה רק על קריאות):

פלט:

Xbc
Xbc
Xbc
end

ב-Windows אפשר לראות 'rb' או 'wb': הדגל b פירושו בינארי ומונע מ-Windows לתרגם סופי שורות. הוא לא עושה כלום ב-Linux וב-macOS, ולכן הוספה שלו לא מזיקה וניידת.

קריאת קובץ שורה אחר שורה עם fgets()

fgets() מחזירה את השורה הבאה כולל ה-\n שלה, ו-false בסוף הקובץ. כיוון שרק שורה אחת נמצאת בזיכרון בכל רגע, הלולאה הזו מטפלת בקובץ לוג בכל גודל, ואפשר לעצור מוקדם עם break:

פלט:

2 errors in 5 lines
line 2: database timeout
line 4: disk full

השוו ל-false עם !==, לא עם while ($line = fgets($fh)) פשוט: שורה שמכילה רק "0" בלי ירידת שורה (השורה האחרונה של קובץ יכולה להיות כזו) היא שקרית והייתה מסיימת את הלולאה מוקדם.

כתיבה והוספה עם fwrite()

fwrite() כותבת מחרוזת במיקום הנוכחי ומחזירה את מספר הבתים שנכתבו, או false בכישלון. fputs() היא אותה פונקציה בשם אחר. פתחו עם 'a' כדי להוסיף לסוף קובץ בלי לגעת במה שכבר שם:

פלט:

wrote 12 bytes
wrote 13 bytes
wrote 15 bytes
visit /home
visit /about
visit /contact
visit /home

PHP_EOL הוא סוף השורה של המערכת ש-PHP רצה עליה (\n ב-Linux וב-macOS, \r\n ב-Windows). לקבצים שתוכנות אחרות קוראות, בחרו אחד במפורש, בדרך כלל "\n".

קריאה וכתיבה של קובצי CSV עם fgetcsv() ו-fputcsv()

fputcsv() כותבת מערך כשורת CSV אחת ועוטפת במירכאות שדות שמכילים פסיקים, מירכאות או ירידות שורה. fgetcsv() קוראת שורה אחת בחזרה כמערך. העבירו את המפריד, את תו העטיפה ותו escape ריק במפורש: זה נותן CSV סטנדרטי, ו-PHP 8.4 מדפיסה הודעת deprecation כשמשמיטים את ארגומנט ה-escape.

פלט:

name,city,note
Ada,London,"likes ""math"", a lot"
Linus,Helsinki,kernel

Ada lives in London: likes "math", a lot
Linus lives in Helsinki: kernel

פיצול שורות עם explode(',', $line) נשבר ברגע ששדה מכיל פסיק; fgetcsv() מטפלת בעטיפה בשבילכם. לקובץ מופרד בנקודה פסיק (נפוץ בייצוא מ-Excel באירופה), העבירו ';' כמפריד.

תנועה בתוך קובץ עם fseek(), ftell() ו-rewind()

לקובץ פתוח יש מיקום. קריאות וכתיבות מתחילות שם ומזיזות אותו קדימה. ftell() אומרת לכם איפה הוא, fseek() מזיזה אותו להיסט בבתים, ו-rewind() חוזרת להתחלה. עם w+ אפשר לכתוב קובץ ולקרוא אותו בחזרה דרך אותו handle:

פלט:

11
world
hello world

נעילת קובץ עם flock()

שתי בקשות שקוראות מונה, מוסיפות אחד וכותבות אותו בחזרה באותו זמן יכולות לקרוא שתיהן 41 ולכתוב שתיהן 42. flock() עם LOCK_EX גורמת לשנייה לחכות עד שהראשונה משחררת את הנעילה. המצב c+ הוא הנכון כאן: הוא יוצר את הקובץ אם צריך, ובניגוד ל-w+, לא מרוקן אותו לפני שמחזיקים בנעילה.

פלט:

Visitor number 1
Visitor number 2
Visitor number 3

נעילות הן מייעצות: הן עוצרות רק קוד אחר שגם קורא ל-flock(). להוספת שורה אחת, file_put_contents($path, $line, FILE_APPEND | LOCK_EX) עושה את אותו הדבר בקריאה אחת.

טעות נפוצה: while (!feof($fh))

מדריכים ישנים רבים עוברים בלולאה עם feof(). היא מחזירה true רק אחרי שקריאה כבר הגיעה לסוף, ולכן הלולאה רצה פעם אחת יותר ממספר השורות וה-fgets() האחרון מחזיר false:

פלט:

string(2) "a
"
string(2) "b
"
string(2) "c
"
bool(false)
4 rounds for 3 lines

ה-false העודף הזה הופך לשורה ריקה בדוח או לאיבר ריק במערך. עברו בלולאה על הקריאה עצמה, while (($line = fgets($fh)) !== false), כמו שכל דוגמה אחרת בעמוד הזה עושה.

שאלות נפוצות

מה הם מצבי fopen ב-PHP?

r קורא, w כותב אחרי ריקון הקובץ, a כותב בסוף, x יוצר קובץ חדש ונכשל אם הוא קיים, ו-c כותב בלי לרוקן אותו. הוספת + (r+, w+, a+, x+, c+) מאפשרת גם קריאה וגם כתיבה. כל מצב חוץ מ-r ו-r+ יוצר את הקובץ כשהוא חסר.

מה ההבדל בין w ל-a ב-fopen?

w מרוקן את הקובץ ברגע שהוא נפתח וכותב מההתחלה. a שומר על התוכן וכל fwrite() הולך לסוף הקובץ, גם אחרי fseek(), וזה הופך אותו למצב של קובצי לוג.

איך קוראים קובץ CSV ב-PHP?

פתחו אותו עם fopen($path, 'r') וקראו ל-fgetcsv() בלולאה: while (($row = fgetcsv($fh, null, ',', '"', '')) !== false) { ... }. כל קריאה מחזירה שורה אחת כמערך של שדות, עם טיפול במירכאות ובפסיקים שבתוך שדות.

האם צריך לקרוא ל-fclose ב-PHP?

PHP סוגרת כל קובץ פתוח כשהסקריפט מסתיים, ולכן סקריפט קצר עובד גם בלעדיה. קראו ל-fclose() בכל זאת ברגע שסיימתם: היא שוטפת כתיבות שבבאפר, משחררת נעילות שנלקחו עם flock(), ומונעת מסקריפט שרץ זמן רב להיגמר לו ה-handles של קבצים.

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

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

להתחיל