In my Unity3D project I have several text fields. I saved my text in some text files.
When I test my project on the computer everything works fine, and my code reads the text files. But if I upload to my iPad it won't work and the text fields stay empty.
In the image you can see where I have saved my text files.
To read my text files I use the following code:
public Text infoText;
void Update()
{
readTextFile("FileName", "StepNumber")
}
public void readTextFile(string fileName, string stepNumber)
{
StreamReader txt_Reader = new StreamReader("Assets/Resources/Text_Files/" + fileName + ".txt");
while(!txt_Reader.EndOfStream)
{
string txt_String = txt_Reader.ReadLine();
if(txt_String.Contains(stepNumber))
{
string[] separator = { "_" };
string[] strList = txt_String.Split(separator, System.StringSplitOptions.RemoveEmptyEntries);
infoText.text = string.Join("\n", strList.Skip(1));
}
}
}
What do I have to change that my iPad can read from the text files?
EDIT:
My text files looks like this:
Step 1:
* Some Text
* Some Text
Step 2:
* Some Text
* Some Text
* Some Text
Step 3:
* Some Text
Step 4:
* Some Text
So each * should be a new line in my text field. With my old c# code this was no problem, but with
var lines = textFiles.text.Split(new char[] { `*` });
foreach(var line in lines)
{
...
}
i do not know how I can do that, that my text field shows all two lines for step one.
First of all from the Best Practices for the Resources folder
**Don't use it!
Please read the reasons there.
In general for system paths do never use simple string concatenation + "/" +!
Rather use Path.Combine which automatically uses the correct path separators according to the executing platform
Path.Combine(Application.dataPath, "Resources", "Text_Files", fileName + ".txt");
However, you don't/can't simply use a StreamReader to access the Resources folders (See Resources API since it is packed into the build so you have to go through Resources.Load like
// Here you can use / since this is how Unity stores internal paths
// for load you omit the suffix
TextAsset textFile = Resources.Load<TextAsset>("Text_Files/" + filename);
string fileContent = textFile.text;
Or also have a look at Resources.LoadAsync to not block the main thread meanwhile.
BUT
Speaking about blocking the main thread: What you definitely do not want to do is using any of these within Update thus doing heavy FileIO/Loading every frame!
Store the content of that file once as it won't change afterwards anyway!
Depending on your needs you could also simply put your file in any other folder inside the Assets and simply use a TextAsset field directly and drag it into according slot via the Inspector
public TextAsset textFile;
Finally you can then go through the lines one by one using e.g.
var lines = textFile.text.Split(new char[]{'/n'});
foreach(var line in lines)
{
...
}
Note that also that Split is a quite heavy operation since it has to parse every single character in the string and create new substrings so even store these results somewhere in a field of you need them multiple times during runtime!
Typed on smartphone but I hope the idea gets clear
In your case, StreamReader txt_Reader = new StreamReader("Assets/Resources/Text_Files/" + fileName + ".txt"); points to a file on your computer. Assets/Resources/Text_Files/ only exists on your computer.
You need to access a folder that exists on your iPad. It's likely you also didn't save your data to a folder existing on your IPad.
For other devices you could use : Application.persistentDataPath + "/" + fileName
Source: https://docs.unity3d.com/ScriptReference/Application-dataPath.html
Related
This question already has answers here:
Adding a Line to the Middle of a File with .NET
(6 answers)
Closed 1 year ago.
I'd just like to start by saying the I am extremely new to C# and coding in general so please excuse the newbie mistakes.
I have a text document that looks something like this (inserted manually)
[VIDEO]
[!VIDEO]
And I want to be able to write some text in between these 2 "tags" without erasing them.
The current code I have to write things in the document is:
private void ReadVideoFiles()
{
string[] files =Directory.GetFiles(#"Z:\APP_LANX\VIDEOS", "*.mp4"); //Goes to the specific location of the file and gets the whole location
videoID = 0;
for (int i = 0; i < files.Length; i++)
{
videoID++;
files[i] = videoID.ToString() + ": " + files[i]; //changes the previous string so that it now contains the ID and the location
}
foreach (string file in files)
{
sw.WriteLine(file); //Uses the StreamWriter, declared previously
}
sw.Close();
}
But as you can imagine this just fully replaces everything in the document and adds the code, and the result is something like this:
1: Z:\APP_LANX\VIDEOS\vid1.mp4
2: Z:\APP_LANX\VIDEOS\vid2.mp4
3: Z:\APP_LANX\VIDEOS\vid3.mp4
4: Z:\APP_LANX\VIDEOS\vid4.mp4
instead of this
[VIDEO]
1: Z:\APP_LANX\VIDEOS\vid1.mp4
2: Z:\APP_LANX\VIDEOS\vid2.mp4
3: Z:\APP_LANX\VIDEOS\vid3.mp4
4: Z:\APP_LANX\VIDEOS\vid4.mp4
[!VIDEO]
Is there any way to check for these tags in specific so that I can write in between? Thank you!
Edit*: Later on I'll be adding new things to this text file, for example after the [!VIDEO] line I'll have other lines with something like EX1: 4 and multiple other lines that shouldn't be overwritten, only the part in between those tags can be changed.
TL:DR - Basically I want to be able to add text between those two tags (overwriting whatever is in between) while the rest of the text file remains unchanged
Assuming that your method ReadVideoFiles is called only, you could do something like this:
private void ReadVideoFiles()
{
string[] files =Directory.GetFiles(#"Z:\APP_LANX\VIDEOS", "*.mp4"); //Goes to the specific location of the file and gets the whole location
videoID = 0;
for (int i = 0; i < files.Length; i++)
{
videoID++;
files[i] = videoID.ToString() + ": " + files[i]; //changes the previous string so that it now contains the ID and the location
}
// Add header to file
sw.WriteLine($"[VIDEO]{Environment.NewLine}{Environment.NewLine}");
foreach (string file in files)
{
sw.WriteLine(file); //Uses the StreamWriter, declared previously
}
// Add footer to file
sw.WriteLine($"{Environment.NewLine}{Environment.NewLine}[!VIDEO]");
sw.Close();
}
Instead of inserting your input between the "tags", this code produces the complete file from scratch including the tags.
Maybe this is already sufficient for you? 🤔
If you don't want to spend too much time on it, you can just change :
sw.WriteLine(file);
by :
sw.WriteLine("[VIDEO]\n\n" + file + "\n\n[!VIDEO]");
You'll have the expected result.
You can insert your text at the index of the end tag in your list without overwriting any previous text you had in the file.
using System.IO;
using System.Linq;
endTag = "[!VIDEO]"
foreach (string file in files)
{
sw.Insert(sw.IndexOf(endTag), "text you want to add");
// You can replace "text you want to add" with your file variable
}
sw.Close();
I need to create a file, write one line of text in the file and then delete the file and estimate how long it will take to do it.
Unfortunately, I am running in couple of problems, first I cannot write in the file, it succesfully creates it but nothing is written to it.
Secondly, I cannot delete the file because it has been used by another process.
Please help.
I have been trying to delete it for quite some time.
I have also tried wrapping it in usings, to no avail.
Writing to the file is the same situation. I even changed it so the file ends in .txt but that does not make any difference.
public static void ProcessFile(string path)
{
string fullpath = path;
int lastBracket = path.LastIndexOf("\\");
// the filename does not contain .html so it can be named to .txt
string newFileName = path.Substring(lastBracket + 1, path.Length - lastBracket - 6) + " hrefcount.txt";
string newPath = Path.Combine(fullpath.Substring(0, lastBracket), newFileName);
Console.WriteLine(newPath);
int counter = 0;
foreach (var line in File.ReadAllLines(path))
{
if (line.Contains("href="))
{
counter++;
}
}
var fileCreated = File.CreateText(newPath);
fileCreated.WriteLine("The number of times href appears is " + counter);
Console.WriteLine();
File.Delete(newPath);
}
File created, nothing written to it, unable to delete due to has been used by another process.
Instead of File.CreateText() use File.WriteAllText(path, content). It writes the text and then closes the file allowing you to delete it if necessary
Instead of the following
var fileCreated = File.CreateText(newPath);
fileCreated.WriteLine("The number of times href appears is " + counter);
You may write
File.WriteAllText(newPath, $"The number of times href appears is {counter}");
Refer documentation here
The issue with your approach is that CreateText() is used to write to a stream. But in your case, it is not necessary since you're writing all the text at once to the file and that text is small in size.
The cause of your error is the fact that you don't close and dispose the variable fileCreated. This, is a FileStream and until you close and dispose this variable the file is not available to anyone, even your own code that has opened the file.
So the first thing to do is
using (var fileCreated = File.CreateText(newPath))
{
fileCreated.WriteLine("The number of times href appears is " + counter);
}
The using block ensure the proper disposal of the variable.
However there are other parts of your code that you can simplify
public static void ProcessFile(string path)
{
string folder = Path.GetDirectoryName(path);
string file = Path.GetFileName(path);
// Keep the first 6 characters from the source file?
string newFile = file.Substring(0, 6) + " hrefcount.txt";
string newPath = Path.Combine(folder, newFile);
// A single line to retrieve your counter thanks to IEnumerables and Linq
int counter = File.ReadLines(path).Count(x => x.Contains("href="));
// Create, but dispose also the file
using (var fileCreated = File.CreateText(newPath))
{
fileCreated.WriteLine("The number of times href appears is " + counter);
}
// Now you should be free to delete the file
File.Delete(newPath);
}
I cannot delete the file because it has been used by another process.
Probably you're not disposed your files after creating. To do that, you should additionally use FileStream.Dispose method:
File.Create(path).Dispose();
estimate how long it will take to do it
You can use System.Diagnostics.Stopwatch class to do that:
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
/*
do the magic
*/
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
You can use File.WriteAllText method instead of File.CreateText to write your text to file:
File.WriteAllText(path, myText);
Remember that since the .NET 4 you can use this method with array or List<T> too instead of string.
File.Create() supports Dispose method which help you to release that file resource to perform further operations
To perform operations on file follow below steps:
Create file and free up the resource using Dispose().
File.Create(newPath).Dispose();
or Use StreamWriter to create file and write text to it.
using( StreamWriter sw = new StreamWriter(newPath, true)
{
sw.Write($"The number of times href appears is {counter}"); //Used string interpolation
}
StreamWriter calls Dispose() function to release file resource.
When Writer release control over file, then you will not face issue related to I cannot delete the file because it has been used by another process.
Now you can delete file using,
File.Delete(newPath);
MSDN : IDisposable.Dispose Method
Performs application-defined tasks associated with freeing, releasing,
or resetting unmanaged resources.
I'm using a Script Task (C#) within SSIS to move all .txt files from one folder to another. However, when I execute this, nothing happens. The Script Task actually reports success but the text files do not move. I'm using variables for all the paths - they all start with 2 backslashes and end with 1 back slash.
All I want to do is move all text files from the source destination to the destination folder.
What am I missing?
public void Main()
{
DirectoryInfo di = new DirectoryInfo(Dts.Variables["IN_Folder"].Value.ToString());
string destinationFolder = Dts.Variables["User::IN_Folder_Processing"].Value.ToString();
string sourceFolder = Dts.Variables["User::IN_Folder"].Value.ToString();
FileInfo[] fi = di.GetFiles("*.txt");
String filename = fi[0].Name;
string sourceFileName = filename;
string destinationFile = destinationFolder + sourceFileName;
string sourceFile =sourceFolder + sourceFileName;
if (File.Exists(destinationFile))
File.Delete(destinationFile);
// To move a file or folder to a new location:
System.IO.File.Move(sourceFile, destinationFile);
Dts.TaskResult = (int)ScriptResults.Success;
}
Like I mentioned in the comments, there seems to be no need to use a Script Task to do this; a far better option would be a Foreach Loop Container and a FileSystem Task.
Firstly create the Foreach Loop Container, open the editor and go to the Collection Pane. Change Enumerator to Foreach File Enumerator. I assume you are using a variable for the directory, so click ... for Expressions and select Directory for the Property and your variable for the expression.
As you specifically want to deal with txt files, change Files to *.txt. What option you use for Retreive File Name depends on how your determining the destination. I'm going to assume you have another variable with the directory target, so select Name and extention.
Go to the Variable Mappings pane and select your filename variable, or create a new one. Leave the Index as 0. This will store the Name and extension of the file you are going to move.
Create a new variable in your package, called OriginalFilePath, or something easily identifiable. Set the value to string and then change the Scope to your Foreach Loop Container. Now open the expression pane for the variable and set the expression to something like:
#[User::SourceDirectoryVariable] + "\\" + #[User::FileNameVariable]
Obviously change the variable names to what they need to be. Now create a second variable (same settings), however, using your destination directory variable instead of the source (#[User::DestinationDirectoryVariable] + "\\" + #[User::FileNameVariable]).
Now, in your Control Flow, create a File System Task within your Foreach Loop Container. Change Operation to Move File. Then fill in the rest of the pane as needed (IsSourceVariable will be True, and then select your variable). Do the same for the destination, and then you should be good to go.
Any problems, please do comment with the error.
You should avoid manually concatenating file paths using the string + operator. This leaves a lot of room for error. Use System.IO.Path.Combine(). This ensures all leading and trialing slashes are formatted properly.
There's also no need to rebuild the file paths manually using all those additional variables. Something like this will work just fine so long as your input variable directories are correct:
public void Main()
{
DirectoryInfo di = new DirectoryInfo(Dts.Variables["IN_Folder"].Value.ToString());
string destinationFolder = Dts.Variables["User::IN_Folder_Processing"].Value.ToString();
FileInfo[] fi = di.GetFiles("*.txt");
foreach (FileInfo f in fi)
{
FileInfo destinationFile = new FileInfo(Path.Combine(destinationFolder, f.Name));
if (destinationFile.Exists)
destinationFile.Delete();
f.MoveTo(destinationFile.FullName);
}
Dts.TaskResult = (int)ScriptResults.Success;
}
So I'm making a program to type what's in my clipboard into a text file, I plan on later transporting this information into an AHK script, but I want to do this all in one program, so if possible it will append to the .ahk file, but instead of it appending to the very last line, I need it to append to the line before return, which is the final line of the file.
Send ::redeem N4E2vzCEp {enter}
Sleep 1000
return
That's the end of the file, if possible I want my program to do something like:
string pasted = Clipboard.GetText();
sw.WriteLine.SecondLastLine("Send ::redeem " + pasted + " {enter}");
sw.WriteLine.SecondLastLine("Sleep 1000"); //Fully aware that secondlastline is not a valid command
But I don't know what the proper way of actually coding this would be.
Current code:
private void paste_Click(object sender, EventArgs e)
{
string path = #"c:\users\john\desktop\auths.txt";
using (StreamWriter sw = File.AppendText(path))
{
string pasted = Clipboard.GetText();
sw.WriteLine("Send ::redeem " + pasted + " {enter}");
sw.WriteLine("Sleep 1000");
}
}
What you can do is reading all lines into a List and then insert the new line at a specific position and write the lines back to the file.
List<string> lines = File.ReadAllLines("your file").ToList();
lines.Insert(lines.Count - 2, "new line");
File.WriteAllLines("your file", lines);
I am working on a code that copies information line by line from one text file and pastes it onto another. Each line contains "|" and after that symbol the timestamp of the date modified of each line is displayed. I am having trouble with finding a way that will allow me to access the date modified property from a build server when I run my utility and replaces the old date modified in the old text file with the new date modified property in the new text file. Here is what I have so far:
class Program
{
class NewTime
{
public DateTime Current { get; set; }
}
static void Main(string[] args)
{
int counter = 0;
string line;
// Read the file and display it line by line.
System.IO.StreamReader file = new System.IO.StreamReader(args[0]);
System.IO.StreamWriter filewriter = new System.IO.StreamWriter(args[1], false);
while ((line = file.ReadLine()) != null)
{
Thread.Sleep(10);
string [] pieces = line.Split(new char[] { '|' });
if(pieces.Length == 2)
{
*DateTime outDate;
if(DateTime.TryParse(pieces[1], out outDate))
{
string outputstring = string.Format(" {0:yyyy-MM-dd-hh-mm-ss-ff-tt}", DateTime.Now);
filewriter.WriteLine(pieces[0] + "|" + outputstring);
}*
else
filewriter.WriteLine(line);
}
else
filewriter.WriteLine(line);
System.Console.WriteLine(line);
counter++;
}
file.Close();
filewriter.Close();
System.Console.ReadLine();
}
}
The portion in between the stars was my first attempt, but that didn't give me what I want. It simply replaced the old time with the current time of when I ran the utility on my computer.
Any help is appreciated =)
If I'm understanding correctly, its looks like a timing problem: You want to replace the old date modified in the old text file with the new date modified property in the new text file. But DateTime.Now is not close enough, and you can't get the filesystem-generated DateModified (actually LastWriteTime) until the file has been saved on the file system.
If so, since you have to flush and close the new file for the file system to write the LastModified value, even re-reading the LastAccessTime on the newly created file FileInfo may not give you the value you are after.
It's a little messy because NTFS updates to the last write access time for a file can take an indeterminate amount of time to resolve after the last access. Even FAT systems have a write time resolution of ~2 seconds, according to Windows SDK
[http://msdn.microsoft.com/en-us/library/windows/desktop/ms724933(v=vs.85).aspx]
So, if replacing the old time with the current time (as you are doing) is not close enough, you would need to complete your initial file-creation loop, then derive a FileSystemInfo object on the file and call its Refresh method to get the latest value, and then re-write the value (LastWriteTime or LastAccessTime) into the file.
Refer to .Net Framework documentation for FileSystemInfo.LastAccessTime property for details: http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.lastaccesstime(v=vs.110).aspx