private void read(Stream fis, ISasReaderCallback callback) { SasHeader header = readHeader(fis); //Logger.Info(string.Format("Header: {0}", header)); readPages(fis, header, callback); //Logger.Info("Done!"); }
public void read(ISasReaderCallback callback) { if (_stream != null) { read(_stream, callback); } else { using (FileStream fis = new FileStream(_file.PathName, FileMode.Open, FileAccess.Read)) { read(fis, callback); } } }
private void readPages(Stream fis, SasHeader header, ISasReaderCallback callback) { List <SasSubHeader> subHeaders = new List <SasSubHeader>(); List <int> columnOffsets = new List <int>(); List <int> columnLengths = new List <int>(); List <SasColumnType> columnTypes = new List <SasColumnType>(); bool subHeadersParsed = false; int rowCount = 0; int pageSize = header.getPageSize(); int pageCount = header.getPageCount(); // these variables will define the default amount of rows per page and // other defaults int row_count = -1; int row_count_fp = -1; int row_length = -1; int col_count = -1; for (int pageNumber = 0; pageNumber < pageCount; pageNumber++) { //Logger.Info(string.Format("Reading page no. {0}", pageNumber)); byte[] pageData = new byte[pageSize]; int read = fis.Read(pageData, 0, pageSize); if (read == -1) { // reached end of file break; } byte pageType = IO.readByte(pageData, 17); switch (pageType) { case 0: case 1: case 2: // accepted type //Logger.Info(string.Format("Page type supported: {0}", pageType)); break; case 4: // accepted but not supported //Logger.Info(string.Format("Page type not fully supported: {0}", pageType)); break; default: throw new Exception("Page " + pageNumber + " has unknown type: " + pageType); } if (pageType == 0 || pageType == 2) { // Read subheaders int subhCount = IO.readInt(pageData, 20); for (int subHeaderNumber = 0; subHeaderNumber < subhCount; subHeaderNumber++) { int _base = 24 + subHeaderNumber * 12; int offset = IO.readInt(pageData, _base); int length = IO.readInt(pageData, _base + 4); if (length > 0) { byte[] rawData = IO.readBytes(pageData, offset, length); byte[] signatureData = IO.readBytes(rawData, 0, 4); SasSubHeader subHeader = new SasSubHeader(rawData, signatureData); subHeaders.Add(subHeader); } } } if ((pageType == 1 || pageType == 2)) { if (!subHeadersParsed) { // Parse subheaders SasSubHeader rowSize = getSubHeader(subHeaders, SUBH_ROWSIZE, "ROWSIZE"); row_length = IO.readInt(rowSize.getRawData(), 20); row_count = IO.readInt(rowSize.getRawData(), 24); int col_count_7 = IO.readInt(rowSize.getRawData(), 36); row_count_fp = IO.readInt(rowSize.getRawData(), 60); SasSubHeader colSize = getSubHeader(subHeaders, SUBH_COLSIZE, "COLSIZE"); int col_count_6 = IO.readInt(colSize.getRawData(), 4); col_count = col_count_6; //if (col_count_7 != col_count_6) { // Logger.Warn( // string.Format("({0}) Column count mismatch: {1} vs. {2}", // _file.PathName, col_count_6, col_count_7 )); //} SasSubHeader colText = getSubHeader(subHeaders, SUBH_COLTEXT, "COLTEXT"); List <SasSubHeader> colAttrHeaders = getSubHeaders( subHeaders, SUBH_COLATTR, "COLATTR"); SasSubHeader colAttr; if (!colAttrHeaders.Any()) { throw new Exception( "No column attribute subheader found"); } else if (colAttrHeaders.Count == 1) { colAttr = colAttrHeaders[0]; } else { colAttr = spliceColAttrSubHeaders(colAttrHeaders); } SasSubHeader colName = getSubHeader(subHeaders, SUBH_COLNAME, "COLNAME"); List <SasSubHeader> colLabels = getSubHeaders(subHeaders, SUBH_COLLABS, "COLLABS"); if (colLabels.Any() && colLabels.Count != col_count) { throw new Exception( "Unexpected column label count (" + colLabels.Count + ") expected 0 or " + col_count); } for (int i = 0; i < col_count; i++) { int _base = 12 + i * 8; String columnName; byte amd = IO.readByte(colName.getRawData(), _base); if (amd == 0) { int off = IO.readShort(colName.getRawData(), _base + 2) + 4; int len = IO.readShort(colName.getRawData(), _base + 4); columnName = IO.readString(colText.getRawData(), off, len); } else { columnName = "COL" + i; } // Read column labels String label; if (colLabels != null && colLabels.Any()) { _base = 42; byte[] rawData = colLabels[i].getRawData(); int off = IO.readShort(rawData, _base) + 4; short len = IO.readShort(rawData, _base + 2); if (len > 0) { label = IO.readString(colText.getRawData(), off, len); } else { label = null; } } else { label = null; } // Read column offset, width, type (required) _base = 12 + i * 12; int offset = IO.readInt(colAttr.getRawData(), _base); columnOffsets.Add(offset); int length = IO.readInt(colAttr.getRawData(), _base + 4); columnLengths.Add(length); short columnTypeCode = IO.readShort( colAttr.getRawData(), _base + 10); SasColumnType columnType = (columnTypeCode == 1 ? SasColumnType.NUMERIC : SasColumnType.CHARACTER); columnTypes.Add(columnType); //Logger.Debug(string.Format( // "Column no. {0} read: name={1},label={2},type={3},length={4}", // i, columnName, label, // columnType, length )); callback.column(i, columnName, label, columnType, length); } subHeadersParsed = true; } if (!callback.readData()) { //Logger.Info("Callback decided to not read data"); return; } // Read data int row_count_p; int _base2; if (pageType == 2) { row_count_p = row_count_fp; int subhCount = IO.readInt(pageData, 20); _base2 = 24 + subhCount * 12; _base2 = _base2 + _base2 % 8; } else { row_count_p = IO.readInt(pageData, 18); _base2 = 24; } if (row_count_p > row_count) { row_count_p = row_count; } for (int row = 0; row < row_count_p; row++) { Object[] rowData = new Object[col_count]; for (int col = 0; col < col_count; col++) { int off = _base2 + columnOffsets[col]; int len = columnLengths[col]; SasColumnType columnType = columnTypes[col]; if (len > 0) { byte[] raw = IO.readBytes(pageData, off, len); if (columnType == SasColumnType.NUMERIC && len < 8) { byte[] bb = new byte[8]; for (int j = 0; j < len; j++) { bb[j] = raw[j]; } for (int j = 0; j < 8 - len; j++) { bb[j] = (byte)0x00; } raw = bb; // col$length <- 8 len = 8; } Object value; if (columnType == SasColumnType.CHARACTER) { String str = IO.readString(raw, 0, len); str = str.Trim(); value = str; } else { value = IO.readNumber(raw, 0, len); } rowData[col] = value; } } //Logger.Debug(string.Format("Row no. {0} read: ", row)); //foreach(Object c in rowData) //{ // Logger.Debug(c); //} rowCount++; bool next = callback.row(rowCount, rowData); if (!next) { //Logger.Info("Callback decided to stop iteration"); return; } _base2 = _base2 + row_length; } } } }