Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more..
    Dismiss Notice
  3. Dismiss Notice

String.Insert causes editor to crash

Discussion in 'Scripting' started by SimonePellegatta, Oct 23, 2020.

  1. SimonePellegatta

    SimonePellegatta

    Joined:
    Oct 17, 2020
    Posts:
    2
    I have a script which contains this foreach loop:

    Code (CSharp):
    1. foreach (KeyCode code in System.Enum.GetValues(typeof(KeyCode)))
    2.  if (Input.GetKeyDown(code))
    3. {
    4.     string key = code.ToString();
    5.  
    6.     switch (key)
    7.     {
    8.        case "Escape":
    9.         key = placeholder.text;
    10.             break;
    11.         case "Mouse0":
    12.             key = "Mouse Left";
    13.             break;
    14.         case "Mouse1":
    15.             key = "Mouse Right";
    16.             break;
    17.         case "Mouse2":
    18.             key = "Mouse Middle";
    19.             break;
    20.         default:
    21.             if (key.Length == 1)
    22.                 key = key + " key"; //Single letter keys (ex. G => G key)
    23.             else
    24.                 for (ushort n = 1; n < key.Length; n++)
    25.                     if (Char.IsUpper(key[n]))
    26.                         key = key.Insert(n - 1, " "); //insert space (ex. LeftShift => Left Shift) --- THE ERROR IS HERE
    27.         break;
    28.     }
    29.  
    30.     last = code;
    31.     placeholder.text = key;
    32.  
    33.     change = false;
    34.     background.color = Color.clear;
    35. }
    When the line where I've written "THE ERROR IS HERE" gets executed, the Unity Editor suddenly freezes, and I have to kill it from Windows Task Manager.

    I've tried several things, and it turns out that the problem is not the insert function, but the assignement:
    • If I execute key.Insert(n - 1, " "); and I do not assign it, the Editor does not crash
    • If I assign key.Insert(n - 1, " "); to a new string, the Editor does not crash
    • If I assign key.Insert(n - 1, " "); to a new string and then I assign that string to key, the Editor does not crash
    So , what could the error be? I'm new to Unity and 'm getting crazy...
     
    Last edited: Oct 24, 2020
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
    My guess is because you are doing key.Length in your for statement and then modifying the length of "key", you have essentially created an infinite loop as you keep increasing the length of key and you can't ever exit the loop. Thus the freeze and crash. You might just want to grab the length of key up front and then use that value in your for loop.
     
  3. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    Your loop runs until
    n < key.length
    is false. By inserting into key, you are making it longer. It's possible that
    n  < key.length
    is always true, because key.length is getting bigger as the loop runs.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,769
    Your
    break;
    statement APPEARS like it should be exiting your inner for() loop without issue.

    But the indentation on that inner
    break;
    is also suspicious, almost as though the code isn't compiling for some other reason, or failing to format. Formatting doesn't matter but it can indicate a syntax issue.

    It's possible you want to completely exit all loops instead of just the one you're in. Apart from the loop on the first line are there any more enclosing loops?

    Easiest is just to attach the debugger and single-step the code and prove to yourself that the code flow is exiting your loops. I predict it is not but I cannot see at a glance why.
     
    Joe-Censored likes this.
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    My typical strategy when iterating over something I am modifying is to iterate over a copy instead while modifying the original. Usually that is for arrays/lists, but I think it would make sense here as well. It prevents any craziness to your iteration from the adding or removing.
     
    SimonePellegatta likes this.
  6. SimonePellegatta

    SimonePellegatta

    Joined:
    Oct 17, 2020
    Posts:
    2
    Thanks to everyone!
    The issue was actually the fact that increasing the length of the string using Insert will cause an infinite loop...

    Problem solved.
     
    Kurt-Dekker likes this.
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
  8. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,533
    Yes, just go through your loop logically. When character at index n is an uppercase letter you will insert a space before that letter which will essentially place a space at the index n and it will move the upper case letter to n+1. That means the next iteration you will hit the same uppercase letter again and insert another space.

    There are two solutions to this issue. The simplest one is just increasing n when you insert a space in order to skip the character you just processed:

    Code (CSharp):
    1.  
    2. for (ushort n = 1; n < key.Length; n++)
    3.     if (char.IsUpper(key[n]))
    4.         key = key.Insert(n++ - 1, " ");
    5.  
    Though a better solution is probably to check for a space before the uppercase letter:

    Code (CSharp):
    1.  
    2. for (ushort n = 1; n < key.Length; n++)
    3.     if (key[n-1] != ' ' && char.IsUpper(key[n]))
    4.         key = key.Insert(n - 1, " ");
    5.  
    This will only insert a space when there is no space before the upper case letter. So if there is already a space the uppercase letter is just ignored. Of course for this usecase it wouldn't matter as KeyCode identifiers can't contain spaces. Though as a general approach this is more robust.
     
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    the best way to debug these kinds of things, in general, and in my opinion, is to "unroll".
    track the individual variable states on a piece of paper, or in a text file, whatever suits you better.
    do each iteration manually, get a feel for what's going on, until you adopt heuristics and your mind is able to discern the nuances and patterns in your future algorithms of this kind. (i.e. don't do donkey-carrot lol)

    this is why I posted that picture above, this is what I see almost immediately (and why all of us liked that post). it happens. I freeze Unity on a weekly basis.
     
    SimonePellegatta likes this.
  10. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    @Bunny83 I like that second approach, that's a true pattern recognition, almost a mini regex, and doesn't require a dangling copy of anything.

    I'd do it more verbosely though (insert not good, a new string instance for every change)
    Code (csharp):
    1. var sb = new StringBuilder();
    2.  
    3. // ...
    4. else {
    5.   sb.Clear();
    6.   for(ushort n = 1; n < key.Length; n++) {
    7.     if(char.IsLower(key[n-1]) && char.IsUpper(key[n])) sb.Append(" ");
    8.     sb.Append(key[n]);
    9.   }
    10.   key = sb.ToString();
    11. }