@@ -2432,5 +2432,111 @@ public static string Interpolate(this string format, IEnumerable<KeyValuePair<st
24322432
24332433 return string . Format ( indexed , parameterValues ) ;
24342434 }
2435+
2436+ /// <summary>
2437+ /// Parses the string as a CSV document.
2438+ /// </summary>
2439+ /// <param name="csv">The string to be parsed</param>
2440+ /// <returns>An array of rows parsed as CSV data.</returns>
2441+ public static string [ ] [ ] ParseAsCSV ( this string csv )
2442+ {
2443+ using StringReader reader = new ( csv ) ;
2444+ List < string [ ] > rows = [ ] ;
2445+
2446+ while ( true )
2447+ {
2448+ string [ ] row = ReadCSVRow ( reader ) ;
2449+ if ( row is null ) break ;
2450+ rows . Add ( row ) ;
2451+ }
2452+
2453+ return rows . ToArray ( ) ;
2454+ }
2455+
2456+ /// <summary>
2457+ /// Reads characters from the text reader and returns a single row of CSV data.
2458+ /// </summary>
2459+ /// <param name="reader">The text reader providing the CSV data</param>
2460+ /// <returns>An array of fields in one row of CSV data or <c>null</c> if there is no more data available from the text reader.</returns>
2461+ public static string [ ] ReadCSVRow ( this TextReader reader )
2462+ {
2463+ List < string > fields = new List < string > ( ) ;
2464+ int c = reader . Read ( ) ;
2465+
2466+ if ( EOF ( ) )
2467+ return null ;
2468+
2469+ while ( ! EOF ( ) && ! EOL ( ) )
2470+ {
2471+ if ( Matches ( '"' ) )
2472+ fields . Add ( ReadQuoted ( ) ) ;
2473+ else
2474+ fields . Add ( ReadToComma ( ) ) ;
2475+
2476+ if ( Matches ( ',' ) )
2477+ {
2478+ Advance ( ) ;
2479+
2480+ // Edge case for when the last
2481+ // field in a row is empty
2482+ if ( EOF ( ) || EOL ( ) )
2483+ fields . Add ( string . Empty ) ;
2484+ }
2485+ }
2486+
2487+ // Advance to the next line before returning
2488+ if ( Matches ( '\r ' ) ) Advance ( ) ;
2489+ if ( Matches ( '\n ' ) ) Advance ( ) ;
2490+ return fields . ToArray ( ) ;
2491+
2492+ // Reads the next character into c and returns the previous value of c
2493+ char Advance ( ) => ( char ) ( c , c = reader . Read ( ) ) . c ;
2494+
2495+ bool Matches ( char m ) => c == m ;
2496+ bool EOL ( ) => Matches ( '\r ' ) || Matches ( '\n ' ) ;
2497+ bool EOF ( ) => c == - 1 ;
2498+
2499+ string ReadToComma ( )
2500+ {
2501+ StringBuilder token = new StringBuilder ( ) ;
2502+
2503+ while ( ! EOF ( ) && ! EOL ( ) && ! Matches ( ',' ) )
2504+ token . Append ( Advance ( ) ) ;
2505+
2506+ return token . ToString ( ) ;
2507+ }
2508+
2509+ string ReadQuoted ( )
2510+ {
2511+ StringBuilder token = new StringBuilder ( ) ;
2512+
2513+ // Skip past the opening quote
2514+ Advance ( ) ;
2515+
2516+ while ( true )
2517+ {
2518+ while ( ! EOF ( ) && ! Matches ( '"' ) )
2519+ token . Append ( Advance ( ) ) ;
2520+
2521+ // Skip past the end quote
2522+ if ( ! EOF ( ) )
2523+ Advance ( ) ;
2524+
2525+ // Check if it's actually an end quote vs an escaped quote
2526+ if ( Matches ( '"' ) )
2527+ token . Append ( Advance ( ) ) ;
2528+ else
2529+ break ;
2530+
2531+ }
2532+
2533+ // Excel treats everything after the
2534+ // end quote as if it were not quoted
2535+ if ( ! EOF ( ) && ! EOL ( ) && ! Matches ( ',' ) )
2536+ token . Append ( ReadToComma ( ) ) ;
2537+
2538+ return token . ToString ( ) ;
2539+ }
2540+ }
24352541 }
24362542}
0 commit comments