private void replaceConfig(Config config) { // Deep-copy the config so we can edit it Config configCopy = Papa.copy(config); //configCopy.chunkSize = parseInt(configCopy.chunkSize); // parseInt VERY important so we don't concatenate strings! if (config.step == null && config.chunk == null) { configCopy.chunkSize = 0; // disable Range header if not streaming; bad values break IIS - see issue #196 } this._handle = new ParserHandle(configCopy); this._handle.streamer = this; this._config = configCopy; // persist the copy to the caller }
// Parses input. Most users won't need, and shouldn't mess with, the baseIndex // and ignoreLastRow parameters. They are used by streamers (wrapper functions) // when an input comes in multiple chunks, like from a file. public Result parse(string input, int baseIndex = 0, bool ignoreLastRow = false) { Func <bool> needsHeaderRow = () => { return(_config.header && _fields.Count == 0); }; Action fillHeaderFields = () => { if (_results == null || _results.data.Count == 0) { return; } for (int i = 0; needsHeaderRow() && i < _results.data.Count; i++) { for (int j = 0; j < _results.data[i].Count; j++) { _fields.Add(_results.data[i][j]); } } _results.data.RemoveRange(0, 1); }; Func <Result> applyHeaderAndDynamicTyping = () => { if (_results == null || (!_config.header && !_config.dynamicTyping)) { return(_results); } for (int i = 0; i < _results.data.Count; i++) { Dictionary <string, string> rowWithHeader = new Dictionary <string, string>(); int j; for (j = 0; j < _results.data[i].Count; j++) { //[TODO] //if (_config.dynamicTyping) //{ // var value = _results.data[i][j]; // if (value == "true" || value == "TRUE") // _results.data[i][j] = true; // else if (value == "false" || value == "FALSE") // _results.data[i][j] = false; // else // _results.data[i][j] = tryParseFloat(value); //} if (_config.header) { if (j >= _fields.Count) { if (!rowWithHeader.ContainsKey("__parsed_extra")) { rowWithHeader.Add("__parsed_extra", ""); } rowWithHeader["__parsed_extra"] += _results.data[i][j]; //[CR we can not simply put an Array into __parsed_extra, so juste pipe it] if (j < _results.data[i].Count - 1) { rowWithHeader["__parsed_extra"] += "|"; } } else { rowWithHeader[_fields[j]] = _results.data[i][j]; } } } if (_config.header) { _results.dataWithHeader.Add(rowWithHeader); //[CR we are not overwriting _results.data here but instead fill another List] if (j > _fields.Count) { addError("FieldMismatch", "TooManyFields", "Too many fields: expected " + _fields.Count + " fields but parsed " + j, i); } else if (j < _fields.Count) { addError("FieldMismatch", "TooFewFields", "Too few fields: expected " + _fields.Count + " fields but parsed " + j, i); } } } if (_config.header && _results.meta != null) { _results.meta.fields = _fields; } return(_results); }; Func <Result> processResults = () => { if (_results != null && _delimiterError) { addError("Delimiter", "UndetectableDelimiter", "Unable to auto-detect delimiting character; defaulted to '" + Papa.DefaultDelimiter + "'"); _delimiterError = false; } if (_config.skipEmptyLines) { for (int i = 0; i < _results.data.Count; i++) { if (_results.data[i].Count == 1 && _results.data[i][0] == "") { _results.data.RemoveRange(i--, 1); } } } if (needsHeaderRow()) { fillHeaderFields(); } return(applyHeaderAndDynamicTyping()); }; //------------------------------------------------------------------------------------------------------------------------------------------------------ if (String.IsNullOrEmpty(_config.newline)) { _config.newline = guessLineEndings(input); } _delimiterError = false; if (String.IsNullOrEmpty(_config.delimiter)) { DelimiterResult delimGuess = guessDelimiter(input); if (delimGuess.successful) { _config.delimiter = delimGuess.bestDelimiter; } else { _delimiterError = true; // add error after parsing (otherwise it would be overwritten) _config.delimiter = Papa.DefaultDelimiter; } _results.meta.delimiter = _config.delimiter; } if (_config.quoteChar == Char.MinValue) { if (Papa.Substr(input, 0, 1) == "'" && Papa.Substr(input, input.IndexOf(_config.delimiter, 0) - 1, 1) == "'") { _config.quoteChar = '\''; } else { _config.quoteChar = '"'; } } Config parserConfig = Papa.copy(_config); if (_config.preview > 0 && _config.header) { parserConfig.preview++; // to compensate for header row } if (Papa.isFunction(_config.step)) { Action <Result, ParserHandle> userStep = _config.step; parserConfig.step = (results, parser) => { _results = results; if (needsHeaderRow()) { processResults(); } else // only call user's step function after header row { processResults(); // It's possbile that this line was empty and there's no row here after all if (_results.data.Count == 0) { return; } _stepCounter += results.data.Count; if (parserConfig.preview > 0 && _stepCounter > parserConfig.preview) { _parser.abort(); } else { userStep(_results, this); } } }; } //---------------------------------------------------------------------- _input = input; _parser = new Parser(parserConfig); _results = _parser.parse(_input, baseIndex, ignoreLastRow); processResults(); if (_paused) { return new Result() { meta = new Meta() { paused = true } } } ; else if (_results != null) { return(_results); } else { return new Result() { meta = new Meta() { paused = false } } }; }