Matt
Matt

Reputation: 15061

C# Read text file from resources rather than locally error, adding namespace didnt work

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 

Upvotes: 1

Views: 3412

Answers (2)

Matt
Matt

Reputation: 15061

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());
}

Upvotes: 3

Thowk
Thowk

Reputation: 407

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());
   }
}

Upvotes: 2

Related Questions