Fast search through text file


Yes. different logic can be used to find those words. I just have to find time for this. :slight_smile:

			public static string GetFirstWord(string line)
				int i = 0;
				while (i < line.Length && char.IsWhiteSpace(line, i)) { i++; }
				int k = i;
				while (k < line.Length && !char.IsWhiteSpace(line, k)) { k++; }
				return line.Substring(i, k-i);
			public static string FindFirstWord(string line)
				line = line.TrimStart();
				int k = 0;
				while (k < line.Length && !char.IsWhiteSpace(line, k)) { k++; }
				return line.Substring(0, k);


1. var word = line.TrimStart().Split(null, 2)[0].ToLower();

~ 34-35

2. var word = FindFirstWord(line).ToLower();

~ 22-24

3. var word = GetFirstWord(line).ToLower();

~ 18-23

technically the #3 could be about 2 times faster than #1. but #1 looks more universal and easier to modify if needed.
My machine is not bad… it has very fast hard drive, and it makes the difference, probably.


My guess is that the difference might be more significant if we use longer lines, because the Split method is the weak link and slows things down the most.


even if the compiler would be smart enough to see that only the first array item is used all the time I doubt that it could optimize the Split method code so it doesn’t proceed after the first array item is collected. Strings and array allocations ruin all the performance.
Anyway, if I subtract 25ms for file read from slow hdd out of average 45ms, processing takes ~20+ ms which is very close to what you have. So any further optimization is kinda meaningless



public string[] Split (char[]? separator, int count);

you can specify the maximum number of substrings (what I do). It means the algorithm stops after (count-1) split. But the problem is in additional memory allocation for this method. The system has to allocate enough space to place both (all) parts after split.


Guys, side question. :slight_smile:

How to prevent the struct members to be printed in the maxscript listener?

	global miauuStr	
	struct miauuTestStr
		defaultLayer = layerManager.getLayer 0,
		numMarks = 0,
		numMarksArr = #(0,1,2,3,4,5,6,7,8,9),
		visState = false,
	miauuStr = miauuTestStr()


see my example… put OK before end



oh sure, I forgot about that you explicitly limit the count. Then it is kinda strange that allocation of one extra array takes so much time. Cause I don’t believe that there could be anything else like GC collections happening that would affect the perf


Performance considerations
The Split methods allocate memory for the returned array object and a String object for each array element. If your application requires optimal performance or if managing memory allocation is critical in your application, consider using the IndexOf or IndexOfAny method, and optionally the Compare method, to locate a substring within a string.

If you are splitting a string at a separator character, use the IndexOf or IndexOfAny method to locate a separator character in the string. If you are splitting a string at a separator string, use the IndexOf or IndexOfAny method to locate the first character of the separator string. Then use the Compare method to determine whether the characters after that first character are equal to the remaining characters of the separator string.

In addition, if the same set of characters is used to split strings in multiple Split method calls, consider creating a single array and referencing it in each method call. This significantly reduces the additional overhead of each method call.

String.Split Method


can the VS Code be configured in such a way that in case of an error in maxscript, the system will find a line with an error and highlight it in the VS Code editor?


Few minutes ago I wrote a script which, after I select the line number in Maxscript listener(where the error occur) will go to the line in the currently opened script. :slight_smile:

local g = (dotNetClass "Autodesk.Max.GlobalInterface").Instance
	local mxse = g.TheMxsEditorInterface
	local editorHandle = mxse.EditorGetMainHWND
	local currentlyOpenTab = mxse.EditorGetEditHWND
	local SCI_GOTOLINE = 2024
	local SCI_SETSEL = 2160
	local windowsSendMessage = windows.SendMessage
	if (selText = (getListenerSelText())) != "" do
		_lineIdx = selText as integer
		windowsSendMessage currentlyOpenTab SCI_GOTOLINE _lineIdx 0
		nStart = SendCommandToScintilla SCI_POSITIONFROMLINE (_lineIdx - 1) 0
		nEnd = SendCommandToScintilla SCI_GETLINEENDPOSITION (_lineIdx - 1) 0
		windowsSendMessage currentlyOpenTab SCI_SETSEL nStart nEnd

Still can’t run it while the Maxscript listener is in focus, but clicking in the viewport and executing the script does the job.


I could only make an app that sends scripts from vscode to selected max instance and not in reverse. Maybe there’s a way to redirect listener output to vscode somehow, but I never digged in that direction.
Moreover I found myself switching back to default max editor most of the time, for any small scripts and prototypes.


AD have added some pre/post listener callbacks to the Listener interface.
But I have no idea, how to check if the output had thrown an error. Parsing listener window could easily freeze max for a long time when lots of data being printed…
tested in 2020

global MyClass =
		local source = "
		using Autodesk.Max;
		using System;	
		namespace Jahman
			public class Test
				public static IGlobal ip = GlobalInterface.Instance;
				public void debugPrint_pre( string str )
					IListener l = ip.TheListener;
					l.EditStream.Wputs(str + \" - pre -\\n\");
				public void debugPrint_post( string str )
					IListener l = ip.TheListener;
					l.EditStream.Wputs(str + \" - post -\\n\");
				public GlobalDelegates.Delegate41 get_delegate_pre()
					return new GlobalDelegates.Delegate41(debugPrint_pre);
				public GlobalDelegates.Delegate41 get_delegate_post()
					return new GlobalDelegates.Delegate41(debugPrint_post);

		compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
		compilerParams.ReferencedAssemblies.Add "System.dll"
		compilerParams.ReferencedAssemblies.Add (getdir #maxroot + "autodesk.max.dll")
		compilerParams.GenerateInMemory = on	
		csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
		compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #( source )
		if (compilerResults.Errors.Count > 0 ) then
			local errs = stringstream ""
			for i = 0 to (compilerResults.Errors.Count-1) do
				local err = compilerResults.Errors.Item[i]
				format "Error:% Line:% Column:% %\n" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
			format "%\n" errs
			return undefined
		DataPair Assembly:compilerResults.CompiledAssembly Instance:((dotNetClass "System.Activator").CreateInstance (compilerResults.CompiledAssembly.GetType "Jahman.Test"))

g.thelistener.MsgOutputCbPrecall = undefined
g.thelistener.MsgOutputCbPostcall = undefined
g.thelistener.MsgOutputCbPrecall  = MyClass.instance.get_delegate_pre()
g.thelistener.MsgOutputCbPostcall = MyClass.instance.get_delegate_post()
format "test\n"

"test_mxs_error" + 42


I am doing something wrong, but I can’t figure out what.
Here is part of the code:

string re_with = (\"with\\\\\");
			var spaces = new char[]{' ','	'};
			using (System.IO.StringReader sr = new System.IO.StringReader(doc))
				string line;
				int line_index = 0;
				string word;
				while ((line = sr.ReadLine()) != null)
					word = line.TrimStart(spaces);
					int len  = word.IndexOfAny( spaces );
					word = word.Substring( 0, len < 0 ? word.Length : len ).ToLower();
					if ( re_with == word )

The if statement is always false. When I collect all word there is “with” inside the collection, but all If statements are always false.


Fixed. It has to be string re_with = (\"with\");


… nevermind