קריאה וכתיבה מקובץ CSV
-
אני מנסה לכתוב בעצמי מתודה שתקרא מקובץ CSV (כדי לייבא נתונים לתוכנה).
בעבר השתמשתי במחלקה מוכנה ליצירת קובץ CSV לייצוא קובץ מהתכנה. אולם עכשיו אני רוצה לממש זאת בעצמי.
אני מנסה להבין כיצד בנוי קובץ כזה כדי לדעת אלו דברים אני צריך לבדוק. חיפשתי הרבה בגוגל, וניסיתי לקרוא קוד כתוב שאנשים כבר עשו, אולם אני לא ממש מצליח להבין, אשמח לעזרה.ידיעותי עד כה:
תוכן הקובץ מופרד ע"י פסיקים (בד"כ, הבנתי שיש כאלו המופקדים ב ; האם ישנם מפרידים נוספים?), כשכל פסיק מסמן עמודה חדשה. וכל שורה חדשה מהוה תוכן חדש (איש קשר חדש וכדו', או שורה חדשה בטבלה).
כאשר הפסיק מהוה חלק מהתוכן של התא, הוא מוקף במרכאות. אם אני מבין נכון ישנם עוד כמה דברים שיכולים להיות מוקפים במרכאות.
הנתונים בשורה הראשונה הם הכותרת של העמודות.אני רוצה לייבא קובץ אנשי קשר, שהמשתמש יערוך בפורמט שאגדיר לו. כך שאני צריך לקרוא את הנתונים של השורה הראשונה ולראות שהם מתאימים למאפיינים של המשתנה שלי. אח"כ לעבור לשורות הבאות, לקרוא שורה שורה ע"י readLine ולשמור את התוכן שלה במאפיינים הרלבנטיים. תוך כדי שאני מסיר את כל התווים המיותרים (גרשיים וכדו').
כמו כן, האם מתאים להשתמש כאן במחלקה גנרית, או שצריך לכתוב משהו ייעודי שמתאים לפי המאפיינים שלי (שהמשתמש יוכל לייבא קובץ של אנשי קשר ולא יצטרך להזין אותם אחד אחד..)
אשמח לתיקונים, הוספות, הערות והארות.
שתהיה לכולנו שנה טובה וחורף פורה, ושנתבשר רק בשורות טובות!!
תודה רבה!
אברהםפורסם במקור בפורום CODE613 ב06/10/2015 17:46 (+03:00)
-
לפי מה שידוע לי שדה שמכיל פסיק חייב להיות מוקף בגרשיים
שדה שמכיל גרשיים יש להוסיף ליד הגרשיים עוד גרשיים
ראה Basic rules and examplesפורסם במקור בפורום CODE613 ב06/10/2015 18:39 (+03:00)
-
שלום לך
לא ספרת לנו באיזה שפה אתה מנסה לקרוא נתונים.
בפייתון עושים זאת כך https://pymotw.com/2/csv/תיקון קטן שיכול לקצר את הקוד עוד קצת הוא להשתמש בopen
(איך כותבים פה קוד?)משהו שרק תשים אליו לב, יש תקן ארופאי (אם אני לא טועה) ששם הפרדה על עמודות זה עם פסיק ועוד משהו.
פורסם במקור בפורום CODE613 ב06/10/2015 21:33 (+03:00)
-
תודה רבה!
זהו, אין עוד סימונים נוספים שצריך לשים אליהם לב מלבד גרשיים, פסיק, פסיק-נקודה??
את הקובץ בויקיפדיה לא בדיוק הצלחתי לקרוא עד סופו
ולtrew ברוך הבא לפורום!! נקווה שתיהנה פה ותישאר איתנו :lol:
לא ציינתי באיזה שפה.. פשוט כי כולם יודעים שאני לומד C#... (מרוב השאלות שאני שואל :lol: )לגבי הוספת קוד אתה יכול לבחור בסרגל "לקוד בחר תחביר" וזה מוסיף לך מקום להזין קוד לפי הסוג שבחרת.
ולגבי התקן האירופי ראיתי שמפרידים בפסיק נקודה, ואת הנקודה העשרונית מסמנים בפסיק במקום נקודה.פורסם במקור בפורום CODE613 ב06/10/2015 21:55 (+03:00)
-
אחרי הרבה עמל ויגיעה... וניסיון לקרוא קוד של אחרים ולראות כיצד הם מימשו זאת
אז הנה התוצר המוגמר:
וכמובן נעזרתי הרבה הרבה בקוד של הבחור הנ"ל.. (כדי לומר דבר בשם אומרו:-)) אם כי נראה לי שהקוד שלו קצת חסר...בכל אופן אשמח מאוד מאוד לשמוע הערות והארות(גם בסגנון יכלת לכתוב את כל זה בשתי שורות :lol: כי בסוף העיקר זה ללמוד ולהשתכלל..):
אז הנה, תתחילו להתקיף :lol: :lol:using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Reflection; using Person; namespace Avraham.CsvReader { /// <summary> /// מחלקה המגדירה את אובייקט השורה של קובץ CSV. /// המחלקה יורשת מליסט כדי לאחסן את הערכים במערך. /// </summary> public class RowCsv:List<string> { /// <summary> /// מאפיין המכיל את השורה שנקראה מהקובץ /// </summary> public string LineText { get; set; } } /// <summary> /// מחלקה המגדירה אובייקט המכיל את ערכי הכותרת העמודות /// המחלקה יורשת מליסט כדי לאחסן זאת במערך. /// </summary> public class ColumnHeader:List<string> { /// <summary> /// מקבל את הסך הכללי של העמודות שיש בקובץ /// </summary> public int ColumnNumber { get; set; } } /// <summary> /// מחלקה לקריאה מקובץ CSV. /// </summary> public class CsvReader:StreamReader { /// <summary> /// הגדרת התו המפריד בין הנתונים /// תקן רגיל: ','. תקן אירופאי: ';'. /// </summary> private char separator = ','; public CsvReader(Stream stream):base(stream,Encoding.Default) { } public CsvReader(string path):base(path,Encoding.Default) { } /// <summary> /// מתודה הקוראת את השורה הראשונה של הקובץ ומגדירה את שמות העמודות /// </summary> /// <param name="row"></param> /// <param name="separator">מגדיר את התוו המפריד בין הנתונים בד"כ ',' בתקן האירופאי ';'.</param> /// <returns>מחזיר אובייקט של רשימת העמודות</returns> public ColumnHeader ReadHeader(RowCsv row, char separator=',') { this.separator = separator; ColumnHeader columns = new ColumnHeader(); string[] value = row.LineText.Split(','); for(int i = 0; i<value.Length;i++) { columns.Add(value[i]); columns.ColumnNumber = i + 1; } return columns; } /// <summary> /// מתודה לקריאת שורה מהקובץ /// </summary> /// <param name="row"></param> /// <param name="separator"></param> /// <returns></returns> private bool ReadRowCsv(RowCsv row,char separator=',') { this.separator = separator; if (string.IsNullOrWhiteSpace(row.LineText)) return false; //משתנה המכיל את מיקום התו הנוכחי int position = 0; ///משתנה המכיל את מיקום התא הנוכחי int cell = 0; //מגדיר את נקודת ההתחלה בשביל הערך של התא הנוכחי שנקרא int start = 0; //לולאה שקוראת תו אחר תו while(position<row.LineText.Length) { string value; //אם התו הנוכחי מכיל גרשיים צריך לקרוא מהקובץ עד לגרשיים הבאים, כיון שהכל הוא נתון של תא אחר //והגרשיים באים כיון שיש באמצע פסיק וכדו' שהוא לא נועד כדי להפריד if(row.LineText[position]=='"') { position++; start = position; //הלולאה קוראת את התווים הבאים עד שהיא מגיעה לגרשיים הבאים while (position < row.LineText.Length) { if (row.LineText[position] == '"') { //מעלה את המונה באחד כדי לבדוק האם התו הבא הוא גם גרשיים //אם כן, נמצא שלא סיימנו את קריאת הערך של התא, אלא זה נועד רק כדי לסמן שהגרשיים הם חלק מהערך position++; // (כאשר התו הבא אינו גרשיים (וכן הם אינם התו האחרון בשורה if (position >= row.LineText.Length || row.LineText[position] != '"') { //מוריד את המונה באחד, כדי שהערך יקרא עד הגרשיים ולא יותר position--; break; } } position++; } //קורא את הערך מהגרשיים הראשונות ועד האחרונות value = row.LineText.Substring(start, position - start); //מחליף את הגרשיים הכפולות הצמודות בגרשיים בודדות value = value.Replace("\"\"", "\""); } //אם הערך לא מתחיל בגרשיים else { start = position; //סופר את התווים עד המיקום של הסימן המפריד while (position < row.LineText.Length && row.LineText[position] != separator) position++; //גוזר את הערך מהמחרוזת value = row.LineText.Substring(start, position - start); } //הוספת הערך שנקרא לליסט של הערכים שנקראו מהשורה if (cell < row.Count) row[cell] = value; else row.Add(value); cell++; //מוסיף עוד ספרה למונה כדי להתחיל את הלולאה הבאה מתחילת הערך הבא ולדלג על התו המפריד if(position<row.LineText.Length) position++; } return true; } /// <summary> /// מתודה לקריאת קובץ CSV שלם. /// </summary> /// <param name="separator"></param> /// <returns></returns> public List<Donor> ReadFileCsv(char separator = ',') { ///המתודה מותאמת לקריאה של אנשי קשר ולכן היא משתמשת במשתנה ספציפי שנועד לזה Donor donor = new Donor(); List<Donor> donors = new List<Donor>(); this.separator = separator; RowCsv row = new RowCsv(); //קורא את השורה הראשונה ובודק האם הקובץ המיובא מתאים למשתני התכנית row.LineText = ReadLine(); int mone = 0; //בדיקה שהעמודות בקובץ קיימות ומתאימות למאפייני המשתנה ColumnHeader columns = ReadHeader(row, separator); for(int i=0;i<columns.ColumnNumber;i++) { MemberInfo mi = (MemberInfo)typeof(Person.Donor).GetProperty(columns[i]); if (mi.Name == columns[i]) mone++; } if (mone != columns.ColumnNumber) throw new Exception("הקובץ אינו תואם למשתנה"); //קורא את השורה הבאה ושם את הערכים במאפייני משתני התכנית row.LineText = ReadLine(); while(!string.IsNullOrEmpty(row.LineText)) { ReadRowCsv(row, separator); donor.Id = int.Parse(row[0]); donor.FirstName = row[1]; donor.LastName = row[2]; donor.Tel = row[3]; donor.Email = row[4]; donor.Adress = row[5]; if (row[6] == "Female") donor.Gender = Gender.female; else donor.Gender = Gender.male; donors.Add(donor); //קורא שורה חדשה row.LineText = ReadLine(); donor = new Donor(); } //מחזיר את הליסט של אנשי הקשר return donors; } } }
פורסם במקור בפורום CODE613 ב07/10/2015 21:25 (+03:00)
-
והנה דוגמא למימוש של זה:
string fileName; OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "csv files (*.csv)|*.csv|excel files (*.xls,*.xlsx)|*.xls*"; ofd.InitialDirectory = "c:\\"; ofd.Title = "בחר את קובץ אנשי הקשר להוספה"; if (ofd.ShowDialog().Value == true) { fileName = ofd.FileName; CsvReader read = new CsvReader(fileName); donors = new ObservableCollection<Donor>(read.ReadFileCsv()); MessageBox.Show("אנשי הקשר נוספו בהצלחה!", "הפעולה הצליחה"); }
סליחה אם חפרתי קצת.. ואם אני קצת מתלהב...
ככה זה כשמצליחים לבסוף :lol: :lol:פורסם במקור בפורום CODE613 ב07/10/2015 21:34 (+03:00)