Notifications
Clear all

[Closed] Use pattern with specific string

I’m trying to search through files but I can’t manage to put file extension from pattern. here’s my code:

fn FindAndReplace str fnd rpl = (
   addpos = findString str fnd
   if (addpos != undefined) do (replace str addpos fnd.count rpl)
)
fm = openFile "D:\\path\\00BCB111D67783\\1FA83D69D89B24_materials.txt"  mode:"rt"
line_cnt = 0
while not eof fm do (
   readLine fm
   line_cnt += 1
)
seek fm 0
for i=1 to line_cnt do (
   eachLine = readline fm
   texNameTemp = (FindAndReplace eachLine "TEXT " "")
   if texNameTemp != undefined then (
      tokens = filterString texNameTemp " "
      texName = substituteString tokens[1] "-" ""
      fileEnd = #(".dds", "_dx10.dds", ".tga")
      print texName
      searchSubDirs = (dotnetClass "System.IO.SearchOption")
      sFiles = dotnetClass "System.IO.Directory"
      theFile = (sFiles.GetFiles @"D:\path\_ex\Materials" (texName + fileEnd) searchSubDirs.AllDirectories)[1]
      if theFile == undefined then (print "file not found")
      else print (theFile + " found.")
   )
   else ()
)

so the file might be <texName>.dds, <texName>_dx10.dds or <texName>.tga. and I want to:

  1. search for <texName>.dds first
  2. if first is fail then search <texName>_dx10.dds
  3. if second is fail then search for <texName>.tga

the ‘Materials’ folder is really huge and don’t want to terrorize my hdd by triple search one by one for each extension. so how can I achieve this?

16 Replies

You could instead collect filenames/paths from these directories, sort them and then use bsearch or regex to find/check that some file exists.
The only drawback is that you’ll have to recreate the filenames list each time new files are added or deleted from these directories or use FileSystemWatcher to do it for you.

Try this:

(
	fn FindAndReplace str fnd rpl = 
	(
	   addpos = findString str fnd
	   if (addpos != undefined) do (replace str addpos fnd.count rpl)
	)
	
	fm = openFile "D:\\path\\00BCB111D67783\\1FA83D69D89B24_materials.txt"  mode:"rt"
	line_cnt = 0
	while not eof fm do 
	(
	   readLine fm
	   line_cnt += 1
	)
	seek fm 0
	
	filesArr = #("texName.dds", "texName_dx10.dds", "texName.tga")
	
	for i=1 to line_cnt do 
	(
	   eachLine = readline fm
	   texNameTemp = (FindAndReplace eachLine "TEXT " "")
	   if texNameTemp != undefined then 
		(
			tokens = filterString texNameTemp " "
			texName = substituteString tokens[1] "-" ""
			fileEnd = #(".dds", "_dx10.dds", ".tga")
			print texName
			searchSubDirs = (dotnetClass "System.IO.SearchOption")
			sFiles = dotnetClass "System.IO.Directory"
			stolLoop = false
			for end in fileEnd while stopLoop == false do
			(
				theFile = (sFiles.GetFiles @"D:\path\_ex\Materials" (texName + end) searchSubDirs.AllDirectories)[1]
				if theFile == undefined then
				(
					print "file not found"
				)
				else
				(
					print (theFile + " found.")
					stopLoop = true
				)
			)			
	   )
	   else ()
	)
)

could you post an example material file? (1FA83D69D89B24_materials.txt???)

of course it’s faster to collect all filenames from a directory first and search only files you need by pattern after that…

i would do something like this:

fn readFilenames file =
(
	if (ss = openfilename file) != undefined do
	(
		file_names = #()
		while not eof ss do
		(
			ff = filterstring (readline ss) " "
			if ss[1] == "TEXT" do appendifunique file_names (tolower (substitutestring ff[2] "-" ""))
		)
		close ss
		file_names
	)
)
fn getFilesRecursive root: pattern:"*" type:".*" =
(
	local sub = @"*"
	local files = getfiles (root + pattern + type)
	local dirs = getdirectories (root + sub)
	for d in dirs do join dirs (getdirectories (d + sub))
	for f in dirs do join files (getfiles (f + pattern + type))
	files 
)

file_names = readFilenames <file> -- material file
files = getFilesRecursive() -- add patterns if you need

files = for file in files collect #(getfilenamepath file, filenamefrompath file) -- split filenames once

type_patterns = #(".dds", "_dx10.dds", ".tga")
textures = #()
for name in file_names do
(
	fp = name + "*"
	for f in files where matchpattern f[2] pattern:fp do
	(
		found = off
		for type in type_patterns while not found do
		(
			if found = (stricmp f[2] (name + type) == 0) do append textures (f[1] + f[2]) 
		)
	)
)

sorry, it’s been night here…

the file 1FA83D69D89B24_materials.txt could look like this:

Material <matName>
==========================
TEXT <texName> 1
TEXT <texName> 1
TEXT <texName> 0

Material <matName>
==========================
TEXT <texName> 1
TEXT <texName> 0
TEXT <texName> 1
TEXT <texName> 1

<matName> – it’s a second part of thename of the folder (full is M_<matName>) containing all the listed textures. I’ve tried to search for that folders first and copy them into specific folder but I don’t know how to search for folders. so I came with this bright idea.

also the structure of the tree is supposed to be any. Right now I’m trying to understand smaller problem. Next I might list all the founds into the list view. Also thanks for the response!

edit: btw the amount of files (textures) is really huge. like 35Gb, 37K files and more than 8K folders

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

In this case, it is obvious that you must first collect all the files in the list, and then search the ones you need in this list.

the files might be changed. I don’t wanna re-list this kind of data. in my case it’s counterproductive.
will these two mentioned solutions will do the job without collecting?

btw thank you for the help miauu and denis!

2 Replies
(@denist)
Joined: 11 months ago

Posts: 0

and what alternative? read from disk will always be slower than from the list

(@tosyk)
Joined: 11 months ago

Posts: 0

well the script I posted do the search for about 10sec. it listed the files with “.dds” on their end only. script can’t find other files though. so 10sec it’s okay till It turned I need to ran the script for 3 times just for a single material file — for each file end.

btw miauu’s script doesn’t work — it prints names from material file but not the files themselves
btw2: I don’t really good in maxscript so I can’t really understand your script, denis, which I want

try this code:

fn CreateFileOpsAssembly =
(
	source  = ""
	source += "using System;\n"
	source += "using System.Collections.Generic;\n"
	source += "using System.Text;\n"
	source += "using System.IO;\n"
	source += "namespace FileOps\n"
	source += "{\n"
	source += "    public class Access\n"
	source += "    {\n"
	source += "        private static void getFilesRecursive(string path, string searchPattern, ref List<string> files)\n"
	source += "        {\n"
	source += "            try\n"
	source += "            {\n"
	source += "                foreach (string p in Directory.GetDirectories(path)) getFilesRecursive(p, searchPattern, ref files);\n"
	source += "                foreach (var file in Directory.GetFiles(path, searchPattern)) files.Add(file);\n"
	source += "            }\n"
	source += "            catch { }\n"
	source += "        }\n"
	source += "        public static List<string> GetFilesRecursive(string path, string searchPattern)\n"
	source += "        {\n"
	source += "            List<string> files = new List<string>();\n"
	source += "            getFilesRecursive(path, searchPattern, ref files);\n"
	source += "            return files;\n"
	source += "        }\n"
	source += "    }\n"
	source += "}\n"

	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"

	compilerParams.ReferencedAssemblies.AddRange #("System.dll")

	compilerParams.GenerateInMemory = on
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
	
	assembly = compilerResults.CompiledAssembly
	assembly.CreateInstance "FileOps.Access"
)
global FileOps = CreateFileOpsAssembly()

(
	gc()
	t0 = timestamp()
	h0 = heapfree

--bins = FileOps.GetFilesRecursive @"c:\windows" "*.bin"
--dlls = FileOps.GetFilesRecursive @"c:\windows" "*.dll"
--pngs = FileOps.GetFilesRecursive @"c:\windows" "*.png"
alls = FileOps.GetFilesRecursive @"c:\windows" "*"

	format "time:% heap:%\n" (timestamp() - t0) (h0 - heapfree)
)

you can see that searching ALL files is longer than by a pattern… but it’s not dramatically longer.
i want to say that faster to collect ALL first and search in the all list after that

it’s almost no difference collect all or by pattern. do you want to collect for every texture name?

generally I have a folder with FBX models in it. next to each FBX there’s a mentioned *_materials.TXT where * – is a name of FBX.

my big goal is to collect all textures from all corresponded TXT from given folder.

say I have 5 FBX in a folder and 5 material files. so I load these FBX into max and copy/paste all found textures into ‘textures’ foder next to FBX files.

I did all of these steps except this thing with searching for textures. this dot.net frustrates me

I’ll try your solution, thank you!

Page 1 / 2