C# Reading of text file stopped working - with Unity - c#

I have a program that reads in any amount of .txt files in a directory and assembles them in a list, this list is then displayed to the user one at a time. The user makes a decision at this point and the reaction time is stored. Recently the text import script stopped working for no reason. I haven't changed any of the text import code in months.
I've checked the code that reads in the files and nothing was being recognised at all.
This is part of Unity program just FYI.
TextImport
using UnityEngine;
using System.Collections;
using System.IO;
using System.Collections.Generic;
public class TextImport : MonoBehaviour {
public static List<string> TextLines;
bool FileRead = false;
// Use this for initialization
void Awake() {
TextLines = new List<string>();
string path;
if(Application.platform == RuntimePlatform.WindowsEditor)
{
path = Application.dataPath + "/Resources/";
}
else if (Application.platform == RuntimePlatform.WindowsPlayer)
{
path = Application.dataPath;
}
else if (Application.platform == RuntimePlatform.Android)
{
FileRead = true;
path = "/StroopTest/";
TextAsset Allfiles = Resources.Load("Android/AllText") as TextAsset;
string[] AllLinesAndroid = Allfiles.text.Split("\n"[0]);
foreach (string Line in AllLinesAndroid)
{
TextLines.Add(Line);
break;
}
}
else
{
path = Application.dataPath;
}
if (!FileRead)
{
DirectoryInfo info = new DirectoryInfo(path);
FileInfo[] fileInfo = info.GetFiles("*.txt");//I've changed the "*.txt" to nothing to read all files instead of just .txt files
foreach (FileInfo file in fileInfo)
{
//I created another List here to store the file names that were read, however none were.
string[] AllLines = File.ReadAllLines(file.FullName);
foreach (string line in AllLines)
{
TextLines.Add(line);
}
}
}
}
}

I suggest using Application.persistentDataPath.
Application.dataPath is a read-only directory, where (if packaged to do so) you can find game-related assets there.
Application.persistentDataPath, however, is a writeable directory and returns a path (different for different platforms) where you can add stuff at runtime (ex: writing a file, downloading a file, etc.)
I hope that helps.

Related

Running into error "Access to the path 'C:\Users\Work & School\Documents\My Music' is denied." C#-App console

i am learning from the tutorial from microsoft "https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-enumerate-directories-and-files"
Specifically, this one:
using System;
using System.IO;
using System.Linq;
class Program
{
static void Main(string[] args)
{
try
{
// Set a variable to the My Documents path.
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var files = from file in Directory.EnumerateFiles(docPath, "*.txt", SearchOption.AllDirectories)
from line in File.ReadLines(file)
where line.Contains("Microsoft")
select new
{
File = file,
Line = line
};
foreach (var f in files)
{
Console.WriteLine($"{f.File}\t{f.Line}");
}
Console.WriteLine($"{files.Count().ToString()} files found.");
}
catch (UnauthorizedAccessException uAEx)
{
Console.WriteLine(uAEx.Message);
}
catch (PathTooLongException pathEx)
{
Console.WriteLine(pathEx.Message);
}
}
}
However, when I run this file, I run into the error:
Access to the path 'C:\Users\Work & School\Documents\My Music' is denied.
Im wondering, why is it accessing the My Music folder in the first place, when all it should be doing is going to Documents? Furthermore, I also tried deleting the Music folder from a separate folder but i still end up getting the same error. I also put two text files in the document folder, both specifying "Microsoft" so they will return the values from the lines. How would I get rid of the error?
*for the text files, I also turned off the readonly attribute
why is it accessing the My Music folder in the first place?
Because of your SearchOption. SeachOption.AllDirectories searches file in current directory as well as all its subdirectories.
If you want to search only in current directory not in its subdirectories then, change SearchOption to SeachOption.TopDirectoryOnly
Your updated code should look like,
var files = from file in Directory.EnumerateFiles(docPath, "*.txt", SearchOption.TopDirectoryOnly)
from line in File.ReadLines(file)
where line.Contains("Microsoft")
select new
{
File = file,
Line = line
};
SearchOption Enum (From MSDN)

Searching through all subfolders when using Resources.Load in Unity

Is it possible to have Resources.Load(name, type) search for a fitting asset not just in the base Resources folder / a specified subfolder, but instead the full subfolder structure under Resources?
Example folder structure of
Resources
- Subfolder
- image.png
I would like something like Resources.Load("image", typeof(Texture2D)) to return the image without the user having to specify "Subfolder/image".
I know it's ugly, but it's supposed to be a "drop it in your bashed together project without worrying about your folder structure"-type utility script and I won't know the subfolder.
The accepted answer only works in the editor context, and doesn't work after building the game. The resources folder is packed when building your game, and will no longer be a file hierarchy you can loop through with Directory.GetDirectories. The only way to get this to work is to save all file paths while still in the editor context, and use this file hierarchy to load the assets at runtime. In my project I used the following code to add a button to the component that uses the assets in the resources folder, named CharacterGen. When this button is clicked, all png files in all subfolders in the Resources folder are saved to the public attribute named filePaths that CharacterGen has.
[CustomEditor(typeof(CharacterGen))]
public class RefreshCharacterList : Editor
{
CharacterGen charGen;
public void OnEnable()
{
charGen = (CharacterGen)target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Load resource paths"))
{
List<String> paths = new List<string>();
LoadPathsRecursive("", ref paths);
charGen.filePaths = paths;
EditorUtility.SetDirty(charGen); //original post didn't have this line, but this is needed to make sure your changes are saved.
}
}
void LoadPathsRecursive(string path, ref List<string> paths)
{
var fullPath = Application.dataPath + "/Resources/" + path;
Debug.Log("fullPath: " + fullPath);
DirectoryInfo dirInfo = new DirectoryInfo(fullPath);
foreach(var file in dirInfo.GetFiles())
{
if (file.Name.Contains(".PNG") && !file.Name.Contains(".meta"))
{
paths.Add(path + "/" + file.Name.Replace(".PNG", ""));
}
}
foreach (var dir in dirInfo.GetDirectories())
{
LoadPathsRecursive(path + "/" + dir.Name, ref paths);
}
}
}
In CharacterGen I later evoke Resources.Load, using the paths that were saved when clicking the button.
foreach (var filePath in filePaths)
{
var loadedSprite = Resources.Load<Sprite>(filePath);
//Do something with loadedSprite
}
Edit: I failed to mention an important detail in my original post. the filePaths is a Monobehaviour field, and I marked it with [SerializeField] (alternatively it could be marked as public) so that unity actually serializes the values of this field and includes it in the build.
There is no way to change the Resouces.Load() static method functionality, it's Unity Internal. However, you can write your own custom class that does your desired functionality. The code needs to find all the directories inside the Resources folder and search for the file. Let's call the class ResourcesExtension
public class ResourcesExtension
{
public static string ResourcesPath = Application.dataPath+"/Resources";
public static UnityEngine.Object Load(string resourceName, System.Type systemTypeInstance)
{
string[] directories = Directory.GetDirectories(ResourcesPath,"*",SearchOption.AllDirectories);
foreach (var item in directories)
{
string itemPath = item.Substring(ResourcesPath.Length+1);
UnityEngine.Object result = Resources.Load(itemPath+"\\"+resourceName,systemTypeInstance);
if(result!=null)
return result;
}
return null;
}
}
Then all you need to do is calling the static method.
ResourcesExtension.Load("image", typeof(Texture2D))
if you want to find all Object of a type and return as a List.
it Emad code Edited a bit.
private List<T> FindAllObject<T>()
{
List<T> tmp = new List<T>();
string ResourcesPath = Application.dataPath + "/Resources";
string[] directories = Directory.GetDirectories(ResourcesPath, "*", SearchOption.AllDirectories);
foreach (string item in directories)
{
string itemPath = item.Substring(ResourcesPath.Length + 1);
T[] reasult = Resources.LoadAll(itemPath, typeof(T)).Cast<T>().ToArray();
foreach (T x in reasult)
{
if (!tmp.Contains(x))
{
tmp.Add(x);
}
}
}
return tmp;
}
To use it.
List<MyClass> myClasses = FindAllObject<MyClass>();

Get file names from Resources sub folder

In my Resources folder I have a subfolder for images, I would like to get all the file names of those images from within that folder.
tried several Resources.loadAll methods to afterwards get the .name but without success
was is the right practice to achieve what I'm trying to do here ?
There is no built-in API to do this because the information is not after you build. You cant' even do this with what's in the accepted answer. That would only work in the Editor. When you build the project, your code will fail.
Here's what to do:
1. Detect when the build button is clicked or when a build is about to happen in the OnPreprocessBuild function.
2. Get all the file names with Directory.GetFiles, serialize it to json and save it to the Resources folder. We use json to make it easier to read individual file name. You don't have to use json. You must exclude the ".meta" extension.
Step 1 and 2 are done in the Editor.
3. After a build or during run-time, you can access the saved file that contains the file names as a TextAsset with Resources.Load<TextAsset>("FileNames") then de-serialize the json from TextAsset.text.
Below is very simplified example. No error handling and that's up to you to implement. The Editor script below saves the file names when you click on the Build button:
[Serializable]
public class FileNameInfo
{
public string[] fileNames;
public FileNameInfo(string[] fileNames)
{
this.fileNames = fileNames;
}
}
class PreBuildFileNamesSaver : IPreprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(UnityEditor.Build.Reporting.BuildReport report)
{
//The Resources folder path
string resourcsPath = Application.dataPath + "/Resources";
//Get file names except the ".meta" extension
string[] fileNames = Directory.GetFiles(resourcsPath)
.Where(x => Path.GetExtension(x) != ".meta").ToArray();
//Convert the Names to Json to make it easier to access when reading it
FileNameInfo fileInfo = new FileNameInfo(fileNames);
string fileInfoJson = JsonUtility.ToJson(fileInfo);
//Save the json to the Resources folder as "FileNames.txt"
File.WriteAllText(Application.dataPath + "/Resources/FileNames.txt", fileInfoJson);
AssetDatabase.Refresh();
}
}
During run-time, you can retrieve the saved file names with the example below:
//Load as TextAsset
TextAsset fileNamesAsset = Resources.Load<TextAsset>("FileNames");
//De-serialize it
FileNameInfo fileInfoLoaded = JsonUtility.FromJson<FileNameInfo>(fileNamesAsset.text);
//Use data?
foreach (string fName in fileInfoLoaded.fileNames)
{
Debug.Log(fName);
}
Hmm... why not try this.
using System.IO;
Const String path = ""; /file path
private void GetFiles()
{
string [] files = Directory.GetFiles (path, "*.*");
foreach (string sourceFile in files)
{
string fileName = Path.GetFileName (sourceFile);
Debug.Log("fileName");
}
}

Programmatically extract the .dlls from clickonce application .deploy files

I have a click once application which is deployed on server (network share), is there a way programmatically that extract the .dlls from the .dll.deploy files. Once we launch the application, it converts into dlls and places in user/appdata/local/... folder, but I need a way to extract the dlls without launching the application.
Thanks for the help.
Simply rename the files by omitting the .deploy suffix and you're done.
Since this task could get a little tedious - depending on the number of files in your ClickOnce package - you might want to use a file renaming tool.
Update:
I just noticed that you were asking for a way to do this programmatically. That makes it even easier since you don't have to worry about finding a file renaming tool:
public class Program
{
private const string SourcePath = "\\\\ClickOnceDeployDir";
private const string TargetPath = "C:\\Users\\UserName\\Documents\\ClickOnceTargetDir";
public static void Main(string[] args)
{
var sourceDirectory = new DirectoryInfo(SourcePath);
var targetDirectory = new DirectoryInfo(TargetPath);
// you can omit this step if you would like to do the renaming in-place
Copy(sourceDirectory, targetDirectory);
foreach (var file in targetDirectory.GetFiles("*.deploy", SearchOption.AllDirectories))
{
var directoryName = file.DirectoryName;
if (directoryName != null)
{
// here it is: rename the file
file.MoveTo(Path.Combine(directoryName, Path.GetFileNameWithoutExtension(file.Name)));
}
}
}
private static void Copy(DirectoryInfo sourceDirectory, DirectoryInfo targetDirectory)
{
foreach (var file in sourceDirectory.GetFiles())
{
file.CopyTo(Path.Combine(targetDirectory.FullName, file.Name));
}
foreach (var directory in sourceDirectory.GetDirectories())
{
Copy(directory, targetDirectory.CreateSubdirectory(directory.Name));
}
}
}

Directory is not reading properly

I am having One Directory
C:\Kuldeep\kverma\kver\
After that It consists thousands of Folders with Different name .Each Folder consists Different Excel File . I need to read Each Files from Different Folders .
I want To read All The Folders Path from C:\Kuldeep\kverma\kver\ Folder.
I used below code for getting the folders name with path ..
string path = #"C:\Kuldeep\kverma\kver\";
DirectoryInfo dir = new DirectoryInfo(path);
Console.WriteLine("File Name Size Creation Date and Time");
Console.WriteLine("========");
foreach (DirectoryInfo dirinfo in dir.GetDirectories())
{
String name = dirinfo.Name;
String pth = dirinfo.FullName;
Console.WriteLine( name, pth);
}
Total 10700 folders are there in C:\Kuldeep\kverma\kver\ Directory But It is reading only 54 Folder..
Please provide me any solution for Reading Folder name and location Also Reading File from Each Folder in Single Shot .
You should put a try catch around the GetDirectories call to handle the exceptions in the below post.
That might give you a clue as to why it is not enumerating properly.
http://msdn.microsoft.com/en-us/library/c1sez4sc.aspx
Try a recursive approach:
namespace ConsoleApplication1
{
using System;
using System.Collections.Generic;
using System.IO;
class Program
{
public static IList<DirectoryInfo> dirs;
static void Main(string[] args)
{
dirs = new List<DirectoryInfo>();
var dir = new DirectoryInfo(#"c:\tmp");
GetDirs(dir);
Console.WriteLine(dirs.Count);
}
public static void GetDirs(DirectoryInfo root)
{
foreach (var directoryInfo in root.GetDirectories())
{
dirs.Add(directoryInfo);
GetDirs(directoryInfo);
}
}
}
}
Now I'm not sure what hidden dangers might be lurking because of this (Stack overflow exceptions, access denied?) so I'd recommend placing a try..catch block in the foreach loop to help you out.
If you want to view the contents of every sub directory:
// Flatten out the directory structure in to a string array.
var directoryList = Directory.GetDirectories("<<RootPath>>", "*", SearchOption.AllDirectories);
foreach (var directory in directoryList)
{
DirectoryInfo info = new DirectoryInfo(directory);
}
edited with the questions code updated:
string path = #"C:\Kuldeep\kverma\kver\";
string[] directoryArray = Directory.GetDirectories(path, "*", SearchOption.AllDirectories);
foreach (var directory in directoryArray)
{
DirectoryInfo dirinfo = new DirectoryInfo(directory);
String name = dirinfo.Name;
String pth = dirinfo.FullName;
Console.WriteLine(name, pth);
}

Categories