none
c# problem with string.contains() RRS feed

  • Question

  • hello all, 

    i have wrote some code and trying to sort file names depending on names and saving them to a ini depending on what names are containing a certain string but i come across a problem i cant seem to fix so in short this is the code 

            public void getmodelnames()
            {
                var dirs = elfenliedtopfan5progsettings.Read("ModelPath", "ModelPaths");
                string[] files = Directory.GetFiles(dirs,"*.xmodel_bin*", SearchOption.AllDirectories);
    
    
    
    
                // Display all the files.
                _MessageResult.Instance.Append("XMODEL BIN FILES ENTRIES" + Environment.NewLine);
    
                    
    
    
    
                    
                    //string[] allFiles = System.IO.Directory.GetFiles(elfenliedtopfan5progsettings.Read("ModelPath", "ModelPaths"));//Change path to yours
                    foreach (string found in files)
                    {
                        string elfenpath = Path.GetFileNameWithoutExtension(found);
                        setmodel(found, elfenpath);
                    }
    
    
                    
    
                    
    
                
                _MessageResult.Instance.Append("XMODEL BIN FILES ENTRIES END" + Environment.NewLine);
    
    
                
            }

    public void setmodel(string set,string name)
            {
                    string elf = Path.GetFileNameWithoutExtension(set);
                    
                    XtraMessageBox.Show("Match Found : " + elf);
                    
                    
                        if(elf.Contains("wm"))
                        {
                                // WM STUFF
                                if (elf.Contains("wm"))
                                {
                                    if (elf.Contains("wm_mag"))
                                    {
                                        elfenliedtopfan5progsettings.Write("WorldModelMag", elf, "WM_MAG");
                                    }
                                    else
                                    {
                                        if (elf.Contains("upg"))
                                        {
                                            elfenliedtopfan5progsettings.Write("WorldModelUpgraded", elf, "WM_UG");
                                        }
                                        else
                                        {
                                            if (elf.Contains("ug"))
                                            {
                                                elfenliedtopfan5progsettings.Write("WorldModelUpgraded", elf, "WM_UG");
                                            }
                                            else
                                            {
                                                elfenliedtopfan5progsettings.Write("WorldModel", elf, "WM");
                                            }
                                        }
                                    }
                                    
                                }
    
    
                        }
                        else
                    {
                         // VM STUFF
                         if(elf.Contains("vm"))
                         {
    
                               
                                    if (elf.Contains("vm"))
                                    {
                                        if (elf.Contains("vm_ug"))
                                        {
                                            elfenliedtopfan5progsettings.Write("ViewModelUpgraded", elf, "VM_UG");
                                        }
                                        else
                                        {
                                            if (elf.Contains("upg"))
                                            {
                                                elfenliedtopfan5progsettings.Write("ViewModelUpgraded", elf, "VM_UG");
                                            }
                                            else
                                            {
                                               elfenliedtopfan5progsettings.Write("ViewModel", elf, "VM");
                                            }
                                        }
    
                                        
    
                                    }
                         }
                         else
                         {
                           if(elf.Contains("animated"))
                           {
    
    
                            if(elf.Contains("ug"))
                            {
                                elfenliedtopfan5progsettings.Write("AnimatedUPG", elf, "AM");
                            }
                            else
                            {
                                elfenliedtopfan5progsettings.Write("Animated", elf, "AM");
                            }
                            
                        }
                         }
    
                       
                        }
            }

    and this will look in the model folder where you select and you should find the following kind of files.

    and all it will get is the .xmodel bin files and then get the file names the issue im having is 

    some of the names are different like instead of vm its view model

    or not ug its upgraded or upg 

    is there a better way of getting strings and divining wm vm upgaded_vm ect 

    example what people could name them 

    weaponname_vm

    weaponname_wm

    weaponname_upgraded

    weaponname_up

    weaponname_upg

    weaponname_animated

    weaponname_animated_ug

    what i need to do is find a better way than doing what im doing now witch is name.contains as it not always working and was wondering if there was a better and more solid way of doing this 

    thank you in advance Mion Shion.

    Wednesday, June 3, 2020 8:01 AM

Answers

  • The first code block with FindFiles is approach #1 that I talked about. The second code block with ProcessFiles is approach #2. You wouldn't use both. 

    Approach #1 was demonstrating the core behavior if you just need to match files in a particular directory but you don't really need to do anything special for each different file "type". In this case you'd call FindFiles and pass it a directory name. It would return all the files that matched the criteria you were looking for.

    var matchingFiles = FindFiles("somepath");

    Approach #1 answered the question you had posted about finding files that matched some criteria. However my comment about approach #2 was that if you wanted to treat these different "types" of files differently then rather than just finding matching files you'd likely want to combine it with the actual processing as well. That is what approach #2 demos and is probably the code you'd want to use given your example code.

    If you went with approach #1 then you'd have a list of files but you'd then need to enumerate the filenames again and parse the filenames (again) to determine what "type" of file it was so you could process it. This would be redundant in your case, hence the approach #2 recommendation. If you really wanted to separate enumeration of the files from processing then you'd need to introduce an intermediate type and approach #1 might start making more sense.

    So, you shouldn't need the code in the first block if you're using the code in the second block. 


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Mion Shion Sunday, June 7, 2020 7:51 PM
    Saturday, June 6, 2020 8:59 PM
    Moderator

All replies

  • I'm a little confused as to your requirements because your code seems to distinguish between these various file "types". So if you need to treat an "_upgraded" file differently than "_upg" then there is no way around hard coding the types you care about. For example if you have a "weaponname_blah_blah" you would ignore it. That doesn't mean you need nested if statements though as you can use a simple list instead. For example here is (untested) code that finds all files in the given directory that match the file name masks you had asked about.

    public IEnumerable<string> FindFiles ( string path )
    {
        //Given the # of masks it is easier to fetch all filenames and go from there
        var files = Directory.GetFiles(path);
        foreach (var file in files)
            if (IsMatchingFile(file))
                yield return file;
    }
    
    //List of substrings we care about - note we collapse some because the masks will already get them
    private readonly static string[] s_fileMatches = new string[] { "_vm", "_wm", "_up", "_animated" };
    
    private bool IsMatchingFile ( string filename )
    {
        filename = Path.GetFileNameWithoutExtension(filename);
    
        //Ignore case
        return s_fileMatches.Any(m => filename.IndexOf(m, StringComparison.OrdinalIgnoreCase) >= 0);
    }

    The challenge is mapping the text you're looking for to the "functionality" you want to run. In your case you're treating each of these different. If that is the functionality you need then looking for the files first and then mapping them to functions is duplicating work so you should combine the behavior.

    //Pass the list of files to process here - could group by higher level name (e.g. "weaponname_")
    void ProcessFiles ( IEnumerable<string> files )
    {
        //For each file find a "handler" to deal with it, if any
        foreach (var file in files)
        {
            var handler = GetHandler(file);
            if (handler != null)
                handler(file);
        };
    }
    
    //Find the "handler" to use - this is based solely on filename but could be expanded...
    Action<string> GetHandler ( string filename )
    {
        //Because you have substrings that use different handlers we must enumerate the handlers in the order
        //you want them
        foreach (var handler in _handlers)
        {
            if (filename.IndexOf(handler.Match, StringComparison.OrdinalIgnoreCase) >= 0)
                return handler.Handler;
        };
    
        return null;
    }
    
    //Simple structure to store file matches to their handlers - if you need something more complex then add an interface and
    //implement handlers as custom types, then move the "matching" logic into the interface for more flexibility
    private struct HandlerInfo
    {
        public HandlerInfo ( string match, Action<string> handler ) : this()
        {
            Match = match;
            Handler = handler;
        }
    
        public string Match { get; }   
        public Action<string> Handler { get; }
    }
    
    //NOTE: We aren't using a dictionary here because order matters - put more specific matches first
    private readonly List<HandlerInfo> _handlers = new List<HandlerInfo>()
    {
        new HandlerInfo("wm_mag", OnWmMag),
        new HandlerInfo("wm_upg", OnWmUpg),
        new HandlerInfo("wm_ug", OnWmUpg),
    
        //Generic ones
        new HandlerInfo("wm", OnWm),
    };
    
    //Handler functions - note they are static just to work around the sample init I'm doing above
    static void OnWmMag ( string file ) { }
    static void OnWmUpg ( string file ) { }
    static void OnWm ( string file ) { }
    It's more code but it allows you to separate out how you handle each match type. It also makes it easy to add a new one because you add a function, add to the handler list and you're done. But as mentioned in the comments if the processing is dramatically different or you need more complex matching logic then add an interface instead. Then create separate implementations for each unique file logic you need.


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, June 3, 2020 2:09 PM
    Moderator
  • I'm a little confused as to your requirements because your code seems to distinguish between these various file "types". So if you need to treat an "_upgraded" file differently than "_upg" then there is no way around hard coding the types you care about. For example if you have a "weaponname_blah_blah" you would ignore it. That doesn't mean you need nested if statements though as you can use a simple list instead. For example here is (untested) code that finds all files in the given directory that match the file name masks you had asked about.

    public IEnumerable<string> FindFiles ( string path )
    {
        //Given the # of masks it is easier to fetch all filenames and go from there
        var files = Directory.GetFiles(path);
        foreach (var file in files)
            if (IsMatchingFile(file))
                yield return file;
    }
    
    //List of substrings we care about - note we collapse some because the masks will already get them
    private readonly static string[] s_fileMatches = new string[] { "_vm", "_wm", "_up", "_animated" };
    
    private bool IsMatchingFile ( string filename )
    {
        filename = Path.GetFileNameWithoutExtension(filename);
    
        //Ignore case
        return s_fileMatches.Any(m => filename.IndexOf(m, StringComparison.OrdinalIgnoreCase) >= 0);
    }

    The challenge is mapping the text you're looking for to the "functionality" you want to run. In your case you're treating each of these different. If that is the functionality you need then looking for the files first and then mapping them to functions is duplicating work so you should combine the behavior.

    //Pass the list of files to process here - could group by higher level name (e.g. "weaponname_")
    void ProcessFiles ( IEnumerable<string> files )
    {
        //For each file find a "handler" to deal with it, if any
        foreach (var file in files)
        {
            var handler = GetHandler(file);
            if (handler != null)
                handler(file);
        };
    }
    
    //Find the "handler" to use - this is based solely on filename but could be expanded...
    Action<string> GetHandler ( string filename )
    {
        //Because you have substrings that use different handlers we must enumerate the handlers in the order
        //you want them
        foreach (var handler in _handlers)
        {
            if (filename.IndexOf(handler.Match, StringComparison.OrdinalIgnoreCase) >= 0)
                return handler.Handler;
        };
    
        return null;
    }
    
    //Simple structure to store file matches to their handlers - if you need something more complex then add an interface and
    //implement handlers as custom types, then move the "matching" logic into the interface for more flexibility
    private struct HandlerInfo
    {
        public HandlerInfo ( string match, Action<string> handler ) : this()
        {
            Match = match;
            Handler = handler;
        }
    
        public string Match { get; }   
        public Action<string> Handler { get; }
    }
    
    //NOTE: We aren't using a dictionary here because order matters - put more specific matches first
    private readonly List<HandlerInfo> _handlers = new List<HandlerInfo>()
    {
        new HandlerInfo("wm_mag", OnWmMag),
        new HandlerInfo("wm_upg", OnWmUpg),
        new HandlerInfo("wm_ug", OnWmUpg),
    
        //Generic ones
        new HandlerInfo("wm", OnWm),
    };
    
    //Handler functions - note they are static just to work around the sample init I'm doing above
    static void OnWmMag ( string file ) { }
    static void OnWmUpg ( string file ) { }
    static void OnWm ( string file ) { }
    It's more code but it allows you to separate out how you handle each match type. It also makes it easy to add a new one because you add a function, add to the handler list and you're done. But as mentioned in the comments if the processing is dramatically different or you need more complex matching logic then add an interface instead. Then create separate implementations for each unique file logic you need.


    Michael Taylor http://www.michaeltaylorp3.net

    thank you man this has worked perfectly thank you for your reply :) marked as answer sorry for late reply 

     
    Friday, June 5, 2020 11:37 PM
  • I'm a little confused as to your requirements because your code seems to distinguish between these various file "types". So if you need to treat an "_upgraded" file differently than "_upg" then there is no way around hard coding the types you care about. For example if you have a "weaponname_blah_blah" you would ignore it. That doesn't mean you need nested if statements though as you can use a simple list instead. For example here is (untested) code that finds all files in the given directory that match the file name masks you had asked about.

    public IEnumerable<string> FindFiles ( string path )
    {
        //Given the # of masks it is easier to fetch all filenames and go from there
        var files = Directory.GetFiles(path);
        foreach (var file in files)
            if (IsMatchingFile(file))
                yield return file;
    }
    
    //List of substrings we care about - note we collapse some because the masks will already get them
    private readonly static string[] s_fileMatches = new string[] { "_vm", "_wm", "_up", "_animated" };
    
    private bool IsMatchingFile ( string filename )
    {
        filename = Path.GetFileNameWithoutExtension(filename);
    
        //Ignore case
        return s_fileMatches.Any(m => filename.IndexOf(m, StringComparison.OrdinalIgnoreCase) >= 0);
    }

    The challenge is mapping the text you're looking for to the "functionality" you want to run. In your case you're treating each of these different. If that is the functionality you need then looking for the files first and then mapping them to functions is duplicating work so you should combine the behavior.

    //Pass the list of files to process here - could group by higher level name (e.g. "weaponname_")
    void ProcessFiles ( IEnumerable<string> files )
    {
        //For each file find a "handler" to deal with it, if any
        foreach (var file in files)
        {
            var handler = GetHandler(file);
            if (handler != null)
                handler(file);
        };
    }
    
    //Find the "handler" to use - this is based solely on filename but could be expanded...
    Action<string> GetHandler ( string filename )
    {
        //Because you have substrings that use different handlers we must enumerate the handlers in the order
        //you want them
        foreach (var handler in _handlers)
        {
            if (filename.IndexOf(handler.Match, StringComparison.OrdinalIgnoreCase) >= 0)
                return handler.Handler;
        };
    
        return null;
    }
    
    //Simple structure to store file matches to their handlers - if you need something more complex then add an interface and
    //implement handlers as custom types, then move the "matching" logic into the interface for more flexibility
    private struct HandlerInfo
    {
        public HandlerInfo ( string match, Action<string> handler ) : this()
        {
            Match = match;
            Handler = handler;
        }
    
        public string Match { get; }   
        public Action<string> Handler { get; }
    }
    
    //NOTE: We aren't using a dictionary here because order matters - put more specific matches first
    private readonly List<HandlerInfo> _handlers = new List<HandlerInfo>()
    {
        new HandlerInfo("wm_mag", OnWmMag),
        new HandlerInfo("wm_upg", OnWmUpg),
        new HandlerInfo("wm_ug", OnWmUpg),
    
        //Generic ones
        new HandlerInfo("wm", OnWm),
    };
    
    //Handler functions - note they are static just to work around the sample init I'm doing above
    static void OnWmMag ( string file ) { }
    static void OnWmUpg ( string file ) { }
    static void OnWm ( string file ) { }
    It's more code but it allows you to separate out how you handle each match type. It also makes it easy to add a new one because you add a function, add to the handler list and you're done. But as mentioned in the comments if the processing is dramatically different or you need more complex matching logic then add an interface instead. Then create separate implementations for each unique file logic you need.


    Michael Taylor http://www.michaeltaylorp3.net

    thank you man this has worked perfectly thank you for your reply :) marked as answer sorry for late reply 

     

    ok a side note when i fully test it hits the FindFiles(string path)
    but dont get a list of files form this tryied opening debug to see if it goes into the find files function and it seems to just skip over it what is the best way to use this as in how do i get it to populate right is there a certain way it should be used. 

    thanks sorry for posting again.

     
    Saturday, June 6, 2020 8:23 PM
  • The first code block with FindFiles is approach #1 that I talked about. The second code block with ProcessFiles is approach #2. You wouldn't use both. 

    Approach #1 was demonstrating the core behavior if you just need to match files in a particular directory but you don't really need to do anything special for each different file "type". In this case you'd call FindFiles and pass it a directory name. It would return all the files that matched the criteria you were looking for.

    var matchingFiles = FindFiles("somepath");

    Approach #1 answered the question you had posted about finding files that matched some criteria. However my comment about approach #2 was that if you wanted to treat these different "types" of files differently then rather than just finding matching files you'd likely want to combine it with the actual processing as well. That is what approach #2 demos and is probably the code you'd want to use given your example code.

    If you went with approach #1 then you'd have a list of files but you'd then need to enumerate the filenames again and parse the filenames (again) to determine what "type" of file it was so you could process it. This would be redundant in your case, hence the approach #2 recommendation. If you really wanted to separate enumeration of the files from processing then you'd need to introduce an intermediate type and approach #1 might start making more sense.

    So, you shouldn't need the code in the first block if you're using the code in the second block. 


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Mion Shion Sunday, June 7, 2020 7:51 PM
    Saturday, June 6, 2020 8:59 PM
    Moderator
  • thanks that works perfectly fine now tested :) thank you for clearing that up.
    Sunday, June 7, 2020 7:51 PM