1

I have tried multiple solutions on here but non of them I can get to work. All i want to do is read a text file from my resources folder rather than the actual local folder.

File name: TextFile.txt

Set to embedded resource.

"Local File" Code that works:

string[] spaces = File.ReadAllLines("C:\\Users\\a\\source\\repos\\a\\bin\\Debug\\TextFile.txt");

Current Code:

        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "TextFile.txt";

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        using (StreamReader reader = new StreamReader(stream))
        {
            string result = reader.ReadToEnd(); 
        }

        string[] spaces = File.ReadAllLines(resourceName);

But I am getting the following error:

System.ArgumentNullException: 'Value cannot be null. Parameter name: stream'

On this line:

using (StreamReader reader = new StreamReader(stream))

EDIT1 Tried this as per link (Why does GetManifestResourceStream returns null while the resource name exists when calling GetManifestResourceNames?) and this NULL ERROR:

        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "programname.TextFile.txt";

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        using (StreamReader reader = new StreamReader(stream))
        {
            string result = reader.ReadToEnd(); 
        }

        string[] spaces = File.ReadAllLines(resourceName);

Same error, am I putting the namespace bit in the wrong place?

Edit 2, tried this:

        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "programname.Resources.TextFile.txt";
        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        using (StreamReader reader = new StreamReader(stream))
        {
            string result = reader.ReadToEnd(); 
        }

        string[] spaces = File.ReadAllLines(resourceName);

New error:

System.IO.FileNotFoundException: 'Could not find file 'C:\Users\a\source\repos\a\bin\Debug\programname.Resources.TextFile.txt'.'

Location of TextFile.txt

programname
    Resources
        TextFile.txt 
Matt
  • 14,906
  • 27
  • 99
  • 149
  • What is the root namespace for your assembly? Is it `programname`? Is `TextFile.txt` located directly in this assembly or in subfolder? Please, add this information – Pavel Anikhouski Sep 10 '19 at 08:57
  • `programname` is the namspace & `TextFile.txt` is located it the resources folder... `programname > Resources > TextFile.txt` – Matt Sep 10 '19 at 08:59
  • then try to do the following `var resourceName = "programname.Resources.TextFile.txt";` – Pavel Anikhouski Sep 10 '19 at 09:01
  • Just tried it, different error... `System.IO.FileNotFoundException: 'Could not find file 'C:\Users\a\source\repos\a\bin\Debug\programname.Resources.TextFile.txt'.'` – Matt Sep 10 '19 at 09:03
  • Which line causes an error? Is the `Assembly.GetExecutingAssembly();` equals to `programname`? – Pavel Anikhouski Sep 10 '19 at 09:05
  • The error is on this line `string[] spaces = File.ReadAllLines(resourceName);` – Matt Sep 10 '19 at 09:05
  • You should not [re-post](https://stackoverflow.com/questions/57866945/c-sharp-read-text-file-from-resources-rather-than-locally) your questions! – TaW Sep 10 '19 at 09:10
  • I added more info and improved it – Matt Sep 10 '19 at 09:12
  • This post: _All i want to do is read a text file from my resources folder_ 1st post comment: _Resources don't go into a folder, they go into a resource. If you embed that resource in your assembly, then see the duplicate for information on how to read said resource from said assembly._ Are you sure the textfile is actually in your recources? Case matters. And why not grab its content like so: `var resourceText = Properties.Resources.TextFile;`?? Note that by default __resource names are stripped of the extension and that case matters__!! – TaW Sep 10 '19 at 09:34
  • It is 100% in my resources folder and set to embed – Matt Sep 10 '19 at 09:36
  • @TaW tried `var resourceText = Properties.Resources.TextFile` i get the error: `Resources does not contain a definition for TextFile` – Matt Sep 10 '19 at 09:38
  • So you have not added it to the Resources.resx file. See [here](https://stackoverflow.com/questions/3314140/how-to-read-embedded-resource-text-file) for two methods to add resources. (not the accepted answer but the one from Contango ! Go to project-properties-Resources.resx. There Add new resource-from File. Pick the file and watch a) the name and b) how a Resource forlder is created in the project and the (unstripped) filename shows up. Now the above code will work without any reflection – TaW Sep 10 '19 at 09:41
  • @TaW Ok, deleted from resources folder, added to Resources.resx as per Contango's answer, im using `var resourceName = Properties.Resources.TextFile;` now im back to my original error `System.ArgumentNullException: 'Value cannot be null. Parameter name: stream'` on line `using (StreamReader reader = new StreamReader(stream))` – Matt Sep 10 '19 at 09:45
  • No need for a stream, really. My line of code pulls the whole text from the file! – TaW Sep 10 '19 at 09:46
  • @TaW So delete both of these lines `using (StreamReader reader = new StreamReader(stream))` & `using (Stream stream = assembly.GetManifestResourceStream(resourceName))` ? – Matt Sep 10 '19 at 09:48
  • Yes. resourceText will already contain the content. If you for some reason need to have a stream you could get it from the Text; many posts about that like [here](https://stackoverflow.com/questions/1879395/how-do-i-generate-a-stream-from-a-string) but usually that isn't what one want, right? – TaW Sep 10 '19 at 09:50
  • @TaW OK, deleted both of them lines, now i am getting this error: `System.ArgumentException: 'Illegal characters in path.'` on this line `string[] spaces = File.ReadAllLines(resourceName);` – Matt Sep 10 '19 at 09:51
  • As I wrote: No need for all of that. `resourceText` will already contain the content. To convert to lines you could do : `var lines = resourceRext.Replace("\r", "").Split('\n');` dpending on the line separators in your file.. – TaW Sep 10 '19 at 09:56
  • i only have this code now `var assembly = Assembly.GetExecutingAssembly(); var resourceName = Properties.Resources.TextFile; string[] spaces = File.ReadAllLines(resourceName);` – Matt Sep 10 '19 at 09:59
  • And that is more than you need. No assembly neeeded just `var resourceRext = Properties.Resources.TestFile; var lines = resourceRext.Replace("\r", "").Split('\n'); ` - Do use the debugger to look intot resourceText to see that all the content is there! – TaW Sep 10 '19 at 10:00
  • Yesssss, finally got it, want to put it as an answer? Or do you want me to? – Matt Sep 10 '19 at 10:04
  • Feel free to do so! – TaW Sep 10 '19 at 10:05

2 Answers2

3

Got this to work by opening Resources.resx, Setting Access Modifier to Public Then adding the file in there.

Then replaced:

string[] spaces = File.ReadAllLines("C:\\Users\\a\\source\\repos\\a\\bin\\Debug\\TextFile.txt")

With:

var resourceText = Properties.Resources.TextFile;
var Lines= resourceText.Replace("\r", "").Split('\n');

A little background:

MSDN:

If you add a resource to your project in the normal way, it will automatically provide a typesafe wrapper for that resource.

For example, in the Solution Explorer, expand the "Properties" node and double click the "Resources.resx" entry. Then add an image via the "Add Resource" button. Give that resource a name, say "MyImage".

You will then be able to programmatically access that image via "Properties.Resources.MyImage"

  • This is the simple and recommended way; it will let you edit the name in the resource file and will check while coding that the name is correct.

Note that the names are stripped of the extension and, as other c# names, are case-sensitive!

  • The other way doesn't register the resources with the .resx file but only add them to the resource folder and marks them as embedded.

To use them you can't use a code as simple as the one above. Instead you need to find out the name (Intellisense will not help you there) and them use calls to the Reflection namespace.

Note that here the names are __not_- stripped of the extension but still are case-sensitive!

Example:

If we don't know the exact name we can search in the assembly:

var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var ress = assembly.GetManifestResourceNames()
                   .Where(x => x.Contains(somepattern)).First();

Usually the 1st way is much recommended. Typesafety and Intellisense support alone are well worth mainainting the resx file! Now we can open a stream to read the resource:

List<string> results = new List<string>(); ;
using (Stream stream = assembly.GetManifestResourceStream(ress))
using (StreamReader reader = new StreamReader(stream))
{
    while (reader.Peek()>=0) results.Add(reader.ReadLine());
}
TaW
  • 53,122
  • 8
  • 69
  • 111
Matt
  • 14,906
  • 27
  • 99
  • 149
2

This function will return the correct resource name for your file:

string resourceName = assembly.GetManifestResourceNames()
  .Single(str => str.EndsWith("TextFile.txt"));

2nd issue:

For string[] spaces = File.ReadAllLines(resourceName); You can't use the same resourceName (embedded resource). If you need to read text line by line use for example:

using (StreamReader reader = new StreamReader(stream))
{
   while (reader.Peek() >= 0)
   {
      Console.WriteLine(reader.ReadLine());
   }
}
Thowk
  • 386
  • 4
  • 15
  • I replaced `var resourceName = "programname.Resources.TextFile.txt";` with `string resourceName = assembly.GetManifestResourceNames() .Single(str => str.EndsWith("TextFile.txt"));` Same error – Matt Sep 10 '19 at 09:16
  • @Matt Look at my response edit. If that make sense, and solve your issue? Simply `ReadAllLines` needs physical path, not resource name – Thowk Sep 10 '19 at 09:48