Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for extractexcel.cs

(view source code of extractexcel.cs as plain text)

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Text;
  9. using System.Text.RegularExpressions;
  10. using System.Threading;
  11. using mXparser = org.mariuszgromada.math.mxparser;
  12. using Excel = Microsoft.Office.Interop.Excel;
  13.  
  14.  
  15. namespace RobvanderWoude
  16. {
  17. 	class ExtractExcel
  18. 	{
  19. 		#region Global Variables
  20.  
  21. 		static readonly string progver = "1.04";
  22.  
  23. 		static bool closewithoutprompt = false;
  24. 		static Excel.Application excelApp;
  25. 		static readonly object readOnly = true;
  26. 		static readonly object readWrite = false;
  27. 		static readonly object missing = Missing.Value;
  28. 		static readonly object noSaveChanges = false;
  29. 		static readonly object saveChanges = true;
  30. 		static Excel.Workbook workbookSource;
  31. 		static Excel.Worksheet workseetSource;
  32. 		static Excel.Workbook workbookTarget;
  33. 		static Excel.Worksheet worksheetTarget;
  34. 		static string sourcefile = string.Empty;
  35. 		static string sourcesheet = string.Empty;
  36. 		static string targetfile = string.Empty;
  37. 		static string targetsheet = string.Empty;
  38. 		static int startrowsource = 1;
  39. 		static int startrowtarget = 1;
  40. 		static List<string> cellsempty = new List<string>( );
  41. 		static List<string> cellsnotempty = new List<string>( );
  42. 		static List<string> columns = new List<string>( );
  43. 		static List<int> headerlines = new List<int>( );
  44. 		static List<int> rows = new List<int>( );
  45. 		static string query = string.Empty;
  46. 		static readonly string parentfolder = Path.GetDirectoryName( new System.Uri( System.Reflection.Assembly.GetExecutingAssembly( ).CodeBase ).LocalPath );
  47.  
  48. 		#endregion Global Variables
  49.  
  50.  
  51. 		static int Main( string[] args )
  52. 		{
  53. 			int rc = 0;
  54.  
  55.  
  56. 			#region mXparser License Requirements
  57.  
  58. 			mXparser.License.iConfirmNonCommercialUse( "Rob van der Woude" );
  59. 			string resource = "ExtractExcel.MathParser.org-mXparser.LICENSE.txt";
  60. 			string readme = "MathParser.org-mXparser.LICENSE.txt";
  61. 			if ( !File.Exists( Path.Combine( parentfolder, resource ) ) )
  62. 			{
  63. 				ExtractEmbeddedResource( resource, readme );
  64. 			}
  65.  
  66. 			#endregion mXparser License Requirements
  67.  
  68.  
  69. 			#region Parse Command Line
  70.  
  71. 			if ( args.Length < 6 || args.Contains( "/?" ) )
  72. 			{
  73. 				return ShowHelp( );
  74. 			}
  75.  
  76. 			for ( int i = 0; i < args.Length; i++ )
  77. 			{
  78. 				if ( args[i][0] == '/' )
  79. 				{
  80. 					switch ( args[i].ToUpper( ) )
  81. 					{
  82. 						case "/SOURCE":
  83. 							sourcefile = args[i + 1].Trim( );
  84. 							if ( args.Length > i + 1 && args[i + 2][0] != '/' )
  85. 							{
  86. 								sourcesheet = args[i + 2].Trim( );
  87. 							}
  88. 							break;
  89. 						case "/TARGET":
  90. 							targetfile = args[i + 1].Trim( );
  91. 							if ( args[i + 2][0] != '/' )
  92. 							{
  93. 								targetsheet = args[i + 2].Trim( );
  94. 							}
  95. 							break;
  96. 						case "/ROWS":
  97. 							rows = Range2Rows( args[i + 1].Trim( ) );
  98. 							break;
  99. 						case "/COLS":
  100. 						case "/COLUMNS":
  101. 							columns = Range2Columns( args[i + 1].Trim( ) );
  102. 							break;
  103. 						case "/HEADER":
  104. 							headerlines = Range2Rows( args[i + 1].Trim( ) );
  105. 							break;
  106. 						case "/SKIP":
  107. 							startrowsource = 1 + int.Parse( args[i + 1].Trim( ) );
  108. 							break;
  109. 						case "/OFFSET":
  110. 							startrowtarget = int.Parse( args[i + 1].Trim( ) );
  111. 							break;
  112. 						case "/EMPTY":
  113. 						case "/ISEMPTY":
  114. 						case "/ISNULL":
  115. 							cellsempty = Range2Columns( args[i + 1].Trim( ) );
  116. 							break;
  117. 						case "/NOTEMPTY":
  118. 							cellsnotempty = Range2Columns( args[i + 1].Trim( ) );
  119. 							break;
  120. 						case "/WHERE":
  121. 							query = args[i + 1].Trim( );
  122. 							break;
  123. 						default:
  124. 							return ShowHelp( );
  125. 					}
  126. 				}
  127. 			}
  128.  
  129. 			if ( columns.Count == 0 )
  130. 			{
  131. 				if ( rows.Count == 0 )
  132. 				{
  133. 					return ShowHelp( "Specify rows or columns or both" );
  134. 				}
  135. 				else
  136. 				{
  137. 					columns = Range2Columns( "A..Z" );
  138. 				}
  139. 			}
  140.  
  141. 			if ( rows.Count == 0 )
  142. 			{
  143. 				rows = Range2Rows( "1..100" );
  144. 			}
  145.  
  146. 			if ( startrowsource > 1 )
  147. 			{
  148. 				startrowsource = rows.First( ) + startrowsource;
  149. 				int lastrowsource = rows.Last( ) + startrowsource;
  150. 				rows = Range2Rows( string.Format( "{0}..{1}", startrowsource, lastrowsource ) );
  151. 			}
  152.  
  153. 			#endregion Parse Command Line
  154.  
  155.  
  156. 			// Make sure Excel is not active
  157. 			KillExcelIfActive( );
  158.  
  159. 			excelApp = new Excel.Application( );
  160.  
  161.  
  162. 			#region Prepare Excel Source and Target Files
  163.  
  164. 			if ( IsOpened( sourcefile, excelApp ) )
  165. 			{
  166. 				Console.Error.WriteLine( "Excel file \"{0}\" in use.\nPlease close it and try again.", sourcefile );
  167. 				return -1;
  168. 			}
  169.  
  170. 			excelApp.Visible = true;
  171. 			workbookSource = excelApp.Workbooks.Open( sourcefile, 0, readOnly, missing, "", "", false, Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false );
  172. 			if ( string.IsNullOrEmpty( sourcesheet ) )
  173. 			{
  174. 				workseetSource = (Excel.Worksheet) workbookSource.Worksheets[1];
  175. 			}
  176. 			else if ( int.TryParse( sourcesheet, out int sheetindex ) )
  177. 			{
  178. 				workseetSource = (Excel.Worksheet) workbookSource.Worksheets[sheetindex];
  179. 			}
  180. 			else
  181. 			{
  182. 				workseetSource = (Excel.Worksheet) workbookSource.Worksheets[sourcesheet];
  183. 			}
  184. 			workseetSource = (Excel.Worksheet) workbookSource.ActiveSheet;
  185.  
  186. 			if ( File.Exists( targetfile ) )
  187. 			{
  188. 				if ( IsOpened( targetfile, excelApp ) )
  189. 				{
  190. 					Console.Error.WriteLine( "Excel file \"{0}\" in use.\nPlease close it and try again.", targetfile );
  191. 					return -1;
  192. 				}
  193. 				workbookTarget = excelApp.Workbooks.Open( targetfile, 0, readWrite, missing, "", "", false, Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false );
  194. 			}
  195. 			else
  196. 			{
  197. 				// Create target file
  198. 				workbookTarget = excelApp.Workbooks.Add( );
  199. 				workbookTarget.SaveAs( targetfile );
  200. 				targetsheet = string.Empty;
  201. 			}
  202.  
  203. 			if ( string.IsNullOrEmpty( targetsheet ) )
  204. 			{
  205. 				worksheetTarget = (Excel.Worksheet) workbookTarget.Worksheets[1];
  206. 			}
  207. 			else if ( int.TryParse( targetsheet, out int sheetindex ) )
  208. 			{
  209. 				worksheetTarget = (Excel.Worksheet) workbookTarget.Worksheets[sheetindex];
  210. 			}
  211. 			else
  212. 			{
  213. 				worksheetTarget = (Excel.Worksheet) workbookTarget.Worksheets[targetsheet];
  214. 			}
  215. 			worksheetTarget = (Excel.Worksheet) workbookTarget.ActiveSheet;
  216.  
  217. 			#endregion Prepare Excel Source and Target Files
  218.  
  219.  
  220. 			try
  221. 			{
  222. 				int targetrow = startrowtarget;
  223. 				foreach ( int sourcerow in rows )
  224. 				{
  225. 					if ( CopyExcelRow( sourcerow, targetrow ) )
  226. 					{
  227. 						targetrow++;
  228. 					}
  229. 					else
  230. 					{
  231. 						rc = -1;
  232. 					}
  233. 				}
  234. 			}
  235. 			catch ( Exception ex )
  236. 			{
  237. 				Console.Error.WriteLine( "Error: {0}\n{1}", ex.Message, ex.StackTrace );
  238. 				rc = -1;
  239. 			}
  240. 			finally
  241. 			{
  242. 				if ( !ShutdownExcel( ) )
  243. 				{
  244. 					rc = -1;
  245. 				}
  246. 			}
  247. 			return rc;
  248. 		}
  249.  
  250.  
  251. 		static bool CopyExcelRow( int sourcerow, int targetrow )
  252. 		{
  253. 			bool success = true;
  254.  
  255. 			// /HEADER
  256. 			if ( headerlines.Count > 0 )
  257. 			{
  258. 				foreach ( int row in headerlines )
  259. 				{
  260. 					if ( row == sourcerow )
  261. 					{
  262. 						return true;
  263. 					}
  264. 				}
  265. 			}
  266.  
  267. 			// /EMPTY
  268. 			if ( cellsempty.Count > 0 )
  269. 			{
  270. 				foreach ( string cell in cellsempty )
  271. 				{
  272. 					if ( ReadCell( workseetSource, sourcerow, cell ) != null )
  273. 					{
  274. 						return false;
  275. 					}
  276. 				}
  277. 			}
  278.  
  279. 			// /NOTEMPTY
  280. 			if ( cellsnotempty.Count > 0 )
  281. 			{
  282. 				foreach ( string cell in cellsnotempty )
  283. 				{
  284. 					if ( ReadCell( workseetSource, sourcerow, cell ) == null )
  285. 					{
  286. 						return false;
  287. 					}
  288. 				}
  289. 			}
  290.  
  291. 			bool valid = true;
  292.  
  293. 			// /WHERE
  294. 			if ( !string.IsNullOrWhiteSpace( query ) )
  295. 			{
  296. 				// Replace cell name with cell value
  297. 				// based on code by Vladimir
  298. 				// https://stackoverflow.com/a/62778031
  299. 				string currentquery = query;
  300. 				string pattern = @"\b([A-Z]{1,3})\b";
  301. 				RegexOptions options = RegexOptions.None; // case sensitive
  302. 				Match match;
  303. 				while ( ( match = Regex.Match( currentquery, pattern, options ) ).Success )
  304. 				{
  305. 					var group = match.Groups[1];
  306. 					var sb = new StringBuilder( );
  307. 					// Anything before the match
  308. 					if ( match.Index > 0 )
  309. 					{
  310. 						sb.Append( currentquery.Substring( 0, match.Index ) );
  311. 					}
  312. 					// The match itself
  313. 					var startIndex = group.Index - match.Index;
  314. 					var length = group.Length;
  315. 					var original = match.Value;
  316. 					var prior = original.Substring( 0, startIndex );
  317. 					var trailing = original.Substring( startIndex + length );
  318. 					sb.Append( prior );
  319. 					object cellvalue = ReadCell( workseetSource, sourcerow, group.Value );
  320. 					if ( cellvalue == null )
  321. 					{
  322. 						return false;
  323. 					}
  324. 					if ( cellvalue.GetType( ) == typeof( Single ) || cellvalue.GetType( ) == typeof( Double ) )
  325. 					{
  326. 						sb.Append( FormatFloat( cellvalue ) );
  327. 					}
  328. 					else
  329. 					{
  330. 						sb.Append( cellvalue );
  331. 					}
  332. 					sb.Append( trailing );
  333. 					// Anything after the match
  334. 					if ( match.Index + match.Length < currentquery.Length )
  335. 					{
  336. 						sb.Append( currentquery.Substring( match.Index + match.Length ) );
  337. 					}
  338. 					currentquery = sb.ToString( );
  339. 				}
  340.  
  341. 				org.mariuszgromada.math.mxparser.Expression expression = new org.mariuszgromada.math.mxparser.Expression( currentquery );
  342. 				if ( !expression.checkSyntax( ) )
  343. 				{
  344. 					return false;
  345. 				}
  346.  
  347. 				valid = valid && ( expression.calculate( ) == 1.0 );
  348. 			}
  349.  
  350. 			if ( valid )
  351. 			{
  352. 				int targetcolumn = 1;
  353. 				foreach ( string sourcecolumn in columns )
  354. 				{
  355. 					object cellValue = ReadCell( workseetSource, sourcerow, sourcecolumn );
  356. 					if ( !WriteCell( worksheetTarget, targetrow, targetcolumn, cellValue ) )
  357. 					{
  358. 						success = false;
  359. 					}
  360. 					targetcolumn++;
  361. 				}
  362. 			}
  363.  
  364. 			return success;
  365. 		}
  366.  
  367.  
  368. 		static bool Empty( object value )
  369. 		{
  370. 			if ( value == null )
  371. 			{
  372. 				return true;
  373. 			}
  374. 			if ( string.IsNullOrEmpty( value.ToString( ) ) )
  375. 			{
  376. 				return true;
  377. 			}
  378. 			return false;
  379. 		}
  380.  
  381.  
  382. 		static void ExtractEmbeddedResource( string resourcename, string filename = null )
  383. 		{
  384. 			var embeddedResource = Assembly.GetExecutingAssembly( ).GetManifestResourceNames( ).FirstOrDefault( s => string.Compare( s, resourcename, true ) == 0 );
  385. 			if ( string.IsNullOrWhiteSpace( filename ) )
  386. 			{
  387. 				filename = resourcename;
  388. 			}
  389. 			// Ensure that the file is in the program's parent folder
  390. 			filename = Path.Combine( parentfolder, Path.GetFileName( filename ) );
  391. 			resourcename = "ExtractExcel." + resourcename;
  392. 			Stream stream = Assembly.GetExecutingAssembly( ).GetManifestResourceStream( embeddedResource );
  393. 			byte[] bytes = new byte[(int) stream.Length];
  394. 			stream.Read( bytes, 0, bytes.Length );
  395. 			stream.Close( );
  396. 			File.WriteAllBytes( filename, bytes );
  397. 		}
  398.  
  399.  
  400. 		static string FormatFloat( Double val )
  401. 		{
  402. 			return val.ToString( new CultureInfo( "en-US" ) );
  403. 		}
  404.  
  405.  
  406. 		static string FormatFloat( object val )
  407. 		{
  408. 			return ( (Double) val ).ToString( new CultureInfo( "en-US" ) );
  409. 		}
  410.  
  411.  
  412. 		static string FormatFloat( Single val )
  413. 		{
  414. 			return val.ToString( new CultureInfo( "en-US" ) );
  415. 		}
  416.  
  417.  
  418. 		static bool InRange( char col, string colrange )
  419. 		{
  420. 			return InRange( col.ToString( ), colrange );
  421. 		}
  422.  
  423.  
  424. 		static bool InRange( string col, string colrange )
  425. 		{
  426. 			List<string> columns = Range2Columns( colrange );
  427. 			if ( columns.Contains( col.ToUpper( ) ) )
  428. 			{
  429. 				return true;
  430. 			}
  431. 			return false;
  432. 		}
  433.  
  434.  
  435. 		static bool InRange( int col, string colrange )
  436. 		{
  437. 			List<string> columns = Range2Columns( colrange );
  438. 			if ( columns.Contains( col.ToString( ) ) )
  439. 			{
  440. 				return true;
  441. 			}
  442. 			if ( columns.Contains( ( (char) ( col + 64 ) ).ToString( ) ) )
  443. 			{
  444. 				return true;
  445. 			}
  446. 			return false;
  447. 		}
  448.  
  449.  
  450. 		static bool InRange( int col, int[] colrange )
  451. 		{
  452. 			return ( colrange.Contains( col ) );
  453. 		}
  454.  
  455.  
  456. 		static bool IsOpened( string excelfile, Excel.Application xlAppRef )
  457. 		{
  458. 			bool isOpened = true;
  459. 			try
  460. 			{
  461. 				// wbook should be: "name-of-the-workbook.xlsx". Otherwise it will always raise the
  462. 				// exception and never return true
  463. 				var test = xlAppRef.Workbooks.Item[Path.GetFileName( excelfile )];
  464.  
  465. 			}
  466. 			catch ( Exception )
  467. 			{
  468. 				isOpened = false;
  469. 			}
  470.  
  471. 			return isOpened;
  472. 		}
  473.  
  474.  
  475. 		static bool IsActive( string progname )
  476. 		{
  477. 			using ( Process prog = Process.GetProcessesByName( progname ).FirstOrDefault( ) )
  478. 			{
  479. 				return ( prog != null );
  480. 			}
  481. 		}
  482.  
  483.  
  484. 		static void KillExcel( )
  485. 		{
  486. 			try
  487. 			{
  488. 				foreach ( Process proc in Process.GetProcessesByName( "excel" ) )
  489. 				{
  490. 					proc.Kill( );
  491. 				}
  492. 			}
  493. 			catch
  494. 			{
  495. 				// ignore
  496. 			}
  497. 		}
  498.  
  499.  
  500. 		static void KillExcelIfActive( )
  501. 		{
  502. 			bool excelactive = IsActive( "excel" );
  503. 			if ( excelactive )
  504. 			{
  505. 				if ( closewithoutprompt )
  506. 				{
  507. 					KillExcel( );
  508. 				}
  509. 				else
  510. 				{
  511. 					Thread.Sleep( 1000 );
  512. 					if ( !IsActive( "excel" ) )
  513. 					{
  514. 						return;
  515. 					}
  516. 					string message;
  517. 					message = "Excel is still active.\nClose it before continuing.\n\n";
  518. 					message += "Press Enter when Excel is closed, or Escape to abort.";
  519. 					Console.WriteLine( message );
  520. 					bool validkey = false;
  521. 					while ( !validkey )
  522. 					{
  523. 						ConsoleKey answer = Console.ReadKey( true ).Key;
  524. 						if ( answer == ConsoleKey.Enter )
  525. 						{
  526. 							closewithoutprompt = true;
  527. 							KillExcel( );
  528. 							validkey = true;
  529. 						}
  530. 						else if ( answer == ConsoleKey.Escape )
  531. 						{
  532. 							Environment.Exit( -1 );
  533. 							validkey = true;
  534. 						}
  535. 					}
  536. 				}
  537. 			}
  538. 		}
  539.  
  540.  
  541. 		static List<int> Range2Rows( string range )
  542. 		{
  543. 			List<int> rows = new List<int>( );
  544. 			foreach ( string part in range.Split( ',' ) )
  545. 			{
  546. 				if ( part.Contains( ".." ) )
  547. 				{
  548. 					char[] separator = ".".ToCharArray( );
  549. 					StringSplitOptions options = StringSplitOptions.RemoveEmptyEntries;
  550. 					int startrange = int.Parse( part.Split( separator, options )[0] );
  551. 					int endrange = int.Parse( part.Split( separator, options )[1] );
  552. 					for ( int i = (int) startrange; i <= (int) endrange; i++ )
  553. 					{
  554. 						rows.Add( i );
  555. 					}
  556. 				}
  557. 				else
  558. 				{
  559. 					rows.Add( int.Parse( part ) );
  560. 				}
  561. 			}
  562. 			return rows;
  563. 		}
  564.  
  565.  
  566. 		static List<string> Range2Columns( string range )
  567. 		{
  568. 			List<string> columns = new List<string>( );
  569. 			foreach ( string part in range.Split( ',' ) )
  570. 			{
  571. 				if ( part.Contains( ".." ) )
  572. 				{
  573. 					char startrange = part[0];
  574. 					char endrange = part[part.Length - 1];
  575. 					for ( int i = (int) startrange; i <= (int) endrange; i++ )
  576. 					{
  577. 						columns.Add( ( (char) i ).ToString( ).ToUpper( ) );
  578. 					}
  579. 				}
  580. 				else
  581. 				{
  582. 					columns.Add( part.ToUpper( ) );
  583. 				}
  584. 			}
  585. 			return columns;
  586. 		}
  587.  
  588.  
  589. 		static object ReadCell( Excel.Worksheet worksheet, int row, string col )
  590. 		{
  591. 			int colnum = Convert.ToInt32( col.ToUpper( )[0] ) - 64;
  592. 			// the following lines allow up to 3 letters for columns
  593. 			if ( col.Length > 1 )
  594. 			{
  595. 				colnum = 26 * colnum + Convert.ToInt32( col.ToUpper( )[1] ) - 64;
  596. 				if ( col.Length > 2 )
  597. 				{
  598. 					colnum = 26 * colnum + Convert.ToInt32( col.ToUpper( )[2] ) - 64;
  599. 				}
  600. 			}
  601. 			Excel.Range cell = (Excel.Range) worksheet.Range[worksheet.Cells[row, colnum], worksheet.Cells[row, colnum]];
  602. 			return cell.Value;
  603. 		}
  604.  
  605.  
  606. 		static object ReadCell( Excel.Worksheet worksheet, int row, int col )
  607. 		{
  608. 			Excel.Range cell = (Excel.Range) worksheet.Range[worksheet.Cells[row, col], worksheet.Cells[row, col]];
  609. 			return cell.Value;
  610. 		}
  611.  
  612.  
  613. 		static int ShowHelp( params string[] errmsg )
  614. 		{
  615. 			#region Error Message
  616.  
  617. 			if ( errmsg.Length > 0 )
  618. 			{
  619. 				List<string> errargs = new List<string>( errmsg );
  620. 				errargs.RemoveAt( 0 );
  621. 				Console.Error.WriteLine( );
  622. 				Console.ForegroundColor = ConsoleColor.Red;
  623. 				Console.Error.Write( "ERROR:\t" );
  624. 				Console.ForegroundColor = ConsoleColor.White;
  625. 				Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
  626. 				Console.ResetColor( );
  627. 			}
  628.  
  629. 			#endregion Error Message
  630.  
  631.  
  632. 			#region Help Text
  633.  
  634. 			/*
  635. 			ExtractExcel,  Version 1.04
  636. 			Filter values from a source spreadsheet and write them to a target spreadsheet
  637.  
  638. 			Usage:   ExtractExcel /SOURCE src /TARGET tgt /COLS "cols" [ options ]
  639.  
  640. 			Where:   src       is the source file name and optional sheet name or index
  641. 			                   (e.g. source.xlsx "Sheet 2"; default sheet index 1)
  642. 			         tgt       is the target file name and optional sheet name or index
  643. 			                   (e.g. target.xlsx 3; default sheet index 1; file is created
  644. 			                   if it does not exist, in which case sheet will be ignored)
  645. 			         cols      is a list or range of columns to be read from the source file
  646. 			                   (letters, e.g. "A..D,F,AA..AG", case insensitive, mandatory
  647. 			                   unless rows are specified, default if rows specified: "A..Z")
  648.  
  649. 			Options: /ROWS     rows      list or range of rows to be used from source file
  650. 			                             (numeric, e.g. "1,3,5..8,14", default: "1..100")
  651. 			         /HEADER   rows      list of (header) rows to be included regardless of
  652. 			                             /ISEMPTY, /NOTEMPTY or /WHERE query match.
  653. 			         /SKIP     n         skip the first n rows in source file (if /ROWS is
  654. 			                             specified, increments start and end of range by n)
  655. 			         /OFFSET   startrow  first row in target sheet to be written
  656. 			                             (numeric, default: 1)
  657. 			         /WHERE    query     filter rule, source row will be skipped if it does
  658. 			                             not comply (see notes; default: do not skip)
  659. 			         /ISEMPTY  columns   skip row if any of specified columns is not empty
  660. 			         /NOTEMPTY columns   skip row if any of specified columns is empty
  661.  
  662. 			Notes:   Queries format is "column operator value/column", e.g. "A < 0", "B > C"
  663. 			         or "AF != 1.5"; whitespace is allowed; columns may be up to 3 letters
  664. 			         (e.g. A for column 1, AA for column 27, AAA for column 703) and must be
  665. 			         in upper case!
  666. 			         Avoid combining "complex" /ROWS ranges with /SKIP argument.
  667. 			         Also avoid using /HEADER and /OFFSET simultaniously.
  668. 			         If the source file has an extension other than ".xlsx" (e.g. ".xlsm"),
  669. 			         you may be prompted that Excel is still active, even if it is not.
  670. 			         Queries are interpreted by mXparser (https://mathparser.org/)
  671. 			         See its tutorial at https://mathparser.org/mxparser-tutorial/ for
  672. 			         details on more complex filter expressions, e.g. "(A=10)|(B<0)".
  673. 			         Return code ("errorlevel") -1 in case of errors, otherwise 0.
  674.  
  675. 			Credits: Code to replace column by cell value by Vladimir
  676. 			         https://stackoverflow.com/a/62778031
  677. 			         Query parsing by mXparser
  678. 			         https://mathparser.org/
  679. 			         Manipulating Excel files by Microsoft.Office.Interop.Excel
  680. 			         https://www.nuget.org/packages/Microsoft.Office.Interop.Excel
  681.  
  682. 			Written by Rob van der Woude
  683. 			https://www.robvanderwoude.com
  684. 			*/
  685.  
  686. 			#endregion Help Text
  687.  
  688.  
  689. 			#region Display Help Text
  690.  
  691. 			Console.Error.WriteLine( );
  692.  
  693. 			Console.Error.WriteLine( "ExtractExcel,  Version {0}", progver );
  694.  
  695. 			Console.Error.WriteLine( "Filter values from a source spreadsheet and write them to a target spreadsheet" );
  696.  
  697. 			Console.Error.WriteLine( );
  698.  
  699. 			Console.Error.Write( "Usage:   " );
  700. 			Console.ForegroundColor = ConsoleColor.White;
  701. 			Console.Error.WriteLine( "ExtractExcel /SOURCE src /TARGET tgt /COLS \"cols\" [ options ]" );
  702. 			Console.ResetColor( );
  703.  
  704. 			Console.Error.WriteLine( );
  705.  
  706. 			Console.Error.Write( "Where:   " );
  707. 			Console.ForegroundColor = ConsoleColor.White;
  708. 			Console.Error.Write( "src" );
  709. 			Console.ResetColor( );
  710. 			Console.Error.WriteLine( "       is the source file name and optional sheet name or index" );
  711.  
  712. 			Console.Error.WriteLine( "                   (e.g. source.xlsx \"Sheet 2\"; default sheet index 1)" );
  713.  
  714. 			Console.ForegroundColor = ConsoleColor.White;
  715. 			Console.Error.Write( "         tgt" );
  716. 			Console.ResetColor( );
  717. 			Console.Error.WriteLine( "       is the target file name and optional sheet name or index" );
  718.  
  719. 			Console.Error.WriteLine( "                   (e.g. target.xlsx 3; default sheet index 1; file is created" );
  720.  
  721. 			Console.Error.WriteLine( "                   if it does not exist, in which case sheet will be ignored)" );
  722.  
  723. 			Console.ForegroundColor = ConsoleColor.White;
  724. 			Console.Error.Write( "         cols" );
  725. 			Console.ResetColor( );
  726. 			Console.Error.WriteLine( "      is a list or range of columns to be read from the source file" );
  727.  
  728. 			Console.Error.WriteLine( "                   (letters, e.g. \"A..D,F,AA..AG\", case insensitive, mandatory" );
  729.  
  730. 			Console.Error.WriteLine( "                   unless rows are specified, default if rows specified: \"A..Z\")" );
  731.  
  732. 			Console.Error.WriteLine( );
  733.  
  734. 			Console.Error.Write( "Options: " );
  735. 			Console.ForegroundColor = ConsoleColor.White;
  736. 			Console.Error.Write( "/ROWS     rows" );
  737. 			Console.ResetColor( );
  738. 			Console.Error.WriteLine( "      list or range of rows to be used from source file" );
  739.  
  740. 			Console.Error.WriteLine( "                             (numeric, e.g. \"1,3,5..8,14\", default: \"1..100\")" );
  741.  
  742. 			Console.ForegroundColor = ConsoleColor.White;
  743. 			Console.Error.Write( "         /HEADER   rows" );
  744. 			Console.ResetColor( );
  745. 			Console.Error.WriteLine( "      list of (header) rows to be included regardless of" );
  746.  
  747. 			Console.Error.WriteLine( "                             /ISEMPTY, /NOTEMPTY or /WHERE query match." );
  748.  
  749. 			Console.ForegroundColor = ConsoleColor.White;
  750. 			Console.Error.Write( "         /SKIP     n" );
  751. 			Console.ResetColor( );
  752. 			Console.Error.WriteLine( "         skip the first n rows in source file (if /ROWS is" );
  753.  
  754. 			Console.Error.WriteLine( "                             specified, increments start and end of range by n)" );
  755.  
  756. 			Console.ForegroundColor = ConsoleColor.White;
  757. 			Console.Error.Write( "         /OFFSET   startrow" );
  758. 			Console.ResetColor( );
  759. 			Console.Error.WriteLine( "  first row in target sheet to be written" );
  760.  
  761. 			Console.Error.WriteLine( "                             (numeric, default: 1)" );
  762.  
  763. 			Console.ForegroundColor = ConsoleColor.White;
  764. 			Console.Error.Write( "         /WHERE    query" );
  765. 			Console.ResetColor( );
  766. 			Console.Error.WriteLine( "     filter rule, source row will be skipped if it does" );
  767.  
  768. 			Console.Error.WriteLine( "                             not comply (see notes; default: do not skip)" );
  769.  
  770. 			Console.ForegroundColor = ConsoleColor.White;
  771. 			Console.Error.Write( "         /ISEMPTY  columns" );
  772. 			Console.ResetColor( );
  773. 			Console.Error.WriteLine( "   skip row if any of specified columns is not empty" );
  774.  
  775. 			Console.ForegroundColor = ConsoleColor.White;
  776. 			Console.Error.Write( "         /NOTEMPTY columns" );
  777. 			Console.ResetColor( );
  778. 			Console.Error.WriteLine( "   skip row if any of specified columns is empty" );
  779.  
  780. 			Console.Error.WriteLine( );
  781.  
  782. 			Console.Error.WriteLine( "Notes:   Queries format is \"column operator value/column\", e.g. \"A < 0\", \"B > C\"" );
  783.  
  784. 			Console.Error.WriteLine( "         or \"AF != 1.5\"; whitespace is allowed; columns may be up to 3 letters" );
  785.  
  786. 			Console.Error.WriteLine( "         (e.g. A for column 1, AA for column 27, AAA for column 703) and must be" );
  787.  
  788. 			Console.Error.WriteLine( "         in upper case!" );
  789.  
  790. 			Console.Error.WriteLine( "         Avoid combining \"complex\" /ROWS ranges with /SKIP argument." );
  791.  
  792. 			Console.Error.WriteLine( "         Also avoid using /HEADER and /OFFSET simultaniously." );
  793.  
  794. 			Console.Error.WriteLine( "         If the source file has an extension other than \".xlsx\" (e.g. \".xlsm\")," );
  795.  
  796. 			Console.Error.WriteLine( "         you may be prompted that Excel is still active, even if it is not." );
  797.  
  798. 			Console.Error.Write( "         Queries are interpreted by mXparser (" );
  799. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  800. 			Console.Error.Write( "https://mathparser.org/" );
  801. 			Console.ResetColor( );
  802. 			Console.Error.WriteLine( ")" );
  803.  
  804. 			Console.Error.Write( "         See its tutorial at " );
  805. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  806. 			Console.Error.Write( "https://mathparser.org/mxparser-tutorial/" );
  807. 			Console.ResetColor( );
  808. 			Console.Error.WriteLine( " for" );
  809.  
  810. 			Console.Error.WriteLine( "         details on more complex filter expressions, e.g. \"(A=10)|(B<0)\"." );
  811.  
  812. 			Console.Error.WriteLine( "         Return code (\"errorlevel\") -1 in case of errors, otherwise 0." );
  813.  
  814. 			Console.Error.WriteLine( );
  815.  
  816. 			Console.Error.WriteLine( "Credits: Code to replace column by cell value by Vladimir" );
  817.  
  818. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  819. 			Console.Error.WriteLine( "         https://stackoverflow.com/a/62778031" );
  820. 			Console.ResetColor( );
  821.  
  822.  
  823.  
  824. 			Console.Error.WriteLine( "         Query parsing by mXparser" );
  825.  
  826. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  827. 			Console.Error.WriteLine( "         https://mathparser.org/" );
  828. 			Console.ResetColor( );
  829.  
  830.  
  831.  
  832. 			Console.Error.WriteLine( "         Manipulating Excel files by Microsoft.Office.Interop.Excel" );
  833.  
  834. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  835. 			Console.Error.WriteLine( "         https://www.nuget.org/packages/Microsoft.Office.Interop.Excel" );
  836. 			Console.ResetColor( );
  837.  
  838.  
  839.  
  840. 			Console.Error.WriteLine( );
  841.  
  842. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  843.  
  844. 			Console.Error.WriteLine( "https://www.robvanderwoude.com" );
  845.  
  846. 			#endregion Display Help Text
  847.  
  848.  
  849. 			return -1;
  850. 		}
  851.  
  852.  
  853. 		static bool ShutdownExcel( )
  854. 		{
  855. 			try
  856. 			{
  857. 				workbookSource.Close( noSaveChanges );
  858. 				workbookTarget.Close( saveChanges );
  859. 				excelApp.Quit( );
  860. 				return true;
  861. 			}
  862. 			catch ( Exception ex )
  863. 			{
  864. 				Console.Error.WriteLine( "Foutmelding bij afluiten Excel: " + ex.Message );
  865. 				Console.Error.WriteLine( ex.StackTrace );
  866. 				return false;
  867. 			}
  868. 		}
  869.  
  870.  
  871. 		static bool WriteCell( Excel.Worksheet worksheet, int row, int col, object value )
  872. 		{
  873. 			Excel.Range cell = (Excel.Range) worksheet.Range[worksheet.Cells[row, col], worksheet.Cells[row, col]];
  874. 			cell.Value = value;
  875. 			object trueval = ReadCell( worksheet, row, col );
  876. 			if ( Empty( value ) )
  877. 			{
  878. 				return Empty( trueval );
  879. 			}
  880. 			if ( Empty( trueval ) )
  881. 			{
  882. 				return Empty( value );
  883. 			}
  884. 			return ( value.ToString( ) == trueval.ToString( ) );
  885. 		}
  886.  
  887. 	}
  888. }

page last modified: 2025-10-11; loaded in 0.0178 seconds