Search Unity

Read from TextAsset line by line

Discussion in 'Scripting' started by laxbrookes, May 21, 2015.

  1. laxbrookes

    laxbrookes

    Joined:
    Jan 9, 2015
    Posts:
    235
    I've done a lot of searching for an answer before posting here but I don't seem to be getting anywhere.

    Essentially I have a streamreader that reads a text file line by line to populate a list. The issue is that after it has been compiled, it will no longer read from this list unless I store the text file as a textasset. I have declared the textasset but I'm unsure of how to access it line by line for the streamreader as using .text reads the whole lot in one go.

    Here is what I have so far

    Code (CSharp):
    1.   TextAsset spellData = Resources.Load("spelldata.txt") as TextAsset;
    2.  
    3.         Debug.Log("Compiling spell database...");
    4.  
    5.         string line;
    6.         System.IO.StreamReader file = new System.IO.StreamReader(Application.dataPath + "/Resources/spelldata.txt"); //load text file with data
    7.         while ((line = file.ReadLine()) != null)
    8.         { //while text exists.. repeat
    9.  
    10.             char[] delimiterChar = { '#' };//variable separation
    11.             string[] split = line.Split(delimiterChar, StringSplitOptions.None); //each line splits parts with # character
    12.             Spell spellIn = new Spell(split[0], Convert.ToInt32(split[1]), Convert.ToInt32(split[2]), Convert.ToBoolean(split[3]), split[4], Convert.ToInt32(split[5]), split[6],
    13.                                       split[7], split[8], split[9]); //create new spell and populate
    14.             Debug.Log(spellIn.spell_name + " found...");
    15.             spellDB.Add(spellIn); //add to database
    16.         }
    17.         file.Close();
    Currently it reads from "Application.dataPath + "/Resources/spelldata.txt" but I'd like it from the declared textasset. Thanks in advance!
     
  2. Bradamante

    Bradamante

    Joined:
    Sep 27, 2012
    Posts:
    300
    Well you are declaring spellData at the top, but you are using while on the file variable. Or how do you mean?
     
  3. laxbrookes

    laxbrookes

    Joined:
    Jan 9, 2015
    Posts:
    235
    Instead of reading from the current file path, how would I read from the textAsset instead. I know i'd have to restructure the streamreader but I'm not sure how.
     
    raulius likes this.
  4. Bradamante

    Bradamante

    Joined:
    Sep 27, 2012
    Posts:
    300
    Don't have to use the StreamReader:

    Code (csharp):
    1.  
    2. public static Spell parseTextAsset ( TextAsset ft ) {
    3.  
    4.   string fs = ft.text;
    5.   string[] fLines = Regex.Split ( fs, "\n|\r|\r\n" );
    6.  
    7.   for ( int i=0; i < fLines.Length; i++ ) {
    8.  
    9.   string valueLine = fLines[i];
    10.   string[] values = Regex.Split ( valueLine, ";" ); // your splitter here
    11.  
    12.   Spell newSpell = new Spell ( values[0], ... ) // etc
    13.   return newSpell;
    14.   // spellDB.Add ( newSpell );
    15.   }
    16. }
     
    henryfjones and SynthX like this.
  5. laxbrookes

    laxbrookes

    Joined:
    Jan 9, 2015
    Posts:
    235
    Thank you for you help, but now I'm unsure how to populate my spellDB list with each of these? Apologies if I'm overlooking something simple!
     
  6. Bradamante

    Bradamante

    Joined:
    Sep 27, 2012
    Posts:
    300
    Well, not like this?

    Code (csharp):
    1.  
    2. TextAsset spellData = Resources.Load("spelldata.txt") as TextAsset;
    3. List<Spell> spellDB = new List<Spell>();
    4.  
    5. // put the parseTextAsset function in a static public class
    6. // that doesn't derive from MonoBehavior
    7. Spell newSpell = TextAssetParser.parseTextAsset ( spellData);
    8. spellDB.Add ( newSpell );
     
  7. laxbrookes

    laxbrookes

    Joined:
    Jan 9, 2015
    Posts:
    235
    Thank you again, but there is still one more issue. The function is erroring saying that it doesn't return a value on all paths. What should I be returning for null if I'm understanding this correctly?
     
  8. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Don't use @Bradamante's code directly; I think he was in a hurry, because he's got a "for" loop that exits (returns) on the very first iteration through the loop. I'm pretty sure that's not what you want!

    But take the big idea — just split the TextAsset.text into lines (for example, with Regex.Split as @Bradamante showed), and then do whatever you want with each line.
     
  9. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    make a public TextAsset property and drag the text into it in editor
    the TextAsset has a text property and that text property has a Split method

    Code (CSharp):
    1.         public void Import()
    2.         {
    3.             Clear();
    4.             var splitFile = new string[] { "\r\n", "\r", "\n" };
    5.             var splitLine = new char[] { ',' };
    6.             // name
    7.             var NameLines = NameFile.text.Split(splitFile, StringSplitOptions.None);
    8.             for (uint i = 0; i < NameLines.Length; i++)
    9.             {
    10.                 var line = NameLines[i].Split(splitLine, StringSplitOptions.None);
    11.                 Utility.Assert(line.Length == _nameFieldCount);
    12.                 // male
    13.                 Add(line[0], Male);
    14.                 // female
    15.                 Add(line[1], Female);
    16.                 // both
    17.                 Add(line[2], Male);
    18.                 Add(line[2], Female);
    19.             }
    20.             // syllable
    21.             var SyllableLines = SyllableFile.text.Split(splitFile, StringSplitOptions.None);
    22.             for (uint i = 0; i < SyllableLines.Length; i++)
    23.             {
    24.                 var line = SyllableLines[i].Split(splitLine, StringSplitOptions.None);
    25.                 Utility.Assert(line.Length == _syllableFieldCount);
    26.                 // male
    27.                 Add(line[0], MaleStart);
    28.                 Add(line[1], MaleMiddle);
    29.                 Add(line[2], MaleEnd);
    30.                 // female
    31.                 Add(line[3], FemaleStart);
    32.                 Add(line[4], FemaleMiddle);
    33.                 Add(line[5], FemaleEnd);
    34.                 // both
    35.                 Add(line[6], MaleStart);
    36.                 Add(line[7], MaleMiddle);
    37.                 Add(line[7], MaleEnd);
    38.                 Add(line[6], FemaleStart);
    39.                 Add(line[7], FemaleMiddle);
    40.                 Add(line[8], FemaleEnd);
    41.             }
    42.             // title
    43.             var TitleLines = TitleFile.text.Split(splitFile, StringSplitOptions.None);
    44.             for (uint i = 0; i < TitleLines.Length; i++)
    45.             {
    46.                 var line = TitleLines[i].Split(splitLine, StringSplitOptions.None);
    47.                 Utility.Assert(line.Length == _titleFieldCount);
    48.                 // male
    49.                 Add(line[0], MalePre);
    50.                 Add(line[1], MalePost);
    51.                 // female
    52.                 Add(line[2], FemalePre);
    53.                 Add(line[3], FemalePost);
    54.                 // both
    55.                 Add(line[4], MalePre);
    56.                 Add(line[5], MalePost);
    57.                 Add(line[4], FemalePre);
    58.                 Add(line[5], FemalePost);
    59.             }
    60.             Sort();
    61.         }
    62.  
     
  10. Warped

    Warped

    Joined:
    Mar 19, 2015
    Posts:
    14
    Try this
    Code (CSharp):
    1. string[] linesFromfile = sourceTexts.text.Split("\n"[0]);
     
    Nit_Ram, Leapingleo, hhoffren and 5 others like this.
  11. Xitech_

    Xitech_

    Joined:
    Feb 19, 2013
    Posts:
    140
    Might be an old-ish post but I created a little function that has a TextAsset as input parameter, and returns a list of each line (split by \n(enters)).


    Code (CSharp):
    1.  private List<string> TextAssetToList(TextAsset ta)
    2.     {
    3.         var listToReturn = new List<string>();
    4.         var arrayString = ta.text.Split('\n');
    5.         foreach (var line in arrayString)
    6.         {
    7.             listToReturn.Add(line);
    8.         }
    9.         return listToReturn;
    10.     }
     
    Gentlenyan and protopop like this.
  12. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I think this would be more efficient...

    Code (CSharp):
    1. private List<string> TextAssetToList(TextAsset ta) {
    2.         return new List<string>(ta.text.Split('\n'));
    3. }
     
  13. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
  14. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    How would you use that on a TextAsset, exactly? I didn't think there was (in a compiled game) an actual file to read from in this case.
     
  15. BackgroundMover

    BackgroundMover

    Joined:
    May 9, 2015
    Posts:
    224
    Extension method version of JoeStrouts solution, call with myTextAsset.TextAssetToList()

    Code (CSharp):
    1.  
    2.     public static class TextAssetExtensionMethods {
    3.         public static List<string> TextAssetToList(this TextAsset textAsset) {
    4.             return new List<string>(textAsset.text.Split(System.Environment.NewLine));
    5.         }
    6.     }
    7.  
     
    Last edited: Mar 12, 2022
    Griffo and Kurt-Dekker like this.
  16. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    Should this perhaps use

    Code (csharp):
    1. System.Environment.NewLine
    instead of just \n ?

    I've always used \n but I'm wondering if we can rely on that with all of Unity's target platforms.
     
    BackgroundMover and JoeStrout like this.
  17. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Yeah, I can't say for sure, but in other cross-platform work I've been bitten by making assumptions about line endings. So using System.Environment.NewLine is probably a good idea — it can't hurt, and it just might help.
     
    BackgroundMover and Kurt-Dekker like this.
  18. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    There is also the System.IO.StringReader way... I haven't tried that yet. Might be safe to assume that takes into account System.Environment.NewLine internally.

    Also, don't forget it is an IDisposable, as so many example codelets seem to forget.
     
    JoeStrout likes this.
  19. stef4nio

    stef4nio

    Joined:
    May 31, 2017
    Posts:
    1
    you are the best!!!! thank you wery much
     
  20. unity_oeMZSDS9APQlWQ

    unity_oeMZSDS9APQlWQ

    Joined:
    Dec 17, 2018
    Posts:
    2
    comparision not working always returns false, can you help me out for searching.
    Debug.Log( words.Contains(inputField.text));

    if (words.Contains(inputField.text, StringComparer.CurrentCultureIgnoreCase)) {
    Debug.Log("Contains");
    } else{
    Debug.Log("not");
    }
     
  21. aloysiustayy

    aloysiustayy

    Joined:
    May 13, 2018
    Posts:
    1
    Thank you!!! This works!
     
  22. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    You need to move that asset into StreamingAssets directory and open it from there. After your game is build, files in Assest and Resources folder will not be available as files anymore. This is because assets storage format in build is different for any platform and when built, they are may be not files anymore. StreamingAssets is special folder for distributing arbitrary files with your app.
    Also I sugges you put your File.Open into using() { } clause so it be closed automatically in case of any error.
     
  23. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    This works, but what it does?
    1. read whole file from HDD to RAM
    2. check every character in file if it is line break
    3. in RAM, create copy of all characters inbetween line breaks
    if file is small and you okay with that, then it probably best way to go
    But if you're trying to read file line by line for loading optimization, this is not best optimization you can make because using reader to read directly from file as in your initial code version is 2x more effective in terms of memory consumption and speed - it checks chars as they are read from hdd and creates new string from them without having whole file loaded in memory
     
  24. Erikoinen

    Erikoinen

    Joined:
    Nov 10, 2014
    Posts:
    68
    @Warped your simple solutions was exactly what I happened to be searching for. Thanks. :)