ChatGPT解决这个技术问题 Extra ChatGPT

How to read embedded resource text file

How do I read an embedded resource (text file) using StreamReader and return it as a string? My current script uses a Windows form and textbox that allows the user to find and replace text in a text file that is not embedded.

private void button1_Click(object sender, EventArgs e)
{
    StringCollection strValuesToSearch = new StringCollection();
    strValuesToSearch.Add("Apple");
    string stringToReplace;
    stringToReplace = textBox1.Text;

    StreamReader FileReader = new StreamReader(@"C:\MyFile.txt");
    string FileContents;
    FileContents = FileReader.ReadToEnd();
    FileReader.Close();
    foreach (string s in strValuesToSearch)
    {
        if (FileContents.Contains(s))
            FileContents = FileContents.Replace(s, stringToReplace);
    }
    StreamWriter FileWriter = new StreamWriter(@"MyFile.txt");
    FileWriter.Write(FileContents);
    FileWriter.Close();
}

W
Wai Ha Lee

You can use the Assembly.GetManifestResourceStream Method:

Add the following usings using System.IO; using System.Reflection; Set property of relevant file: Parameter Build Action with value Embedded Resource Use the following code var assembly = Assembly.GetExecutingAssembly(); var resourceName = "MyCompany.MyProduct.MyFile.txt"; using (Stream stream = assembly.GetManifestResourceStream(resourceName)) using (StreamReader reader = new StreamReader(stream)) { string result = reader.ReadToEnd(); } resourceName is the name of one of the resources embedded in assembly. For example, if you embed a text file named "MyFile.txt" that is placed in the root of a project with default namespace "MyCompany.MyProduct", then resourceName is "MyCompany.MyProduct.MyFile.txt". You can get a list of all resources in an assembly using the Assembly.GetManifestResourceNames Method.

A no brainer astute to get the resourceName from the file name only (by pass the namespace stuff):

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

A complete example:

public string ReadResource(string name)
{
    // Determine path
    var assembly = Assembly.GetExecutingAssembly();
    string resourcePath = name;
    // Format: "{Namespace}.{Folder}.{filename}.{Extension}"
    if (!name.StartsWith(nameof(SignificantDrawerCompiler)))
    {
        resourcePath = assembly.GetManifestResourceNames()
            .Single(str => str.EndsWith(name));
    }

    using (Stream stream = assembly.GetManifestResourceStream(resourcePath))
    using (StreamReader reader = new StreamReader(stream))
    {
        return reader.ReadToEnd();
    }
}

The argument for GetManifestResourceStream needs the path as @adrian indicates. If it helps anyone, that path is like what @SimpleCoder shows in the example: MyNamespace.Filename.Ext. I had previously tried MyNamespace.Resources.Filename.Ext but that results in a null.
If you have your resource not directly in the project root, but in some subfolder, don't forget to put this folder name in resourceName as well (e.g. "MyProjectNameSpace.MyProjectSubFolder.FileName.FileExtention")
Worth to say that resource "Build Action" has to be set as "Embedded Resource"
Er ... SignificantDrawerCompiler ?
Also confused as to what SignificantDrawerCompiler is?
S
StayOnTarget

You can add a file as a resource using two separate methods.

The C# code required to access the file is different, depending on the method used to add the file in the first place.

Method 1: Add existing file, set property to Embedded Resource

Add the file to your project, then set the type to Embedded Resource.

NOTE: If you add the file using this method, you can use GetManifestResourceStream to access it (see answer from @dtb).

https://i.stack.imgur.com/qimwi.png

Method 2: Add file to Resources.resx

Open up the Resources.resx file, use the dropdown box to add the file, set Access Modifier to public.

NOTE: If you add the file using this method, you can use Properties.Resources to access it (see answer from @Night Walker).

https://i.stack.imgur.com/m56aJ.png


A third method is to add the file to the project, then set "Copy to Output Directory" to "True". On compile, the file is copied into the output dir, and you can read the file using normal means. Example: in a WPF app when you want to display an image.
so setting the build action to Resource does nothing that allows you to read out the item as a resource? you have to use EmbeddedResource or add to a Resources.resx file?
@Maslow Setting the build action to 'Resource' creates a linked resource, whereas setting the build action to 'Embedded Resource' compiles the resource into the output assembly. The term 'linked resource' is a fancy term for 'copy the file into the output directory on compile' (you can then read the file at runtime using any normal method). For more on the difference between these two types, see Adding and Editing Resources (Visual C#) at msdn.microsoft.com/en-us/library/7k989cfy(v=vs.90).aspx.
If you would like to use resources this way, but dynamically, just instead of this: Properties.Resources.Your_resource_name write this: Properties.Resources.ResourceManager.GetObject("Your_resource_name").
This answer actually helped me figure out what I was still missing: setting "Build Action: Embedded Resource" in the files. I was wondering why they didn't show up. Now I'm wondering why this isn't the default setting. Is there any reason why you would add a file as a resource and not have it embedded?
S
Steve

Basically, you use System.Reflection to get a reference to the current Assembly. Then, you use GetManifestResourceStream().

Example, from the page I posted:

Note: need to add using System.Reflection; for this to work

   Assembly _assembly;
   StreamReader _textStreamReader;

   try
   {
      _assembly = Assembly.GetExecutingAssembly();
      _textStreamReader = new StreamReader(_assembly.GetManifestResourceStream("MyNamespace.MyTextFile.txt"));
   }
   catch
   {
      MessageBox.Show("Error accessing resources!");
   }

+1 For including the namespace as part of the resource name
var auxList= System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames(); This method could be very useful when you want to learn the exact resource name. (Taken from question stackoverflow.com/questions/27757/…)
C
Community

https://i.stack.imgur.com/Q5rXc.png

The resulting file can then be accessed as a byte array by

byte[] jsonSecrets = GoogleAnalyticsExtractor.Properties.Resources.client_secrets_reporter;

Should you need it as a stream, then ( from https://stackoverflow.com/a/4736185/432976 )

Stream stream = new MemoryStream(jsonSecrets)

You can also use this with a text file, in which case you would have: string jsonSecrets = YourNameSpace.Properties.Resources.YourFileName;
A
Ami Tavory

When you added the file to the resources, you should select its Access Modifiers as public than you can make something like following.

byte[] clistAsByteArray = Properties.Resources.CLIST01;

CLIST01 is the name of the embedded file.

Actually you can go to the resources.Designer.cs and see what is the name of the getter.


Could you please explain this more? When i right-click->properties on a file in the solution explorer, and then set Action to Incorporated ressource, I don't have any Access Modifiers field in the properties panel. Also, I don't have a Propersites.Resources class, I get a The name 'Properties' does not exist in the current context error when compiling your code.
This will only work if you embed the file into Resources.resx, see my answer on the different methods to embed files into a project.
where is Properties defined?
m
miciry89

adding e.g. Testfile.sql Project Menu -> Properties -> Resources -> Add Existing file

    string queryFromResourceFile = Properties.Resources.Testfile.ToString();

https://i.stack.imgur.com/FFklR.png


It will return byte[] , for text files use ` Encoding.UTF8.GetString(Properties.Resources.Testfile)`
A
Amber

I know it is an old thread, but this is what worked for me :

add the text file to the project resources set the access modifier to public, as showed above by Andrew Hill read the text like this : textBox1 = new TextBox(); textBox1.Text = Properties.Resources.SomeText;

The text that I added to the resources: 'SomeText.txt'


P
Peter Gfader

Something I learned just now is that your file is not allowed to have a "." (dot) in the filename.

https://i.stack.imgur.com/NuoOi.jpg

Templates.plainEmailBodyTemplate-en.txt --> Works!!! Templates.plainEmailBodyTemplate.en.txt --> doesn't work via GetManifestResourceStream()

Probably because the framework gets confused over namespaces vs filename...


Sorry. This is wrong. Dots work. (at least it worked for me, NET4.5) I don't know why you had this bug.
Yes they work but they act as Directory separator. Templates.plainEmailBodyTemplate.en.txt will look for "\Templates\plainEmailBodyTemplate\en.txt" resource
No. I tried it. GetManifestResourceStream can access embedded resources with more than one dot in the filename. (NET4.5)
I had a same problem in .NET 4.5. Files with dots in name were not even added to resource collection. Method assembly.GetManifestResourceNames() returns empty list to me.Later I've found out that problem was only with language code. ca.abcd.sk.crt was not added to resources while ca.abcd.crt was added without problem.
Still correct in 2020. Thanks a lot for this, I was getting crazy with this.
T
Timmerz

You can also use this simplified version of @dtb's answer:

public string GetEmbeddedResource(string ns, string res)
{
    using (var reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(string.Format("{0}.{1}", ns, res))))
    {
        return reader.ReadToEnd();
    }
}

are you sure about that? according this this link it looks like I am....stackoverflow.com/questions/1065168/…
t
t3chb0t

By all your powers combined I use this helper class for reading resources from any assembly and any namespace in a generic way.

public class ResourceReader
{
    public static IEnumerable<string> FindEmbededResources<TAssembly>(Func<string, bool> predicate)
    {
        if (predicate == null) throw new ArgumentNullException(nameof(predicate));

        return
            GetEmbededResourceNames<TAssembly>()
                .Where(predicate)
                .Select(name => ReadEmbededResource(typeof(TAssembly), name))
                .Where(x => !string.IsNullOrEmpty(x));
    }

    public static IEnumerable<string> GetEmbededResourceNames<TAssembly>()
    {
        var assembly = Assembly.GetAssembly(typeof(TAssembly));
        return assembly.GetManifestResourceNames();
    }

    public static string ReadEmbededResource<TAssembly, TNamespace>(string name)
    {
        if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
        return ReadEmbededResource(typeof(TAssembly), typeof(TNamespace), name);
    }

    public static string ReadEmbededResource(Type assemblyType, Type namespaceType, string name)
    {
        if (assemblyType == null) throw new ArgumentNullException(nameof(assemblyType));
        if (namespaceType == null) throw new ArgumentNullException(nameof(namespaceType));
        if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));

        return ReadEmbededResource(assemblyType, $"{namespaceType.Namespace}.{name}");
    }

    public static string ReadEmbededResource(Type assemblyType, string name)
    {
        if (assemblyType == null) throw new ArgumentNullException(nameof(assemblyType));
        if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));

        var assembly = Assembly.GetAssembly(assemblyType);
        using (var resourceStream = assembly.GetManifestResourceStream(name))
        {
            if (resourceStream == null) return null;
            using (var streamReader = new StreamReader(resourceStream))
            {
                return streamReader.ReadToEnd();
            }
        }
    }
}

plus one for captain planet :P
a
ann

I know this is old, but I just wanted to point out for NETMF (.Net MicroFramework), you can easily do this:

string response = Resources.GetString(Resources.StringResources.MyFileName);

Since NETMF doesn't have GetManifestResourceStream


P
Philippe

I read an embedded resource text file use:

    /// <summary>
    /// Converts to generic list a byte array
    /// </summary>
    /// <param name="content">byte array (embedded resource)</param>
    /// <returns>generic list of strings</returns>
    private List<string> GetLines(byte[] content)
    {
        string s = Encoding.Default.GetString(content, 0, content.Length - 1);
        return new List<string>(s.Split(new[] { Environment.NewLine }, StringSplitOptions.None));
    }

Sample:

var template = GetLines(Properties.Resources.LasTemplate /* resource name */);

template.ForEach(ln =>
{
    Debug.WriteLine(ln);
});

D
Dietrich Baumgarten

Some VS .NET project types don’t auto-generate a .NET (.resx) file. The following steps add a Resource file to your project:

Right-click the project node and select Add/New Item, scroll to Resources File. In the Name box choose an appropriate name, for instance Resources and click the button Add. The resource file Resources.resx is added to the project and can be seen as a node in the solution explorer. Actually, two files are created, there is also an auto-generated C# class Resources.Designer.cs. Don’t edit it, it is maintained by VS. The file contains a class named Resources.

Now you can add a text file as a resource, for example an xml file:

Double-click Resources.resx. Select Add Resource > Add Existing File and scroll to the file you want to be included. Leave the default value Internal for Access Modify. An icon represents the new resource item. If selected, the property pane shows its properties. For xml files, under the property Encoding select Unicode (UTF-8) – Codepage 65001 instead of the default local codepage. For other text files select the correct encoding of this file, for example codepage 1252. For text files like xml files, the class Resources has a property of type string that is named after the included file. If the file name is e.g. RibbonManifest.xml, then the property should have the name RibbonManifest. You find the exact name in the code file Resources.Designer.cs. Use the string property like any other string property, for example: string xml = Resources.RibbonManifest. The general form is ResourceFileName.IncludedTextFileName. Don’t use ResourceManager.GetString since the get-function of the string property has done that already.


Thank you! This was the only answer that worked for me, which had intelligible steps I could understand. My app did not have a resource file and I had to create one. Only thing was on step 1 for me there was no Add button, I had to click save instead.
B
BullyWiiPlaza

This is a class which you might find very convenient for reading embedded resource files from the current Assembly:

using System.IO;
using System.Linq;
using System.Text;
using System.Reflection;

public static class EmbeddedResourceUtils
{
    public static string ReadFromResourceFile(string endingFileName)
    {
        var assembly = Assembly.GetExecutingAssembly();
        var manifestResourceNames = assembly.GetManifestResourceNames();

        foreach (var resourceName in manifestResourceNames)
        {
            var fileNameFromResourceName = _GetFileNameFromResourceName(resourceName);
            if (!fileNameFromResourceName.EndsWith(endingFileName))
            {
                continue;
            }

            using (var manifestResourceStream = assembly.GetManifestResourceStream(resourceName))
            {
                if (manifestResourceStream == null)
                {
                    continue;
                }

                using (var streamReader = new StreamReader(manifestResourceStream))
                {
                    return streamReader.ReadToEnd();
                }
            }
        }

        return null;
    }
    
    // https://stackoverflow.com/a/32176198/3764804
    private static string _GetFileNameFromResourceName(string resourceName)
    {
        var stringBuilder = new StringBuilder();
        var escapeDot = false;
        var haveExtension = false;

        for (var resourceNameIndex = resourceName.Length - 1;
            resourceNameIndex >= 0;
            resourceNameIndex--)
        {
            if (resourceName[resourceNameIndex] == '_')
            {
                escapeDot = true;
                continue;
            }

            if (resourceName[resourceNameIndex] == '.')
            {
                if (!escapeDot)
                {
                    if (haveExtension)
                    {
                        stringBuilder.Append('\\');
                        continue;
                    }

                    haveExtension = true;
                }
            }
            else
            {
                escapeDot = false;
            }

            stringBuilder.Append(resourceName[resourceNameIndex]);
        }

        var fileName = Path.GetDirectoryName(stringBuilder.ToString());
        return fileName == null ? null : new string(fileName.Reverse().ToArray());
    }
}

@Krythic: It's obvious if you copy the whole class into your source code. Your IDE will resolve everything automatically then. I already included the using statements so the information was not missing from the answer.
@Krythic: It's edited now to remove the static usings
Thanks so much for this just a polite notice it's missing a using System.Reflection; :D
B
Bimo

After reading all the solutions posted here. This is how I solved it:

// How to embedded a "Text file" inside of a C# project
//   and read it as a resource from c# code:
//
// (1) Add Text File to Project.  example: 'myfile.txt'
//
// (2) Change Text File Properties:
//      Build-action: EmbeddedResource
//      Logical-name: myfile.txt      
//          (note only 1 dot permitted in filename)
//
// (3) from c# get the string for the entire embedded file as follows:
//
//     string myfile = GetEmbeddedResourceFile("myfile.txt");

public static string GetEmbeddedResourceFile(string filename) {
    var a = System.Reflection.Assembly.GetExecutingAssembly();
    using (var s = a.GetManifestResourceStream(filename))
    using (var r = new System.IO.StreamReader(s))
    {
        string result = r.ReadToEnd();
        return result;
    }
    return "";      
}

J
Jamisco

The answer is quite simple, simply do this if you added the file directly from the resources.resx.

string textInResourceFile = fileNameSpace.Properties.Resources.fileName;

With that line of code, the text from the file is directly read from the file and put into the string variable.


That will return byte array not string
@Elnoor I do not believe this is true or at least not true with all versions. of .NetCore/Framework. I tested this with .Net 4.0 and fileNameSpace.Properties.Resources.fileName; is a string type.
I have tested in Core. Maybe in Net standard yeah will return string
S
Saeb Amini

I wanted to read the embedded resource just as a byte array (without assuming any specific encoding), and I ended up using a MemoryStream which makes it very simple:

using var resStream = assembly.GetManifestResourceStream(GetType(), "file.txt");
var ms = new MemoryStream();
await resStream .CopyToAsync(ms);
var bytes = ms.ToArray();

b
bytedev
public class AssemblyTextFileReader
{
    private readonly Assembly _assembly;

    public AssemblyTextFileReader(Assembly assembly)
    {
        _assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
    }

    public async Task<string> ReadFileAsync(string fileName)
    {
        var resourceName = _assembly.GetManifestResourceName(fileName);

        using (var stream = _assembly.GetManifestResourceStream(resourceName))
        {
            using (var reader = new StreamReader(stream))
            {
                return await reader.ReadToEndAsync();
            }
        }
    }
}

public static class AssemblyExtensions
{
    public static string GetManifestResourceName(this Assembly assembly, string fileName)
    {
        string name = assembly.GetManifestResourceNames().SingleOrDefault(n => n.EndsWith(fileName, StringComparison.InvariantCultureIgnoreCase));

        if (string.IsNullOrEmpty(name))
        {
            throw new FileNotFoundException($"Embedded file '{fileName}' could not be found in assembly '{assembly.FullName}'.", fileName);
        }

        return name;
    }
}
// To use the code above:
var reader = new AssemblyTextFileReader(assembly);

string text = await reader.ReadFileAsync(@"MyFile.txt");

F
Felix Keil

I was annoyed that you had to always include the namespace and the folder in the string. I wanted to simplify the access to the embedded resources. This is why I wrote this little class. Feel free to use and improve!

Usage:

using(Stream stream = EmbeddedResources.ExecutingResources.GetStream("filename.txt"))
{
 //...
}

Class:

public class EmbeddedResources
{
    private static readonly Lazy<EmbeddedResources> _callingResources = new Lazy<EmbeddedResources>(() => new EmbeddedResources(Assembly.GetCallingAssembly()));

    private static readonly Lazy<EmbeddedResources> _entryResources = new Lazy<EmbeddedResources>(() => new EmbeddedResources(Assembly.GetEntryAssembly()));

    private static readonly Lazy<EmbeddedResources> _executingResources = new Lazy<EmbeddedResources>(() => new EmbeddedResources(Assembly.GetExecutingAssembly()));

    private readonly Assembly _assembly;

    private readonly string[] _resources;

    public EmbeddedResources(Assembly assembly)
    {
        _assembly = assembly;
        _resources = assembly.GetManifestResourceNames();
    }

    public static EmbeddedResources CallingResources => _callingResources.Value;

    public static EmbeddedResources EntryResources => _entryResources.Value;

    public static EmbeddedResources ExecutingResources => _executingResources.Value;

    public Stream GetStream(string resName) => _assembly.GetManifestResourceStream(_resources.Single(s => s.Contains(resName)));

}

And what about super simple solution: var resName = assembly.GetManifestResourceNames().Where(i => i.EndsWith(fileName)).FirstOrDefault(); It won't work if you place whole directories into assembly, but otherwise it's just one line ;)
@Harry sure you can do this. How does this correlate with my answer? Do you want to improve the GetStream Method? And how do you handle ambiguity then?
@Estevez using System; using System.IO; using System.Linq; using System.Reflection;
For some reason class doesn't work when placed in another project. Calling and Executing assemblies are both refer to the assembly with this class, not the one that actually executes tests. Without static and lazy initialization all good, tho.
@CurlyBrace Thank you. The lazy evaluation is a real flaw in this answer, because the calling & executing assemblies change dependending on the context. They need to be resolved on every access.
M
Matteo Gariglio

As indicated by SonarCloud better to do:

public class Example
{
    public static void Main()
    { 
        // Compliant: type of the current class
        Assembly assembly = typeof(Example).Assembly; 
        Console.WriteLine("Assembly name: {0}", assem.FullName);

        // Non-compliant
        Assembly assembly = Assembly.GetExecutingAssembly();
        Console.WriteLine("Assembly name: {0}", assem.FullName);
    }
}

S
Swat

For all the people that just quickly want the text of a hardcoded file in winforms;

Right-click your application in the solution explorer > Resources > Add your file. Click on it, and in the properties tab set the "FileType" to "Text". In your program just do Resources..toString(); to read the file.

I would not recommend this as best practice or anything, but it works quickly and does what it needs to do.


M
MasterCassidy

Read Embedded TXT FILE on Form Load Event.

Set the Variables Dynamically.

string f1 = "AppName.File1.Ext";
string f2 = "AppName.File2.Ext";
string f3 = "AppName.File3.Ext";

Call a Try Catch.

try 
{
     IncludeText(f1,f2,f3); 
     /// Pass the Resources Dynamically 
     /// through the call stack.
}

catch (Exception Ex)
{
     MessageBox.Show(Ex.Message);  
     /// Error for if the Stream is Null.
}

Create Void for IncludeText(), Visual Studio Does this for you. Click the Lightbulb to AutoGenerate The CodeBlock.

Put the following inside the Generated Code Block

Resource 1

var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(file1))
using (StreamReader reader = new StreamReader(stream))
{
string result1 = reader.ReadToEnd();
richTextBox1.AppendText(result1 + Environment.NewLine + Environment.NewLine );
}

Resource 2

var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(file2))
using (StreamReader reader = new StreamReader(stream))
{
string result2 = reader.ReadToEnd();
richTextBox1.AppendText(
result2 + Environment.NewLine + 
Environment.NewLine );
}

Resource 3

var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(file3))

using (StreamReader reader = new StreamReader(stream))
{
    string result3 = reader.ReadToEnd();
    richTextBox1.AppendText(result3);
}

If you wish to send the returned variable somewhere else, just call another function and...

using (StreamReader reader = new StreamReader(stream))
{
    string result3 = reader.ReadToEnd();
    ///richTextBox1.AppendText(result3);
    string extVar = result3;

    /// another try catch here.

   try {

   SendVariableToLocation(extVar)
   {
         //// Put Code Here.
   }

       }

  catch (Exception ex)
  {
    Messagebox.Show(ex.Message);
  }

}

What this achieved was this, a method to combine multiple txt files, and read their embedded data, inside a single rich text box. which was my desired effect with this sample of Code.


s
schlebe

For users that are using VB.Net

Imports System.IO
Imports System.Reflection

Dim reader As StreamReader
Dim ass As Assembly = Assembly.GetExecutingAssembly()
Dim sFileName = "MyApplicationName.JavaScript.js" 
Dim reader = New StreamReader(ass.GetManifestResourceStream(sFileName))
Dim sScriptText = reader.ReadToEnd()
reader.Close()

where MyApplicationName is namespace of my application. It is not the assembly name. This name is define in project's properties (Application tab).

If you don't find correct resource name, you can use GetManifestResourceNames() function

Dim resourceName() As String = ass.GetManifestResourceNames()

or

Dim sName As String 
    = ass.GetManifestResourceNames()
        .Single(Function(x) x.EndsWith("JavaScript.js"))

or

Dim sNameList 
    = ass.GetManifestResourceNames()
        .Where(Function(x As String) x.EndsWith(".js"))