Repository: mholt/PapaParse
Pull request #464 (2018-03-12 16:05:03)

return null values when dealing with empty strings (dynamictyping mode)

pokoli: Hi @mfds, thanks for your pull request. 

Could you please add a test that ensures this behaviour

Patch:
diff --git a/papaparse.js b/papaparse.js
index c1f1ec4..6c2a415 100755
--- a/papaparse.js
+++ b/papaparse.js
@@ -1123,7 +1123,7 @@
 		function tryParseFloat(val)
 		{
 			var isNumber = FLOAT.test(val);
-			return isNumber ? parseFloat(val) : val;
+			return isNumber ? parseFloat(val) : (val === '' ? null : val);
 		}
 
 		function addError(type, code, msg, row)
@@ -1158,6 +1158,10 @@
 		} else {
 			var quoteChar = config.quoteChar;
 		}
+		var escapeChar = quoteChar;
+		if (config.escapeChar !== undefined){
+			escapeChar = config.escapeChar;
+		}
 
 		// Delimiter must be valid
 		if (typeof delim !== 'string'
@@ -1236,7 +1240,7 @@
 
 			var nextDelim = input.indexOf(delim, cursor);
 			var nextNewline = input.indexOf(newline, cursor);
-			var quoteCharRegex = new RegExp(quoteChar+quoteChar, 'g');
+			var quoteCharRegex = new RegExp(escapeChar.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')+quoteChar, 'g');
 
 			// Parser loop
 			for (;;)
@@ -1279,12 +1283,19 @@
 						}
 
 						// If this quote is escaped, it's part of the data; skip it
-						if (input[quoteSearch+1] === quoteChar)
+						// If the quote character is the escape character, then check if the next character is the escape character
+						if (quoteChar === escapeChar &&  input[quoteSearch+1] === escapeChar)
 						{
 							quoteSearch++;
 							continue;
 						}
 
+						// If the quote character is not the escape character, then check if the previous character was the escape character
+						if (quoteChar !== escapeChar && quoteSearch !== 0 && input[quoteSearch-1] === escapeChar)
+						{
+							continue;
+						}
+
 						var spacesBetweenQuoteAndDelimiter = extraSpaces(nextDelim);
 
 						// Closing quote followed by delimiter or 'unnecessary steps + delimiter'


Test patch:
diff --git a/tests/test-cases.js b/tests/test-cases.js
index 503e905..9ad3e9a 100644
--- a/tests/test-cases.js
+++ b/tests/test-cases.js
@@ -852,6 +852,15 @@ var PARSE_TESTS = [
 			errors: []
 		}
 	},
+	{
+		description: "Dynamic typing converts empty values into NULL",
+		input: '1,2.2,1e3\r\n,NULL,\r\n-,5a,null',
+		config: { dynamicTyping: true },
+		expected: {
+			data: [[1, 2.2, 1000], [null, "NULL", null], ["-", "5a", "null"]],
+			errors: []
+		}
+	},
 	{
 		description: "Blank line at beginning",
 		input: '\r\na,b,c\r\nd,e,f',
@@ -1080,6 +1089,36 @@ var PARSE_TESTS = [
 			errors: []
 		}
 	},
+	{
+		description: "Custom escape character in the middle",
+		notes: "Must parse correctly if the backslash sign (\\) is configured as a custom escape character",
+		input: 'a,b,"c\\"d\\"f"',
+		config: { escapeChar:'\\'},
+		expected: {
+			data: [['a', 'b', 'c"d"f']],
+			errors: []
+		}
+	},
+	{
+		description: "Custom escape character at the end",
+		notes: "Must parse correctly if the backslash sign (\\) is configured as a custom escape character and the escaped quote character appears at the end of the column",
+		input: 'a,b,"c\\"d\\""',
+		config: { escapeChar:'\\'},
+		expected: {
+			data: [['a', 'b', 'c"d"']],
+			errors: []
+		}
+	},
+	{
+		description: "Custom escape character not used for escaping",
+		notes: "Must parse correctly if the backslash sign (\\) is configured as a custom escape character and appears as regular character in the text",
+		input: 'a,b,"c\\d"',
+		config: { escapeChar:'\\'},
+		expected: {
+			data: [['a', 'b', 'c\\d']],
+			errors: []
+		}
+	},
 	{
 		description: "Header row with preceding comment",
 		notes: "Must parse correctly headers if they are preceded by comments",