In-Browser CSV Parsers

JavaScript performance comparison

Revision 13 of this test case created by Fab and last updated

Preparation code

<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<!-- CSV libraries -->
<script src="http://jquery-csv.googlecode.com/git/src/jquery.csv.js"></script>
<script src="https://rawgit.com/henix/csv.js/master/csv.js"></script>
<script>var henixCsv = CSV; // avoid namespace collision</script>
<script src="https://rawgit.com/knrz/CSV.js/master/csv.min.js"></script>
<script src="https://rawgit.com/mholt/PapaParse/master/papaparse.min.js"></script>
<script>
// Papa Parse 3.1 version of the parser
function Papa3Parser(e){function y(){while(f<n.length){if(g)break;if(u>0&&m>=u)break;if(a=='"')E();else if(l)S();else x();b()}return w()}function b(){f++;a=n[f]}function w(){if(g)B("Abort","ParseAbort","Parsing was aborted by the user's step function");if(l)B("Quotes","MissingQuotes","Unescaped or mismatched quotes");A();if(!q(s))return I()}function E(){if(P()&&!D())l=!l;else{C();if(l&&D())f++;else B("Quotes","UnexpectedQuotes","Unexpected quotes")}}function S(){if(M(f)||_(f))c++;C()}function x(){if(a==r)k();else if(M(f)){L();b()}else if(_(f))L();else if(T())N();else C()}function T(){if(!i)return false;var e=f==0||_(f-1)||M(f-2);return e&&n[f]===i}function N(){while(!M(f)&&!_(f)&&f<n.length){b()}}function C(){h[d][v]+=a}function k(){h[d].push("");v=h[d].length-1}function L(){A();c++;m++;h.push([]);d=h.length-1;k()}function A(){O();if(q(s)){if(h[d])s(I());F()}}function O(){if(h[d].length==1&&t.test(h[d][0])){if(e.keepEmptyRows)h[d].splice(0,1);else h.splice(d,1);d=h.length-1}}function M(e){return e<n.length-1&&(n[e]=="\r"&&n[e+1]=="\n"||n[e]=="\n"&&n[e+1]=="\r")}function _(e){return n[e]=="\r"||n[e]=="\n"}function D(){return!P()&&f<n.length-1&&n[f+1]=='"'}function P(){return!l&&H(f-1)||H(f+1)}function H(e){if(typeof e!="number")e=f;var t=n[e];return e<=-1||e>=n.length||t==r||t=="\r"||t=="\n"}function B(e,t,n){p.push({type:e,code:t,message:n,line:c,row:d,index:f})}function j(e){n=e;l=false;f=0,m=0,c=1;F();h=[[""]];a=n[f]}function F(){h=[];p=[];d=0;v=0}function I(){return{data:h,errors:p,meta:{lines:c,delimiter:r,aborted:g,truncated:u>0&&f<n.length}}}function q(e){return typeof e==="function"}var t=/^\s*$/;var n;var r;var i;var s;var o;var u;var a;var f;var l;var c;var h;var p;var d;var v;var m;var g=false;e=e||{};r=e.delimiter;i=e.comments;s=e.step;u=e.preview;if(typeof r!=="string"||r.length!=1)r=",";if(i===true)i="#";else if(typeof i!=="string"||i.length!=1||i==r)i=false;this.parse=function(e){if(typeof e!=="string")throw"Input must be a string";j(e);return y()};this.abort=function(){g=true};this.getCharIndex=function(){return f}};
</script>

<!-- CSV strings to parse -->
<script>
var csvString = "status,status_description,status_type\n100,Continue,Informational\n101,Switching Protocols,Informational\n200,OK,Successful\n201,Created,Successful\n202,Accepted,Successful\n203,Non-Authoritative Information,Successful\n204,No Content,Successful\n205,Reset Content,Successful\n206,Partial Content,Successful\n300,Multiple Choices,Redirection\n301,Moved Permanently,Redirection\n302,Found,Redirection\n303,See Other,Redirection\n304,Not Modified,Redirection\n305,Use Proxy,Redirection\n307,Temporary Redirect,Redirection\n400,Bad Request,Client Error\n401,Unauthorized,Client Error\n402,Payment Required,Client Error\n403,Forbidden,Client Error\n404,Not Found,Client Error\n405,Method Not Allowed,Client Error\n406,Not Acceptable,Client Error\n407,Proxy Authentication Required,Client Error\n408,Request Timeout,Client Error\n409,Conflict,Client Error\n410,Gone,Client Error\n411,Length Required,Client Error\n412,Precondition Failed,Client Error\n413,Request Entity Too Large,Client Error\n414,Request-URI Too Long,Client Error\n415,Unsupported Media Type,Client Error\n416,Requested Range Not Satisfiable,Client Error\n417,Expectation Failed,Client Error\n500,Internal Server Error,Server Error\n501,Not Implemented,Server Error\n502,Bad Gateway,Server Error\n503,Service Unavailable,Server Error\n504,Gateway Timeout,Server Error\n505,HTTP Version Not Supported,Server Error";



var csvStringSimple = "status,status_description,status_type\n100,Continue,Informational\n101,Switching Protocols,Informational\n200,OK,Successful\n201,Created,Successful\n202,Accepted,Successful\n203,Non-Authoritative Information,Successful\n204,No Content,Successful\n205,Reset Content,Successful\n206,Partial Content,Successful\n300,Multiple Choices,Redirection\n301,Moved Permanently,Redirection\n302,Found,Redirection\n303,See Other,Redirection\n304,Not Modified,Redirection\n305,Use Proxy,Redirection\n307,Temporary Redirect,Redirection\n400,Bad Request,Client Error\n401,Unauthorized,Client Error\n402,Payment Required,Client Error\n403,Forbidden,Client Error\n404,Not Found,Client Error\n405,Method Not Allowed,Client Error\n406,Not Acceptable,Client Error\n407,Proxy Authentication Required,Client Error\n408,Request Timeout,Client Error\n409,Conflict,Client Error\n410,Gone,Client Error\n411,Length Required,Client Error\n412,Precondition Failed,Client Error\n413,Request Entity Too Large,Client Error\n414,Request-URI Too Long,Client Error\n415,Unsupported Media Type,Client Error\n416,Requested Range Not Satisfiable,Client Error\n417,Expectation Failed,Client Error\n500,Internal Server Error,Server Error\n501,Not Implemented,Server Error\n502,Bad Gateway,Server Error\n503,Service Unavailable,Server Error\n504,Gateway Timeout,Server Error\n505,HTTP Version Not Supported,Server Error";
</script>

<!-- Prepare the parsers -->
<script>
var papa = new Papa.Parser();
var fastPapa = new Papa.Parser({ fastMode: true });
var oldPapa = new Papa3Parser();
var knrzCsv = new CSV(csvString);
var jsonObj = papa.parse(csvString);
var jsonString = JSON.stringify(jsonObj);
</script>
      
<script>
Benchmark.prototype.setup = function() {
  function DMparse(line) {
    var elem = [''];
    var elemCount = 0;
    var charCount = 0;
    for (x = 0; x < line.length; x++) {
      if (line[x] == ',' && line[x - 1] != '\\') {
        elemCount++;
        elem[elemCount] = '';
        charCount = 0;
      } else if (charCount == 0 && line[x] == '\\' && line[x + 1] == 'N' && (line[x + 2] == ',' || (x + 2) >= line.length)) {
        x = x + 2;
        if (x < line.length) {
          elemCount++;
          elem[elemCount] = '';
        }
      } else {
        if (line[x] == ',') {
          elem[elemCount] = elem[elemCount].substr(0, elem[elemCount].length - 1) + ',';
        } else {
          elem[elemCount] = elem[elemCount] + line[x];
          charCount++;
        }
      }
    }
    return elem;
  }
  
  var ESCAPED_CHARS = [',', ':', ';'];
  CSVParse = function(str) {
    var currentChars = '';
    var output = [];
  
    for (var i = 0, j = str.length; i < j; i++) {
      if (str[i] === ',') {
        output.push(currentChars);
        currentChars = '';
      } else if (str[i] === '\\') {
        if (ESCAPED_CHARS.indexOf(str[i + 1]) !== -1) {
          currentChars += str[++i];
        } else if (str[i + 1] === 'N' &&
          ((i - 1 < 0) || (str[i - 1] === ',')) &&
          ((i + 2 >= str.length) || (str[i + 2] === ','))) {
          i++;
        } else {
          currentChars += str[i];
        }
      } else {
        currentChars += str[i];
      }
    }
  
    output.push(currentChars);
    return output;
  };

};
</script>

Preparation code output

<!-- jQuery --> <!-- CSV libraries --> <script src="http://jquery-csv.googlecode.com/git/src/jquery.csv.js"></script> <script src="https://rawgit.com/henix/csv.js/master/csv.js"></script> <script>var henixCsv = CSV; // avoid namespace collision</script> <script src="https://rawgit.com/knrz/CSV.js/master/csv.min.js"></script> <script src="https://rawgit.com/mholt/PapaParse/master/papaparse.min.js"></script> <script> // Papa Parse 3.1 version of the parser function Papa3Parser(e){function y(){while(f<n.length){if(g)break;if(u>0&&m>=u)break;if(a=='"')E();else if(l)S();else x();b()}return w()}function b(){f++;a=n[f]}function w(){if(g)B("Abort","ParseAbort","Parsing was aborted by the user's step function");if(l)B("Quotes","MissingQuotes","Unescaped or mismatched quotes");A();if(!q(s))return I()}function E(){if(P()&&!D())l=!l;else{C();if(l&&D())f++;else B("Quotes","UnexpectedQuotes","Unexpected quotes")}}function S(){if(M(f)||_(f))c++;C()}function x(){if(a==r)k();else if(M(f)){L();b()}else if(_(f))L();else if(T())N();else C()}function T(){if(!i)return false;var e=f==0||_(f-1)||M(f-2);return e&&n[f]===i}function N(){while(!M(f)&&!_(f)&&f<n.length){b()}}function C(){h[d][v]+=a}function k(){h[d].push("");v=h[d].length-1}function L(){A();c++;m++;h.push([]);d=h.length-1;k()}function A(){O();if(q(s)){if(h[d])s(I());F()}}function O(){if(h[d].length==1&&t.test(h[d][0])){if(e.keepEmptyRows)h[d].splice(0,1);else h.splice(d,1);d=h.length-1}}function M(e){return e<n.length-1&&(n[e]=="\r"&&n[e+1]=="\n"||n[e]=="\n"&&n[e+1]=="\r")}function _(e){return n[e]=="\r"||n[e]=="\n"}function D(){return!P()&&f<n.length-1&&n[f+1]=='"'}function P(){return!l&&H(f-1)||H(f+1)}function H(e){if(typeof e!="number")e=f;var t=n[e];return e<=-1||e>=n.length||t==r||t=="\r"||t=="\n"}function B(e,t,n){p.push({type:e,code:t,message:n,line:c,row:d,index:f})}function j(e){n=e;l=false;f=0,m=0,c=1;F();h=[[""]];a=n[f]}function F(){h=[];p=[];d=0;v=0}function I(){return{data:h,errors:p,meta:{lines:c,delimiter:r,aborted:g,truncated:u>0&&f<n.length}}}function q(e){return typeof e==="function"}var t=/^\s*$/;var n;var r;var i;var s;var o;var u;var a;var f;var l;var c;var h;var p;var d;var v;var m;var g=false;e=e||{};r=e.delimiter;i=e.comments;s=e.step;u=e.preview;if(typeof r!=="string"||r.length!=1)r=",";if(i===true)i="#";else if(typeof i!=="string"||i.length!=1||i==r)i=false;this.parse=function(e){if(typeof e!=="string")throw"Input must be a string";j(e);return y()};this.abort=function(){g=true};this.getCharIndex=function(){return f}}; </script> <!-- CSV strings to parse --> <script> var csvString = "status,status_description,status_type\n100,Continue,Informational\n101,Switching Protocols,Informational\n200,OK,Successful\n201,Created,Successful\n202,Accepted,Successful\n203,Non-Authoritative Information,Successful\n204,No Content,Successful\n205,Reset Content,Successful\n206,Partial Content,Successful\n300,Multiple Choices,Redirection\n301,Moved Permanently,Redirection\n302,Found,Redirection\n303,See Other,Redirection\n304,Not Modified,Redirection\n305,Use Proxy,Redirection\n307,Temporary Redirect,Redirection\n400,Bad Request,Client Error\n401,Unauthorized,Client Error\n402,Payment Required,Client Error\n403,Forbidden,Client Error\n404,Not Found,Client Error\n405,Method Not Allowed,Client Error\n406,Not Acceptable,Client Error\n407,Proxy Authentication Required,Client Error\n408,Request Timeout,Client Error\n409,Conflict,Client Error\n410,Gone,Client Error\n411,Length Required,Client Error\n412,Precondition Failed,Client Error\n413,Request Entity Too Large,Client Error\n414,Request-URI Too Long,Client Error\n415,Unsupported Media Type,Client Error\n416,Requested Range Not Satisfiable,Client Error\n417,Expectation Failed,Client Error\n500,Internal Server Error,Server Error\n501,Not Implemented,Server Error\n502,Bad Gateway,Server Error\n503,Service Unavailable,Server Error\n504,Gateway Timeout,Server Error\n505,HTTP Version Not Supported,Server Error"; var csvStringSimple = "status,status_description,status_type\n100,Continue,Informational\n101,Switching Protocols,Informational\n200,OK,Successful\n201,Created,Successful\n202,Accepted,Successful\n203,Non-Authoritative Information,Successful\n204,No Content,Successful\n205,Reset Content,Successful\n206,Partial Content,Successful\n300,Multiple Choices,Redirection\n301,Moved Permanently,Redirection\n302,Found,Redirection\n303,See Other,Redirection\n304,Not Modified,Redirection\n305,Use Proxy,Redirection\n307,Temporary Redirect,Redirection\n400,Bad Request,Client Error\n401,Unauthorized,Client Error\n402,Payment Required,Client Error\n403,Forbidden,Client Error\n404,Not Found,Client Error\n405,Method Not Allowed,Client Error\n406,Not Acceptable,Client Error\n407,Proxy Authentication Required,Client Error\n408,Request Timeout,Client Error\n409,Conflict,Client Error\n410,Gone,Client Error\n411,Length Required,Client Error\n412,Precondition Failed,Client Error\n413,Request Entity Too Large,Client Error\n414,Request-URI Too Long,Client Error\n415,Unsupported Media Type,Client Error\n416,Requested Range Not Satisfiable,Client Error\n417,Expectation Failed,Client Error\n500,Internal Server Error,Server Error\n501,Not Implemented,Server Error\n502,Bad Gateway,Server Error\n503,Service Unavailable,Server Error\n504,Gateway Timeout,Server Error\n505,HTTP Version Not Supported,Server Error"; </script> <!-- Prepare the parsers --> <script> var papa = new Papa.Parser(); var fastPapa = new Papa.Parser({ fastMode: true }); var oldPapa = new Papa3Parser(); var knrzCsv = new CSV(csvString); var jsonObj = papa.parse(csvString); var jsonString = JSON.stringify(jsonObj); </script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
JSON parse
JSON.parse(jsonString);
pending…
Papa Parse
papa.parse(csvString);
pending…
Papa Parse (fast mode, no quoted fields)
fastPapa.parse(csvStringSimple);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments