/// <summary> /// Open the file ready for reading. /// </summary> /// <param name="fileName">Name of the file.</param> /// <param name="sheetName">Name of excel worksheet, if applicable</param> /// <exception cref="System.Exception">Cannot find file: + FileName</exception> public void Open(string fileName, string sheetName = "") { if (fileName == "") { return; } if (!File.Exists(fileName)) { throw new Exception("Cannot find file: " + fileName); } _FileName = fileName; _SheetName = sheetName; IsCSVFile = System.IO.Path.GetExtension(fileName).ToLower() == ".csv"; IsExcelFile = System.IO.Path.GetExtension(fileName).ToLower() == ExcelUtilities.ExcelExtension; if (IsExcelFile) { OpenExcelReader(); } else { inStreamReader = new StreamReaderRandomAccess(_FileName); Open(); } }
/// <summary> /// Looks the ahead for non missing value. /// </summary> /// <param name="inData">The in.</param> /// <param name="w">The w.</param> /// <returns></returns> private string LookAheadForNonMissingValue(StreamReaderRandomAccess inData, int w) { if (inData.EndOfStream) { return("?"); } int Pos = inData.Position; StringCollection Words = new StringCollection(); while (GetNextLine(inData, ref Words) && (Words[w] == "?" || Words[w] == "*")) { ; } inData.Position = Pos; if (Words.Count > w) { return(Words[w]); } else { return("?"); } }
/// <summary> /// Opens the specified stream. /// </summary> /// <param name="stream">The stream.</param> public void Open(Stream stream) { _FileName = "Memory stream"; IsCSVFile = false; inStreamReader = new StreamReaderRandomAccess(stream); Open(); }
/// <summary> /// Reads the apsim header lines. /// </summary> /// <param name="inData">The in.</param> /// <param name="constantLines">The constant lines.</param> /// <param name="headingLines">The heading lines.</param> private void ReadApsimHeaderLines(StreamReaderRandomAccess inData, ref StringCollection constantLines, ref StringCollection headingLines) { string PreviousLine = ""; while (!inData.EndOfStream) { string Line = inData.ReadLine(); int PosEquals = Line.IndexOf('='); if (PosEquals != -1) { // constant found. constantLines.Add(Line); } else { if (IsCSVFile) { headingLines.Add(Line); break; } char[] whitespace = { ' ', '\t' }; int PosFirstNonBlankChar = StringUtilities.IndexNotOfAny(Line, whitespace); if (PosFirstNonBlankChar != -1 && Line[PosFirstNonBlankChar] == '(') { headingLines.Add(PreviousLine); headingLines.Add(Line); break; } } PreviousLine = Line; } }
/// <summary> /// Determine and return the data types of the specfied words. /// </summary> /// <param name="inData">The in.</param> /// <param name="words">The words.</param> /// <returns></returns> private Type[] DetermineColumnTypes(StreamReaderRandomAccess inData, StringCollection words) { Type[] Types = new Type[words.Count]; for (int w = 0; w != words.Count; w++) { if (words[w] == "?" || words[w] == "*" || words[w] == "") { Types[w] = StringUtilities.DetermineType(LookAheadForNonMissingValue(inData, w), Units[w]); } else { Types[w] = StringUtilities.DetermineType(words[w], Units[w]); } } return(Types); }
/// <summary> /// Return the next line in the file as a collection of words. /// </summary> /// <param name="inData">The in.</param> /// <param name="words">The words.</param> /// <returns></returns> /// <exception cref="System.Exception">Invalid number of values on line: + Line + \r\nin file: + _FileName</exception> private bool GetNextLine(StreamReaderRandomAccess inData, ref StringCollection words) { if (inData.EndOfStream) { return(false); } string Line = inData.ReadLine(); if (Line == null || Line.Length == 0) { return(false); } if (Line.IndexOf("!") > 0) //used to ignore "!" in a row { Line = Line.Substring(0, Line.IndexOf("!") - 1); } if (IsCSVFile) { words.Clear(); Line = Line.TrimEnd(','); words.AddRange(Line.Split(",".ToCharArray())); } else { words = StringUtilities.SplitStringHonouringQuotes(Line, " \t"); } if (words.Count != Headings.Count) { throw new Exception("Invalid number of values on line: " + Line + "\r\nin file: " + _FileName); } // Remove leading / trailing double quote chars. for (int i = 0; i < words.Count; i++) { words[i] = words[i].Trim("\"".ToCharArray()); } return(true); }
/// <summary> /// Determine and return the data types of the specfied words. /// </summary> /// <param name="inData">The in.</param> /// <param name="words">The words.</param> /// <returns></returns> private Type[] DetermineColumnTypes(StreamReaderRandomAccess inData, StringCollection words) { Type[] Types = new Type[words.Count]; for (int w = 0; w != words.Count; w++) { if (words[w] == "?" || words[w] == "*" || words[w] == "") { Types[w] = StringUtilities.DetermineType(LookAheadForNonMissingValue(inData, w), Units[w]); } else { Types[w] = StringUtilities.DetermineType(words[w], Units[w]); } // If we can parse as a DateTime, but don't yet have an explicit format, try to determine // the correct format and make it explicit. if (Types[w] == typeof(DateTime) && (Units[w] == "" || Units[w] == "()")) { // First try our traditional default format DateTime dtValue; if (DateTime.TryParseExact(words[w], "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dtValue)) { Units[w] = "(yyyy-MM-dd)"; } else { // We know something in the current culture works. Step through the patterns until we find it. string[] dateFormats = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.GetAllDateTimePatterns(); foreach (string dateFormat in dateFormats) { if (DateTime.TryParseExact(words[w], dateFormat, System.Globalization.CultureInfo.CurrentCulture, System.Globalization.DateTimeStyles.None, out dtValue)) { Units[w] = "(" + dateFormat + ")"; break; } } } } } return(Types); }
/// <summary> /// Read in the APSIM header - headings/units and constants. /// </summary> /// <param name="inData">The in.</param> /// <exception cref="System.Exception">The number of headings and units doesn't match in file: + _FileName</exception> private void ReadApsimHeader(StreamReaderRandomAccess inData) { StringCollection ConstantLines = new StringCollection(); StringCollection HeadingLines = new StringCollection(); ReadApsimHeaderLines(inData, ref ConstantLines, ref HeadingLines); bool TitleFound = false; foreach (string ConstantLine in ConstantLines) { string Line = ConstantLine; string Comment = StringUtilities.SplitOffAfterDelimiter(ref Line, "!"); Comment.Trim(); int PosEquals = Line.IndexOf('='); if (PosEquals != -1) { string Name = Line.Substring(0, PosEquals).Trim(); if (Name.ToLower() == "title") { TitleFound = true; Name = "Title"; } string Value = Line.Substring(PosEquals + 1).Trim(); string Unit = string.Empty; if (Name != "Title") { Unit = StringUtilities.SplitOffBracketedValue(ref Value, '(', ')'); } _Constants.Add(new ApsimConstant(Name, Value, Unit, Comment)); } } if (HeadingLines.Count >= 1) { if (IsCSVFile) { HeadingLines[0] = HeadingLines[0].TrimEnd(','); Headings = new StringCollection(); Units = new StringCollection(); Headings.AddRange(HeadingLines[0].Split(",".ToCharArray())); for (int i = 0; i < Headings.Count; i++) { Headings[i] = Headings[i].Trim(); Headings[i] = Headings[i].Trim('"'); Units.Add("()"); } } else { Headings = StringUtilities.SplitStringHonouringQuotes(HeadingLines[0], " \t"); Units = StringUtilities.SplitStringHonouringQuotes(HeadingLines[1], " \t"); } TitleFound = TitleFound || StringUtilities.IndexOfCaseInsensitive(Headings, "title") != -1; if (Headings.Count != Units.Count) { throw new Exception("The number of headings and units doesn't match in file: " + _FileName); } } if (!TitleFound) { _Constants.Add(new ApsimConstant("Title", System.IO.Path.GetFileNameWithoutExtension(_FileName), "", "")); } }
/// <summary> /// Return the next line in the file as a collection of words. /// </summary> /// <param name="inData">The in.</param> /// <param name="words">The words.</param> /// <returns></returns> /// <exception cref="System.Exception">Invalid number of values on line: + Line + \r\nin file: + _FileName</exception> private bool GetNextLine(StreamReaderRandomAccess inData, ref StringCollection words) { if (inData.EndOfStream) return false; string Line = inData.ReadLine(); if (Line == null || Line.Length == 0) return false; if (Line.IndexOf("!") > 0) //used to ignore "!" in a row Line = Line.Substring(0, Line.IndexOf("!") - 1); if (IsCSVFile) { words.Clear(); Line = Line.TrimEnd(','); words.AddRange(Line.Split(",".ToCharArray())); } else words = StringUtilities.SplitStringHonouringQuotes(Line, " \t"); if (words.Count != Headings.Count) throw new Exception("Invalid number of values on line: " + Line + "\r\nin file: " + _FileName); // Remove leading / trailing double quote chars. for (int i = 0; i < words.Count; i++) words[i] = words[i].Trim("\"".ToCharArray()); return true; }
/// <summary> /// Looks the ahead for non missing value. /// </summary> /// <param name="inData">The in.</param> /// <param name="w">The w.</param> /// <returns></returns> private string LookAheadForNonMissingValue(StreamReaderRandomAccess inData, int w) { if (inData.EndOfStream) return "?"; int Pos = inData.Position; StringCollection Words = new StringCollection(); while (GetNextLine(inData, ref Words) && (Words[w] == "?" || Words[w] == "*")) ; inData.Position = Pos; if (Words.Count > w) return Words[w]; else return "?"; }
/// <summary> /// Determine and return the data types of the specfied words. /// </summary> /// <param name="inData">The in.</param> /// <param name="words">The words.</param> /// <returns></returns> private Type[] DetermineColumnTypes(StreamReaderRandomAccess inData, StringCollection words) { Type[] Types = new Type[words.Count]; for (int w = 0; w != words.Count; w++) { if (words[w] == "?" || words[w] == "*" || words[w] == "") Types[w] = StringUtilities.DetermineType(LookAheadForNonMissingValue(inData, w), Units[w]); else Types[w] = StringUtilities.DetermineType(words[w], Units[w]); } return Types; }
/// <summary> /// Read in the APSIM header - headings/units and constants. /// </summary> /// <param name="inData">The in.</param> /// <exception cref="System.Exception">The number of headings and units doesn't match in file: + _FileName</exception> private void ReadApsimHeader(StreamReaderRandomAccess inData) { StringCollection ConstantLines = new StringCollection(); StringCollection HeadingLines = new StringCollection(); ReadApsimHeaderLines(inData, ref ConstantLines, ref HeadingLines); bool TitleFound = false; foreach (string ConstantLine in ConstantLines) { string Line = ConstantLine; string Comment = StringUtilities.SplitOffAfterDelimiter(ref Line, "!"); Comment.Trim(); int PosEquals = Line.IndexOf('='); if (PosEquals != -1) { string Name = Line.Substring(0, PosEquals).Trim(); if (Name.ToLower() == "title") { TitleFound = true; Name = "Title"; } string Value = Line.Substring(PosEquals + 1).Trim(); string Unit = string.Empty; if (Name != "Title") Unit = StringUtilities.SplitOffBracketedValue(ref Value, '(', ')'); _Constants.Add(new ApsimConstant(Name, Value, Unit, Comment)); } } if (HeadingLines.Count >= 1) { if (IsCSVFile) { HeadingLines[0] = HeadingLines[0].TrimEnd(','); Headings = new StringCollection(); Units = new StringCollection(); Headings.AddRange(HeadingLines[0].Split(",".ToCharArray())); for (int i = 0; i < Headings.Count; i++) { Headings[i] = Headings[i].Trim(); Units.Add("()"); } } else { Headings = StringUtilities.SplitStringHonouringQuotes(HeadingLines[0], " \t"); Units = StringUtilities.SplitStringHonouringQuotes(HeadingLines[1], " \t"); } TitleFound = TitleFound || StringUtilities.IndexOfCaseInsensitive(Headings, "title") != -1; if (Headings.Count != Units.Count) throw new Exception("The number of headings and units doesn't match in file: " + _FileName); } if (!TitleFound) _Constants.Add(new ApsimConstant("Title", System.IO.Path.GetFileNameWithoutExtension(_FileName), "", "")); }
/// <summary> /// Reads the apsim header lines. /// </summary> /// <param name="inData">The in.</param> /// <param name="constantLines">The constant lines.</param> /// <param name="headingLines">The heading lines.</param> private void ReadApsimHeaderLines(StreamReaderRandomAccess inData, ref StringCollection constantLines, ref StringCollection headingLines) { string PreviousLine = ""; string Line = inData.ReadLine(); while (!inData.EndOfStream) { int PosEquals = Line.IndexOf('='); if (PosEquals != -1) { // constant found. constantLines.Add(Line); } else { if (IsCSVFile) { headingLines.Add(Line); break; } char[] whitespace = { ' ', '\t' }; int PosFirstNonBlankChar = StringUtilities.IndexNotOfAny(Line, whitespace); if (PosFirstNonBlankChar != -1 && Line[PosFirstNonBlankChar] == '(') { headingLines.Add(PreviousLine); headingLines.Add(Line); break; } } PreviousLine = Line; Line = inData.ReadLine(); } }
/// <summary> /// Open the file ready for reading. /// </summary> /// <param name="fileName">Name of the file.</param> /// <param name="sheetName">Name of excel worksheet, if applicable</param> /// <exception cref="System.Exception">Cannot find file: + FileName</exception> public void Open(string fileName, string sheetName = "") { if (fileName == "") return; if (!File.Exists(fileName)) throw new Exception("Cannot find file: " + fileName); _FileName = fileName; _SheetName = sheetName; IsCSVFile = System.IO.Path.GetExtension(fileName).ToLower() == ".csv"; IsExcelFile = System.IO.Path.GetExtension(fileName).ToLower() == ExcelUtilities.ExcelExtension; if (IsExcelFile) { OpenExcelReader(); } else { inStreamReader = new StreamReaderRandomAccess(_FileName); Open(); } }