I want to declare a string array, I was using this way of doing it
string[] matchingFiles = Directory.GetFiles(FilePath, FileNamePattern);
which worked perfectly, but now I want to enclose the Directory.GetFiles call in a try/catch block, but I cant also have the declaration of the string array in there because then it wont be in the right scope to use it outside of the try block. But if I try this:
string[] matchingActiveLogFiles;
try
{
matchingFiles = Directory.GetFiles(FilePath, FileNamePattern);
}
catch (Exception ex)
{
//logerror
}
I have not initialized the string array so I have an error. So I am wondering what is best practise in this situation, should I declare the string array outside the try block? And if so how?
The name is different for the string arrays, one is matchingActiveLogFiles the other is
matchingFiles
string[] matchingActiveLogFiles;
try
{
matchingActiveLogFiles = Directory.GetFiles(FilePath, FileNamePattern);
}
catch (Exception ex)
{
//logerror
}
This will initialize your array:
string[] matchingActiveLogFiles = {};
try
{
matchingFiles = Directory.GetFiles(FilePath, FileNamePattern);
}
catch (Exception ex)
{
//logerror
}
But I'm wondering, what error are you getting? Even with an uninitialized array, the above code should work. I also noticed that you have "matchingActiveLogFiles" on line 1 and "matchingFiles" on line 4. Perhaps that's your problem?
Initialize it first:
string[] matchingActiveLogFiles = new string[0];
The issue is your naming. You're defining matchingActiveLogFiles but assigning matchingFiles.
You should declare the variable in the scope in which that variable is needed.
If you only need the variable in the try block, put it in there!
If you need it outside of the try block, what do you want the value to be if your code can't get the file contents? Set it to that on error.
Though I generally dislike methods with out params, this seems like a good candidate for a Try method:
bool TryGetMatchingLogFiles(out string[] matchingFiles )
{
matchingFiles = null;
try
{
matchingFiles = Directory.GetFiles(FilePath, FileNamePattern);
return true;
}
catch (Exception ex)
{
//logerror
return false;
}
}
Usage:
string[] matchingActiveLogFiles;
if (TryGetMatchingLogFiles(out matchingActiveLogFiles))
{
// Use matchingActiveLogFiles here
}
Or alternatively, just initialise your variable to null:
string[] matchingActiveLogFiles = null;
try ...
you do not have to know the exact number of Items in order to initialize an array like
string[] matchingActiveLogFiles = {};
this is what is called a dynamic array it's totally functional and I've had ZERO issues with declaring arrays this way..
List<string> matchingFiles= new List<string>();
try
{
matchingFiles.Add(Directory.GetFiles(FilePath, FileNamePattern));
matchingActiveLogFiles = matchingFiles.ToArray();
}
catch (Exception ex)
{
//logerror
}
Related
I have a variable in one loop in C# that cannot be recognized in the other one, and I am aware that it is not possible to create a true global variable in C#, however I wonder if one can mimic one. Some of my code is this:
foreach (string line in lines)
{
if (line.Contains("write"))
{
var tempctr = line.Replace("(", "");
var tempctr2 = line.Replace(")", "");
var ctr = tempctr2.Remove(0, 6);
Console.Write(ctr);
}
else if (line.Contains("sayinput"))
{
Console.Write(usrinput);
}
else if (line.Contains("inputget"))
{
var tempctr = line.Replace("(", "");
var tempctr2 = line.Replace(")", "");
var ctr = tempctr2.Remove(0, 9);
Console.Write(ctr);
string usrinput = Console.ReadLine();
}
}
The code reads from a text file and runs a certain command based on what is in the text. My intention is for it to create a variable with inputget and spit it back out with sayinput.
And the first usrinput reference is an error, since the variable is declared outside of the loop.
You don't need a global variable here. Just declare usrinput outside your loop, like so:
string usrinput = "";
foreach (string line in lines)
{
if (line.Contains("write"))
{
//...
}
else if (line.Contains("sayinput"))
{
Console.Write(usrinput);
}
else if (line.Contains("inputget"))
{
// ...
usrinput = Console.ReadLine();
}
}
it is not possible to create a true global variable in C#,
Static variable on a class. Done. Global in the definition of any global variable (i.e. you must be in the same process). And standard C#.
How can I ban a variable from a list without removing it from that list by adding the variable to a list of "banned" variable?
I wish to be able to type in a string. That string is compared to the file names in a folder. If there is a match, the file is read. If I type this same string again, the file should not be read again. There for I want to have a list of "banned" string that is checked whilst typing to avoid the file to be read again.
I have tried a few ways but not getting there. Below is an example of my last attempt.
What would be the best way?
public class test
{
string scl= "test3";
List <string> lsf,lso;
void Start ()
{
lsf=//file names
new List<string>();
lso=//files open
new List<string>();
lsf.Add("test0");
lsf.Add("test1");
lsf.Add("test2");
lsf.Add("test3");
lsf.Add("test4");
lso.Add("idhtk49fngo");//random string
}
void Update ()
{
if
(
Input.GetKeyDown("a")
)
{
for
(
int i=0;
i<lsf.Count;
i++
)
{
if(lsf[i]==scl)
{
Debug.Log
(i+" is read");
for
(
int j=0;
j<lso.Count;
j++
)
{
//how can i avoid reading
//lsf[3] here the second time
//"a" is pressed (by having "test3"
//added to a "ban" list (lso) )
if(scl!=lso[j])
{
lso.Add(lsf[i]);
}
}
}
}
}
}
Michael’s answer is the way to go here but it can be improved using the more appropriate collection available to keep track of opened files; if you want uniqueness use a set, not a list:
HashSet<string> openedFiles = new HashSet<string>();
public static bool TryFirstRead(
string path,
out string result)
{
if (openedFiles.Add(path))
{
result = File.ReadAllText(path);
return true;
}
result = null;
return false;
}
Also, I’d avoid throwing vexing exceptions. Give the consumer a friendly way to know if the file was read or not, don’t make them end up having to use exceptions as a flow control mechanism.
I didn't understand although if you want to replace a value from another list.
You can use the list index to create a new list with the values which you removed.
String list1 = {"hi", "hello", "World"};
String list2 = {"bye", "goodbye", "World"};
List1[1] = list2[1];
I would suggest such way:
public static List<string> openedFiles = new List<string>();
public static string ReadFileAndAddToOpenedList(string path)
{
if (openedFiles.Contains(path))
throw new Exception("File already opened");
// Instead of throwing exception you could for example just log this or do something else, like:
// Consolle.WriteLine("File already opened");
else
{
openedFiles.Add(path);
return File.ReadAllText(path);
}
}
The idea is - on every file read, add file to list, so you can check every time you try read file, if it was already read (or opened). If it is, throw exception (or do something else). Else read a file.
You could instead of making it a string list use your own class
public class MyFile
{
public string Name;
public bool isOpen;
public MyFile(string name)
{
Name = name;
isOpen = false;
}
}
List<MyFile> lsf = new List<MyFile>()
{
new MyFile("test0"),
new MyFile("test1"),
new MyFile("test2"),
new MyFile("test3"),
new MyFile("test4")
};
Than when you read the file set isOpen to true
MyFile[someIndex].isOpen = true;
and later you can check this
// E.g. skip in a loop
if(MyFile[someIndex]) continue;
You could than also use Linq in order to get a list of only unread files:
var unreadFiles = lsf.Select(f => f.Name).Where(file => !file.isOpen);
so i am trying to read text from a txt file and then add the text in to a custom class list,
the code is
public static List<BookInfo> LoadCSVFile(string fileName, out string qError)
{
qError = "";
fileName = "books.txt";
List<BookInfo> Book_Info = new List<BookInfo>();
StreamReader read = null;
try
{
read = new StreamReader(fileName);
while (!read.EndOfStream)
{
string line = read.ReadLine();
string[] values = line.Split(',');
if (values.Length == 3)
{
string Title = values[0].Trim();
string Author = values[1].Trim();
string ISBN = values[2].Trim();
try
{
Book_Info.Add(new BookInfo(Title, Author, ISBN));
}
catch (Exception ex)
{
qError = ex.Message;
return null;
}
}
else
{
qError = $"line {line} was unable to be read";
return null;
}
}
}
catch
{
qError = $"failed to open file: {fileName}";
return null;
}
finally
{
if (read != null)
{
read.Close();
}
}
if (qError == "")
{
return Book_Info;
}
return null;
}
once i have read the text it will be displayed in a form which i believe to be coded correctly
i have placed an error message in to show when the file has been read and each time i try something new the same error appears.
have i gone wrong somewhere when reading the txt file?
Edit:
Text file was created using visual studio and is in the same solution, text file is in bin/debug
I totally agree with thegeneral's answer, but to answer your initial question I suspect your books.txt file was not located in your Bin/Debug folder. I did test your code ;-P
Some notes
if you are going to use something that Implements IDisposable, it's always good practice to use the using statement
If this is only a small file, why bother with StreamReader when you can just use File.ReadAllLines
Linq is your friend, projection is a wonderful thing.
If you really want to parse a CSV file, I'd seriously consider a dedicated CSV parser library (like CsvHelper). It will save you many headaches
This is not really the bastion of perfect coding, however I tried to work with what you had and the spirit of what you were trying to do.
Some code:
public static List<BookInfo> LoadCSVFile(string fileName, out string qError)
{
try
{
// read all lines in to another type
// just makes it easier for errors which you seem to want
var lines = File.ReadAllLines(fileName)
.Select(x => new { Values = x.Split(','), Text = x })
.ToList();
// get a list of errors,
var errors = lines.Where(x => x.Values.Length != 3)
.Select((s, i) => $"Bad book! Line {i} : {s.Text}");
// return some errors
qError = string.Join(Environment.NewLine, errors);
// project lines to your books
return lines.Where(x => x.Values.Length == 3)
.Select(x => new BookInfo(x.Values[0], x.Values[0], x.Values[0]))
.ToList();
}
catch (Exception e)
{
qError = e.Message;
}
return null;
}
Disclaimer
I wouldn't usually catch errors like this, it's rather nasty
The whole anonymous type and returning errors is a bit smelly, if the file is corrupt, I'd just throw a big nasty exception and be done with it. Make the user do the right thing
This is going to fail the second you have a title with a comma in it
This is completely untested!
If I'm reading a string from a config file, I'd use a similar approach to the below, in case the string isn't present in the file being read and an exception results. However, if I want to do the same for a string[] array, I can't just 'new it up' outside the try block because the size is not known.
I can't new it up in the try block itself. How should it be approached?
string[] logContent; // can't new it up here as don't know the size
try
{
logContent = File.ReadAllLines(aLogFile);
}
catch
{
throw new Exception("LoggerStandard: Specified Logfile exists but could not be read.");
}
You could initialize it to a default value:
string[] logContent = null;
try
{
logContent = File.ReadAllLines(aLogFile);
}
catch
{
// Be careful with the error message here => there might be other reasons
// that the ReadAllLines threw an exception
throw new Exception("LoggerStandard: Specified Logfile exists but could not be read.");
}
You can initialize it with null and then check against it.
By default it is null. You can leave it as is, if this is appropriate for your program, or initialize to any array according to your needs. Anyway, successful initialization inside of try block overrides this.
string[] logContent=null;
try
{
logContent = File.ReadAllLines(aLogFile);
}
catch
{
throw new Exception("LoggerStandard: Specified Logfile exists but could not be read.");
}
I am trying to create a program to copy all the files from one directory to another. But I am running in a basic issue. It says indentifier expected when I try to compile on line 52.
public bool RecursiveCopy()
{
string origDir = #"D:\Documents and Settings\Dub\My Documents\HoN Updates\test";
string destDir = #"C:\Games\HoN";
bool status = false;
//get all the info about the original directory
var dirInfo = new DirectoryInfo(origDir);
//retrieve all the _fileNames in the original directory
var files = dirInfo.GetFiles(origDir);
//always use a try...catch to deal
//with any exceptions that may occur
try
{
//loop through all the file names and copy them
foreach (string file in Directory.GetFiles(origDir))
{
var origFile = new FileInfo(file);
var destFile = new FileInfo(file.Replace(origDir, destDir));
//copy the file, use the OverWrite overload to overwrite
//destination file if it exists
System.IO.File.Copy(origFile.FullName, destFile.FullName, true);
//TODO: If you dont want to remove the original
//_fileNames comment this line out
File.Delete(origFile.FullName);
status = true;
}
Console.WriteLine("All files in " + origDir + " copied successfully!");
}
catch (Exception ex)
{
status = false;
//handle any errors that may have occurred
Console.WriteLine(ex.Message);
}
return status;
}
public string origDir = #"D:\Documents and Settings\Dub\My Documents\HoN Updates\test"; // ERROR HERE
public string destDir = #"C:\Games\HoN"; // ERROR HERE
private static void RecursiveCopy(origDir, destDir)
{
Console.WriteLine("done");
Console.ReadLine();
}
You did not give type identifiers to your argument list here
static void RecursiveCopy(origDir, destDir)
should be
static void RecursiveCopy(string origDir, string destDir)
Your method RecursiveCopy has two parameters listed without their types. It should be this:
static void RecursiveCopy(string origDir, string destDir)
Here is your problem:
static void RecursiveCopy(origDir, destDir)
You don't specify the types for the parameters, perhaps you intended the following:
static void RecursiveCopy(string origDir, string destDir)
There are more issues however that I've noticed. It's possible you're still working on these, but from what you've posted:
You never call your RecursiveCopy method. Perhaps you meant to call it from Main() instead of declaring an overload with two parameters?
You declare two public fields origDir and destDir but then never use them. Instead you create two local variables in RecursiveCopy() and use these instead. Did you intend to create parameters or use the public fields instead?
Your copy is not actually true to its name of "recursive".
cYou are missing the parameter types in the RecursiveCopy method declaration. Just Change
static void RecursiveCopy(origDir, destDir)
to
static void RecursiveCopy(String origDir, String destDir)
and all is fine.