Hi I'm programing a console application that reads in the text of a .txt file and then calculates the Flesch Score (how easy a text is to read). It's already able to get started by the console by typing in the path and some parameters f.e. -f "filename" or -e for english. Now I want to read in multiple textfiles with the command -f "testfile*.txt. The * means that it doesn't matter what is written after f.e. enumeration. How can I achieve that with a console command? Correct me if I'm wrong with something I'm kind of new to programing :)
Here's how I am doing it:
string[] parameters = new string[] { "-f", "-e", "-g" };
Flesch_Reading_Ease.FleschScore.Language lang = FleschScore.Language.Undefined;
string filename = "";
foreach (string arg in args)
{
switch (arg.Trim().ToLower())
{
case "-e":
lang = Flesch_Reading_Ease.FleschScore.Language.English;
break;
case "-g":
lang = Flesch_Reading_Ease.FleschScore.Language.German;
break;
case "-f":
break;
default:
{
if (File.Exists(arg))
filename = arg;
}
break;
}
}
If you use the Directory.GetFiles method it will return a list of files that match the wildcard:
string[] files = Directory.GetFiles(folder, arg);
where folder is the name of the directory you're looking in. You'll either have to assume the current folder "." or split the path from the input arguments.
You can then loop over this list:
foreach (var file in files)
{
// Do your processing
}
Related
I have a .NET Console application that is consuming a SOAP API. It was written originally in .NET 5 on a Windows computer. It now targets .NET 6 and was published on a Red Hat Linux Server.
When I uploaded and ran the code on Red Hat, I ran into some minor issues related to Windows (EventLog, etc). Not a big deal. However, I am now getting two different outputs when it checks the directory for files.
Expected output and current output in Windows during Debug:
info: FarFetch.Controllers.FileHandlerController[0]
In directory: C:\Users\user\some\dir\test\IN\
info: FarFetch.Controllers.FileHandlerController[0]
Files found: 0
However, on Red Hat, I receive the following output:
Please provide a valid file type!
info: FarFetch.Controllers.FileHandlerController[0]
In directory: /usr/some/dir/path/T_IN/
info: FarFetch.Controllers.FileHandlerController[0]
Files found: 1
As you can see on the very first line of the output above, it is outputting the default of the switch in the foreach loop before it gets the service on the lines before the loop.
It then goes on and executes the foreach loop again after GetRequiredService has returned.
public static async Task Main(string[] args)
{
var services = new ServiceCollection();
ConfigureServices(services);
//create service provider
var serviceProvider = services.BuildServiceProvider();
var fileHandler = serviceProvider.GetRequiredService<IFileHandler>();
var files = fileHandler.GetAllFiles();
foreach (var file in files)
{
switch (fileHandler.GetFileType(file))
{
case "CS":
await RetrieveStockInfo(serviceProvider, fileHandler, file);
break;
case "GOBD":
await RetrieveOrdersByDate(serviceProvider, fileHandler, file);
break;
case "GOH":
await RetrieveOrderHeaders(serviceProvider, fileHandler, file);
break;
case "GOR":
await RetrieveOrderRows(serviceProvider, fileHandler, file);
break;
case "PKG":
await DecidePackaging(serviceProvider, fileHandler, file);
break;
case "RBS":
await RecommendedBoxSize(serviceProvider, fileHandler, file);
break;
default:
Console.WriteLine("Please provide a valid file type!");
break;
}
}
}
Here is the GetAllFiles implementation:
public IEnumerable<string> GetAllFiles()
{
if (Directory.Exists(_filePaths.Value.In))
{
_files = ProcessDirectory(_filePaths.Value.In);
}
_logger.LogInformation($"Files found: {_files.Count()}");
return _files;
}
Process directory:
private IEnumerable<string> ProcessDirectory(string targetDirectory)
{
return Directory.EnumerateFiles(targetDirectory, "*.RDY");
}
And the Get File Type:
public string GetFileType(string filepath)
{
string[] fileParts = filepath.Split('\\');
string fileName = fileParts[fileParts.Length - 1];
return fileName.Split('_')[0];
}
Any help would be appreciated. Seems like it skips ahead to the foreach loop when it's getting the FileHandler service. Then returns to normal execution after it gets the required service.
Why does it do this on Linux, but not on Windows?
Thanks in advance!
Without more context, the first suspect is this line:
string[] fileParts = filepath.Split('\\');
That says to split the path using \ character. That's the directory sparator on Windows, but doesn't do anything on Linux (the file separator is / on Linux). You probably want Path.DirectorySeparatorChar here:
string[] fileParts = filepath.Split(Path.DirectorySeparatorChar);
Or, perhaps just use the built in functions to get the file name directly:
string fileName = Path.GetFileName(filePath);
I currently have this method that can successfully quote-enclose a single CSV file but I am trying to loop through 600+ CSV files in a directory and perform the Quote Enclose method on each one. I am unsure how to do this effectively. Any feedback is appreciated.
Below is my code:
public void QuoteEnclosingCSV()
{
string fileNamePath = Path.GetTempPath() + #"\Reports\*.csv";
var stringBuilder = new StringBuilder();
foreach (var line in File.ReadAllLines(fileNamePath))
{
stringBuilder.AppendLine(string.Format("\"{0}\"", string.Join("\",\"", line.Split(','))));
}
File.WriteAllText(string.Format(fileNamePath, Path.GetDirectoryName(fileNamePath)), stringBuilder.ToString());
}
string marFolder = Path.GetTempPath() + #"\Reports\";
var dir = new DirectoryInfo(marFolder);
foreach (var file in dir.EnumerateFiles("*.csv"))
{
QuoteEnclosingCSV();
}
Below is the error I'm receiving:
Illegal characters in path.
My first step in unraveling this conundrum would be to guess what the error message is trying to tell me. My first guess would be that it's trying to say that the path has illegal characters in it. Did you stop to check what characters were in the path that you get the error on?
I'll show you:
C:\Users\YoungStamos\AppData\Local\Temp\\Reports\*.csv
That's the path you pass to File.ReadAllLines(). The single argument to that method is a path to one single file. You can't have an asterisk (*) in a filename in Windows, because it's a wildcard.
What you seem to be trying to do is pass a parameter to QuoteEnclosingCSV(). In this loop, you carefully list each file, but you never tell QuoteEnclosingCSV() about any of them.
foreach (var file in dir.EnumerateFiles("*.csv"))
{
QuoteEnclosingCSV();
}
This is more like what you want:
public void QuoteEnclosingCSV(string fileNamePath)
{
var stringBuilder = new StringBuilder();
foreach (var line in File.ReadAllLines(fileNamePath))
{
stringBuilder.AppendLine(string.Format("\"{0}\"", string.Join("\",\"", line.Split(','))));
}
// I don't know what string.Format() is meant to do here; I'm guessing your guess is
// as good as mine, so I'm eliminating it.
//File.WriteAllText(string.Format(fileNamePath, Path.GetDirectoryName(fileNamePath)), stringBuilder.ToString());
File.WriteAllText(fileNamePath, stringBuilder.ToString());
}
And then call it like this:
string marFolder = Path.Combine(Path.GetTempPath(), "Reports");
var dir = new DirectoryInfo(marFolder);
foreach (var fileInfo in dir.EnumerateFiles("*.csv"))
{
QuoteEnclosingCSV( fileInfo.FullName );
}
This question already has answers here:
File count from a folder
(12 answers)
how to display the statistics about all the files in a folder in C# [closed]
(1 answer)
Closed 9 years ago.
I'm writing an console application to show folder statistics on C:\windows, and show the total files in there is there anyways it can be simplified and link the file type with the user!. This is what I've got so far:
{
String extention = String.Empty;
// Prompt the user to enter extention type
Console.Write("Please enter extention type: ");
extention = Console.ReadLine();
// This gets the Folder location which in this case is C:\\windows
DirectoryInfo root = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Windows));
// This is basicly the bit that collects the data after the user has entered the extention type
FileInfo[] executables = root.GetFiles("*exe");
foreach (var exe in executables)
{
//This will show the word txt in the console window
Console.WriteLine(exe.Name);
}
}
}
}
{
String extention2 = String.Empty;
// Prompt the user to enter extention type
extention2 = Console.ReadLine();
// This gets the Folder location which in this case is C:\\windows
DirectoryInfo root2 = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Windows));
FileInfo[] text = root2.GetFiles("*.txt");
foreach (var txt in text)
{
//This will show the word txt in the console window
Console.WriteLine(txt.Name);
}
}
String extention4 = String.Empty;
// Prompt the user to enter extention type
extention4 = Console.ReadLine();
// This gets the Folder location which in this case is C:\\windows
DirectoryInfo root4 = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Windows));
FileInfo[] windows = root4.GetFiles("*.win");
foreach (var win in windows)
{
//This will show the word txt in the console window
Console.WriteLine(win.Name);
}
Try using: new DirectoryInfo("C:\\Windows")
If you want to get a list of the files in that directory then call:
EnumerateFiles("*",SearchOption.AllDirectories)
on the DirectoryInfo object
Look at this:
"How to: Get Information About Files, Folders, and Drives (C# Programming Guide)"
http://msdn.microsoft.com/en-us/library/6yk7a1b0.aspx
For example:
// Get the files in the directory and print out some information about them.
System.IO.FileInfo[] fileNames = dirInfo.GetFiles("*.*");
foreach (System.IO.FileInfo fi in fileNames)
{
Console.WriteLine("{0}: {1}: {2}", fi.Name, fi.LastAccessTime, fi.Length);
}
You can change the Console.WriteLine to the format that you want...
Update:
System.IO.DriveInfo di = new System.IO.DriveInfo(#"C:\");
// Get the root directory
System.IO.DirectoryInfo dirInfo = di.RootDirectory;
// And then you can do .GetFiles()
System.IO.FileInfo[] fileNames = dirInfo.GetFiles("*.*");
This program is supposed to show the path of a directory and the directory if its exists then it should also show the files inside with the following extensions (i.e .doc, .pdf, .jpg, .jpeg) but I'm getting an error
*Index was outside the bounds of the array.
on this line of code
string directoryPath = args[0];
This is the code in the main function
class Program
{
static void Main(string[] args)
{
string directoryPath = args[0];
string[] filesList, filesListTmp;
IFileOperation[] opList = { new FileProcNameAfter10(),
new FileProcEnc(),
new FileProcByExt("jpeg"),
new FileProcByExt("jpg"),
new FileProcByExt("doc"),
new FileProcByExt("pdf"),
new FileProcByExt("djvu")
};
if (Directory.Exists(directoryPath))
{
filesList = Directory.GetFiles(directoryPath);
while (true)
{
Thread.Sleep(500);
filesListTmp = Directory.GetFiles(directoryPath);
foreach (var elem in Enumerable.Except<string>(filesListTmp, filesList))
{
Console.WriteLine(elem);
foreach (var op in opList)
{
if (op.Accept(elem)) op.Process(elem);
}
}
filesList = filesListTmp;
if (Console.KeyAvailable == true && Console.ReadKey(true).Key == ConsoleKey.Escape) break;
}
}
else
{
Console.WriteLine("There is no such directory.");
}
}
}
How can I handle this error it seems to be common but it happens id different ways
You need to pass the necessary arguments to the program when running it. You can either do this by running the program from the command line, or else when running Visual Studio by doing the following:
Right click on project
Properties
Debug tag
Enter arguments under Start Options -> Command line arguments
You might want to pass the arguments into the program from command line.
like this:
> yourProgram.exe directoryName
Also, to avoid such problems in the code,
if(args.Length > 0){
string directoryPath = args[0];
}else{
//print a help message and exit, or do something like set the
//default directoryPath to current directory
}
Do you want the user to enter a path when the program starts or when they start the program? If it's the first, then you should add a Console.Read() method that asks for the path.
If it's the latter, then you need to pass the path as an argument when starting the program. You should also do a check against the args array before reading from it to check that it contains data and that data is a valid path.
Something like:
if(args.Length > 0 && Directory.Exists(args[0]))
{
// Do Something.
}
Right what im trying to accomplish is a program that basically sets the active partition in 1 click, saving the effort time and skill of using cmd prompt etc.
I have looked into the System.Management name space but couldn't work out how to use it :(
So i have resorted to using CMD, i have got a module application written in C# and basically i want to run "DISKPART" which then starts the diskpart in the cmd window, then i want to ask it to "Select disk 0" followed by "select partition 1" finally followed by "active".
Doing this in CMD yourself works fine but with an application its proved to be awkward :( What ive managed to get it to do is run DiskPart fine in one window with Process.Start, then get it to open a new window and run the next piece of code but because the new window hasnt ran the diskpart cmd it doesnt work >:(
Any suggestions?
Thanks!
Ash
As long as you aren't making decisions on the output, you could build a batch file in your C# app and start that via Process.Start(...).
You'll need to generate two files.
First runDiskPart.bat:
diskpart /s myScript.dp
Second myScript.dp:
...some commands...
exit
Obviously the names are completely arbitrary but the /s directive needs to reference the name of your second file.
After some searching, I think you can do what you want with a script file. Read this.
You can therefore run diskpart /s script.txt with Process.Start after creating a script.txt file with your necessary commands.
This may be a bit of a read so im sorry in advance. And this is my tried and tested way of doing this, there may be a simpler way but this is from me throwing code at a wall and seeing what stuck
TLDR code for this question in particular
Ok sorry This one is actually not tested. this one IN THEORY works
public static void ChangeMe()
{
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string path1 = docPath + "\\Test.txt";
string path2 = docPath + "\\Test.bat";
string[] lines =
{
"select disk 0",
"clean",
"convert gpt",
"create partition primary size=300",
"format quick fs=ntfs label=Windows RE tools",
"assign letter=T"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.txt")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
string[] lines =
{
"diskpart /s test.txt"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path2);
}
If what you want to do be can be done in batch file, then the maybe over complicated work around is have c# write a .bat file and run it. If you want user input you could place the input into a variable and have c# write it into the file. it will take trial and error with this way because its like controlling a puppet with another puppet. And with Diskpart its a little more complicated because you have to make 2 files one that is a .bat and one that is a txt.
here is an example for just a batch file, In this case the function is for a push button in windows forum app that clears the print queue.
using System.IO;
using System;
public static void ClearPrintQueue()
{
//this is the path the document or in our case batch file will be placed
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
//this is the path process.start usues
string path1 = docPath + "\\Test.bat";
// these are the batch commands
// remember its "", the comma separates the lines
string[] lines =
{
"#echo off",
"net stop spooler",
"del %systemroot%\\System32\\spool\\Printers\\* /Q",
"net start spooler",
//this deletes the file
"del \"%~f0\"" //do not put a comma on the last line
};
//this writes the string to the file
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
//This writes the file line by line
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path1);
}
IF you want user input then you could try something like this.
This is for setting the computer IP as static but asking the user what the IP, gateway, and dns server is.
you will need this for it to work
public static void SetIPStatic()
{
//These open pop up boxes which ask for user input
string STATIC = Microsoft.VisualBasic.Interaction.InputBox("Whats the static IP?", "", "", 100, 100);
string SUBNET = Microsoft.VisualBasic.Interaction.InputBox("Whats the Subnet?(Press enter for default)", "255.255.255.0", "", 100, 100);
string DEFAULTGATEWAY = Microsoft.VisualBasic.Interaction.InputBox("Whats the Default gateway?", "", "", 100, 100);
string DNS = Microsoft.VisualBasic.Interaction.InputBox("Whats the DNS server IP?(Input required, 8.8.4.4 has already been set as secondary)", "", "", 100, 100);
//this is the path the document or in our case batch file will be placed
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
//this is the path process.start usues
string path1 = docPath + "\\Test.bat";
// these are the batch commands
// remember its "", the comma separates the lines
string[] lines =
{
"SETLOCAL EnableDelayedExpansion",
"SET adapterName=",
"FOR /F \"tokens=* delims=:\" %%a IN ('IPCONFIG ^| FIND /I \"ETHERNET ADAPTER\"') DO (",
"SET adapterName=%%a",
"REM Removes \"Ethernet adapter\" from the front of the adapter name",
"SET adapterName=!adapterName:~17!",
"REM Removes the colon from the end of the adapter name",
"SET adapterName=!adapterName:~0,-1!",
//the variables that were set before are used here
"netsh interface ipv4 set address name=\"!adapterName!\" static " + STATIC + " " + STATIC + " " + DEFAULTGATEWAY,
"netsh interface ipv4 set dns name=\"!adapterName!\" static " + DNS + " primary",
"netsh interface ipv4 add dns name=\"!adapterName!\" 8.8.4.4 index=2",
")",
"ipconfig /flushdns",
"ipconfig /registerdns",
":EOF",
"DEL \"%~f0\"",
""
};
//this writes the string to the file
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
//This writes the file line by line
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path1);
}
Like I said. It may be a little overcomplicated but it never fails unless I write the batch commands wrong.
This is the code for diskpart. You have to understand the command prompt in order to get these to work. With diskpart you cannot just write a script like
diskpart
select disk 0
clean
convert gpt
create partition primary size=300
format quick fs=ntfs label=Windows RE tools
assign letter=T
This is because diskpart opens its own window and the rest of the commands just throw errors in the command prompt window
so you have to get c# to first write a text file with the commands. Then a batch file with the diskpart command to call the text file that you just wrote.
As I said at first This one is actually not tested. this one IN THEORY works
public static void ChangeMe()
{
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string path1 = docPath + "\\Test.txt";
string path2 = docPath + "\\Test.bat";
string[] lines =
{
"select disk 0",
"clean",
"convert gpt",
"create partition primary size=300",
"format quick fs=ntfs label=Windows RE tools",
"assign letter=T"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.txt")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
string[] lines =
{
"diskpart /s test.txt"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path2);
}
What about introducing a delay, such as Thread.Sleep(1000), so that the other process has time to complete the first command?
What you really want to do is wait for the program to exit and then move onto the next invocation. Take a look at this question.