How to get a substring from the specific string in c# - c#

I have
string filepath = #"F:\first_folder\Node3_V_1.3";
I have a button named version_check
On the click of this button i want to print the message:
upgrading node3 to version 1.3
The details are fetched from string filepath. How do I code this in C#.

You can do in this way
version_check.Click += version_check_Click; //subscription of the event
public void version_check_Click(object sender, EventArgs e)
{
string filepath = #"F:\first_folder\Node3_V_1.3";
var name = Path.GetFileName(filepath).Split(new[] {'_', 'V'}, StringSplitOptions.RemoveEmptyEntries);
if (name.Length < 1)
{
MessageBox.Show("Failed to update");
return;
}
MessageBox.Show(string.Format("Upgrading {0} to version {1} ...", name[0], name[1]));
}
Debug Information:

This is really simple using the already available classes in the NET Framework,
The Path class has a method GetFileName that takes the last part of a pathname also if it is not a real filename but just a folder....
string filePath = #"F:\first_folder\Node3_V_1.3";
string lastFolder = Path.GetFileName(filePath);
Console.WriteLine(lastFolder);
Now, if your lastFolder is regularly composed of three parts (IE, software, V per version and finally the version number, you could use the Split method of the String class to divide you lastFolder in three parts
string[] parts = lastFolder.Split('_');
Console.WriteLine("Upgrading {0} to version {1}", parts[0], parts[2]);
If you work with C# 6.0 you could also write the last statement using string interpolation with
Console.WriteLine($"Upgrading {parts[0]} to version {parts[2]}");

This will work for the sample folder path you gave,
private void version_check_Click(object sender, EventArgs e)
{
string version = "F:\\first_folder\\Node3_V_1.3";
var result = version.Substring(version.LastIndexOf('\\') + 1);
string[] splitString = result.Split('_').ToArray();
MessageBox.Show("upgrading "+ splitString[0] + " to version" + splitString[2]);
}

Regex solution:
string filepath = #"F:\first_folder\Node3_V_1.3";
string filename = Path.GetFileName(filepath);
Match m = Regex.Match(filename, "^(?<name>.+)_V_(?<version>.+)$");
string output = string.Format("upgrading {0} to version {1}",
m.Groups["name"].Value,
m.Groups["version"].Value);
String.Split solution:
string filepath = #"F:\first_folder\Node3_V_1.3";
string filename = Path.GetFileName(filepath);
string[] parts = filename.Split(new[] { "_V_" }, 2, StringSplitOptions.None);
string output = string.Format("upgrading {0} to version {1}",
parts[0],
parts[1]);

Related

Dealing with multiple '.' in a file extension

I have this string that contains a filename
string filename = "C:\\Users\\me\\Desktop\\filename.This.Is.An.Extension"
I tried using the conventional
string modifiedFileName = System.IO.Path.GetFileNameWithoutExtension(filename);
but it only gets me:
modifiedFileName = "C:\\Users\\me\\Desktop\\filename.This.Is.An"
In order for me to get "C:\\Users\\me\\Desktop\\filename" I would have to use System.IO.Path.GetFileNameWithoutExtension several times, and that's just not efficient.
What better way is there to take my file name and have it return the directory + filename and no exceptions?
Many thanks in advance!
If you want to stop at the first period, you will have to handle it yourself.
Path.GetDirectoryName(filepath) + Path.GetFileName(filepath).UpTo(".")
using this string extension:
public static string UpTo(this string s, string stopper) => s.Substring(0, Math.Max(0, s.IndexOf(stopper)));
Take the directory and the base name:
var directoryPath = Path.GetDirectoryName(filename);
var baseName = Path.GetFileName(filename);
Strip the base name’s “extensions”:
var baseNameWithoutExtensions = baseName.Split(new[] {'.'}, 2)[0];
Recombine them:
var modifiedFileName = Path.Combine(directoryPath, baseNameWithoutExtensions);
demo
Without built in function:
public static void Main(string[] args)
{
string s = "C:\\Users\\me\\Desktop\\filename.This.Is.An.Extension";
string newString="";
for(int i=0;i<s.Length;i++)
{
if(s[i]=='.'){
break;
}else{
newString += s[i].ToString();
}
}
Console.WriteLine(newString); //writes "C:\Users\me\Desktop\filename"
}

Edit a string of 685 characters and replace substring with another string in C#

Browse for a folder containing specific files using a folder dialog...
private void btnBrowse_Click(object sender, EventArgs e)
{
DialogResult result = fbdPath.ShowDialog();
if (result == DialogResult.OK)
{
string[] files = Directory.GetFiles(fbdPath.SelectedPath);
txtPath.Text = fbdPath.SelectedPath;
MessageBox.Show(txtPath.Text.ToString());
}
}
Update the file ...
try
{
String path = txtPath.Text;
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] files = dir.GetFiles();
foreach( FileInfo file in files)
{
StreamReader sReader;
sReader = File.OpenText(dir + #"\" + file.Name );
StreamWriter sWriter = new StreamWriter(#"f:\" + file.Name);
while (sReader.EndOfStream == false)
{
string contents = sReader.ReadLine();
String Flag = contents.Substring(655, 29);
String emBossFName = contents.Substring(41, 40);
String emBossLName = contents.Substring(121, 40);
String emBossFullName = emBossFName.Trim() + " " + emBossLName.Trim();
String newString = emBossFullName;
sWriter.WriteLine(contents.Replace(Flag, newString));
}
sWriter.Close();
sReader.Close();
}
}
catch (IOException ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
I need to replace the 655th position of a string which is of 29 Characters(combination of first & last name of a person seperated by a whitespace), but the problem is the how to maintain fix length of a string which is of 685 characters long?
Just take your final string and call Remove
"...".Remove(684);
And, if you are concerned with it not being long enough, you could first use PadRight
I think that you should make newString exactlly 29 characters long:
if (newString.Length > 29) newString = newString.Substring(0, 29);
if (newString.Length < 29) newString = newString.PadRight(29);
This code trims string if it is longer than 29 charaters or pads right side with spaces if string is shorter than 29 characters.
You can use StringBuilder class.
Ex: replace defg with 1234
StringBuilder sb = new StringBuilder( "abcdefghi");
var newstr = sb.Remove(3, 4).Insert(3, "1234").ToString();

File.Move() rename not working

My app takes "unclean" file names and "cleans" them up. "Unclean" file names contain characters like #, #, ~, +, %, etc. The "cleaning" process replaces those chars with "". However, I found that if there are two files in the same folder that, after a cleaning, will have the same name, my app does not rename either file. (I.e. ##test.txt and ~test.txt will both be named test.txt after the cleaning).
Therefore, I put in a loop that basically checks to see if the file name my app is trying to rename already exists in the folder. However, I tried running this and it would not rename all the files. Am I doing something wrong?
Here's my code:
public void FileCleanup(List<string> paths)
{
string regPattern = (#"[~#&!%+{}]+");
string replacement = "";
Regex regExPattern = new Regex(regPattern);
List<string> existingNames = new List<string>();
StreamWriter errors = new StreamWriter(#"C:\Documents and Settings\joe.schmoe\Desktop\SharePointTesting\Errors.txt");
StreamWriter resultsofRename = new StreamWriter(#"C:\Documents and Settings\joe.schmoe\Desktop\SharePointTesting\Results of File Rename.txt");
var filesCount = new Dictionary<string, int>();
string replaceSpecialCharsWith = "_";
foreach (string files2 in paths)
try
{
string filenameOnly = Path.GetFileName(files2);
string pathOnly = Path.GetDirectoryName(files2);
string sanitizedFileName = regExPattern.Replace(filenameOnly, replacement);
string sanitized = Path.Combine(pathOnly, sanitizedFileName);
if (!System.IO.File.Exists(sanitized))
{
System.IO.File.Move(files2, sanitized);
resultsofRename.Write("Path: " + pathOnly + " / " + "Old File Name: " + filenameOnly + "New File Name: " + sanitized + "\r\n" + "\r\n");
}
else
{
existingNames.Add(sanitized);
foreach (string names in existingNames)
{
string sanitizedPath = regExPattern.Replace(names, replaceSpecialCharsWith);
if (filesCount.ContainsKey(sanitizedPath))
{
filesCount[names]++;
}
else
{
filesCount.Add(sanitizedPath, 1);
}
string newFileName = String.Format("{0},{1}, {2}", Path.GetFileNameWithoutExtension(sanitizedPath),
filesCount[sanitizedPath] != 0
? filesCount[sanitizedPath].ToString()
: "",
Path.GetExtension(sanitizedPath));
string newFilePath = Path.Combine(Path.GetDirectoryName(sanitizedPath), newFileName);
System.IO.File.Move(names, newFileName);
}
}
}
catch (Exception e)
{
//write to streamwriter
}
}
}
Anybody have ANY idea why my code won't rename duplicate files uniquely?
You do foreach (string names in existingNames), but existingNames is empty.
You have your if (System.IO.File.Exists(sanitized)) backwards: it makes up a new name if the file doesn't exist, instead of when it exists.
You make a string newFileName, but still use sanitizedPath instead of newFileName to do the renaming.
The second parameter to filesCount.Add(sanitizedPath, 0) should be 1 or 2. After all, you have then encountered your second file with the same name.
If filesCount[sanitizedPath] equals 0, you don't change the filename at all, so you overwrite the existing file.
In addition to the problem pointed out by Sjoerd, it appears that you are checking to see if the file exists and if it does exist you move it. Your if statement should be
if (!System.IO.File.Exists(sanitized))
{
...
}
else
{
foreach (string names in existingNames)
{
...
}
}
}
Update:
I agree that you should split the code up into smaller methods. It will help you identify which pieces are working and which aren't. That being said, I would get rid of the existingNames list. It is not needed because you have the filesCount Dictionary. Your else clause would then look something like this:
if (filesCount.ContainsKey(sanitized))
{
filesCount[sanitized]++;
}
else
{
filesCount.Add(sanitized, 1);
}
string newFileName = String.Format("{0}{1}.{2}",
Path.GetFileNameWithoutExtension(sanitized),
filesCount[sanitized].ToString(),
Path.GetExtension(sanitized));
string newFilePath = Path.Combine(Path.GetDirectoryName(sanitized), newFileName);
System.IO.File.Move(files2, newFileName);
Please note that I changed your String.Format method call. You had some commas and spaces in there that looked incorrect for building a path, although I could be missing something in your implementation. Also, in the Move I changed the first argument from "names" to "files2".
A good way to make the code less messy would be to split it to methods as logical blocks.
FindUniqueName(string filePath, string fileName);
The method would prefix the fileName with a character, until the fileName is unique withing the filePath.
MoveFile(string filePath, string from, string to);
The method would use the FindUniqueName method if the file already exists.
It would be way easier to test the cleanup that way.
Also you should check if a file actually requires renaming:
if (String.Compare(sanitizedFileName, filenameOnly, true) != 0)
MoveFile(pathOnly, fileNameOnly, sanitizedFileName);
private string FindUniqueName(string fileDirectory, string from, string to)
{
string fileName = to;
// There most likely won't be that many files with the same name to reach max filename length.
while (File.Exists(Path.Combine(fileDirectory, fileName)))
{
fileName = "_" + fileName;
}
return fileName;
}
private void MoveFile(string fileDirectory, string from, string to)
{
to = FindUniqueName(fileDirectory, from, to);
File.Move(Path.Combine(fileDirectory, from), Path.Combine(fileDirectory, to));
}

Problem with Existing File Name & Creating a Unique File Name

I have this code:
public void FileCleanup(List<string> paths)
{
string regPattern = (#"[~#&!%+{}]+");
string replacement = "";
string replacement_unique = "_";
Regex regExPattern = new Regex(regPattern);
List<string> existingNames = new List<string>();
StreamWriter errors = new StreamWriter(#"C:\Documents and Settings\jane.doe\Desktop\SharePointTesting\Errors.txt");
StreamWriter resultsofRename = new StreamWriter(#"C:\Documents and Settings\jane.doe\Desktop\SharePointTesting\Results of File Rename.txt");
foreach (string files2 in paths)
try
{
string filenameOnly = Path.GetFileName(files2);
string pathOnly = Path.GetDirectoryName(files2);
string sanitizedFileName = regExPattern.Replace(filenameOnly, replacement);
string sanitized = Path.Combine(pathOnly, sanitizedFileName);
if (!System.IO.File.Exists(sanitized))
{
existingNames.Add(sanitized);
try
{
foreach (string names in existingNames)
{
string filename = Path.GetFileName(names);
string filepath = Path.GetDirectoryName(names);
string cleanName = regExPattern.Replace(filename, replacement_unique);
string scrubbed = Path.Combine(filepath, cleanName);
System.IO.File.Move(names, scrubbed);
//resultsofRename.Write("Path: " + pathOnly + " / " + "Old File Name: " + filenameOnly + "New File Name: " + sanitized + "\r\n" + "\r\n");
resultsofRename = File.AppendText("Path: " + filepath + " / " + "Old File Name: " + filename + "New File Name: " + scrubbed + "\r\n" + "\r\n");
}
}
catch (Exception e)
{
errors.Write(e);
}
}
else
{
System.IO.File.Move(files2, sanitized);
resultsofRename.Write("Path: " + pathOnly + " / " + "Old File Name: " + filenameOnly + "New File Name: " + sanitized + "\r\n" + "\r\n");
}
}
catch (Exception e)
{
//write to streamwriter
}
}
}
}
What i'm trying to do here is rename "dirty" filenames by removing invalid chars (defined in the Regex), replace them with "". However, i noticed if i have duplicate file names, the app does not rename them. I.e. if i have ##test.txt and ~~test.txt in the same folder, they'd be renamed to test.txt. So, i created another foreach loop that instead replaces the invalid char with a "_" versus a blank space.
Problem is, whenever i try to run this, nothing ends up happening! None of the files are renamed!
Can someone tell me if my code is incorrect and how to fix it?
ALSO-- does anybody know how i could replace the invalid char in the 2nd foreach loop with a different char everytime? That way if there are multiple instances of i.e. %Test.txt, ~Test.txt and #test.txt (all to be renamed to test.txt), they can somehow be uniquely named with a different char?
However, would you know how to replace the invalid char with a different unique character every time so that each filename remains unique?
This is one way:
char[] uniques = ",'%".ToCharArray(); // whatever chars you want
foreach (string file in files)
{
foreach (char c in uniques)
{
string replaced = regexPattern.Replace(file, c.ToString());
if (File.Exists(replaced)) continue;
// create file
}
}
You may of course want to refactor this into its own method. Take note also that the maximum number of files only differing by unique character is limited to the number of characters in your uniques array, so if you have a lot of files with the same name only differing by the special characters you listed, it might be wise to use a different method, such as appending a digit to the end of the file name.
how would i append a digit to the end of the file name (with a different # everytime?)
A slightly modified version of Josh's suggestion would work that keeps track of the modified file names mapped to the number of times the same file name has been generated after the replacement:
var filesCount = new Dictionary<string, int>();
string replaceSpecialCharsWith = "_"; // or "", whatever
foreach (string file in files)
{
string sanitizedPath = regexPattern.Replace(file, replaceSpecialCharsWith);
if (filesCount.ContainsKey(sanitizedPath))
{
filesCount[file]++;
}
else
{
filesCount.Add(sanitizedPath, 0);
}
string newFileName = String.Format("{0}{1}{2}",
Path.GetFileNameWithoutExtension(sanitizedPath),
filesCount[sanitizedPath] != 0
? filesCount[sanitizedPath].ToString()
: "",
Path.GetExtension(sanitizedPath));
string newFilePath = Path.Combine(Path.GetDirectoryName(sanitizedPath),
newFileName);
// create file...
}
just a suggestion
after removing/replacing the special characters append timestamp to the file name. timestamps are unique so appending them to filenames will give you a unique filename.
How about maintaining a dictionary of all renamed files, checking each file against it, and if already existing add a number to the end of it?
In response to the answer #Josh Smeaton's gave here's some sample code using a dictionary to keep track of the file names :-
class Program
{
private static readonly Dictionary<string,int> _fileNames = new Dictionary<string, int>();
static void Main(string[] args)
{
var fileName = GetUniqueFileName("filename.txt");
Console.WriteLine(fileName);
fileName = GetUniqueFileName("someotherfilename.txt");
Console.WriteLine(fileName);
fileName = GetUniqueFileName("filename.txt");
Console.WriteLine(fileName);
fileName = GetUniqueFileName("adifferentfilename.txt");
Console.WriteLine(fileName);
fileName = GetUniqueFileName("filename.txt");
Console.WriteLine(fileName);
fileName = GetUniqueFileName("adifferentfilename.txt");
Console.WriteLine(fileName);
Console.ReadLine();
}
private static string GetUniqueFileName(string fileName)
{
// If not already in the dictionary add it otherwise increment the counter
if (!_fileNames.ContainsKey(fileName))
_fileNames.Add(fileName, 0);
else
_fileNames[fileName] += 1;
// Now return the new name using the counter if required (0 means it's just been added)
return _fileNames[fileName].ToString().Replace("0", string.Empty) + fileName;
}
}

RegEx -- getting rid of double whitespaces?

I have an app that goes in, replaces "invalid" chars (as defined by my Regex) with a blankspace. I want it so that if there are 2 or more blank spaces in the filename, to trim one. For example:
Deal A & B.txt after my app runs, would be renamed to Deal A   B.txt (3 spaces b/w A and B). What i want is really this: Deal A B.txt (one space between A and B).
I'm trying to determine how to do this--i suppose my app will have to run through all filenames at least once to replace invalid chars and then run through filenames again to get rid of extraneous whitespace.
Can anybody help me with this?
Here is my code currently for replacing the invalid chars:
public partial class CleanNames : Form
{
public CleanNames()
{
InitializeComponent();
}
public void Sanitizer(List<string> paths)
{
string regPattern = (#"[~#&$!%+{}]+");
string replacement = " ";
Regex regExPattern = new Regex(regPattern);
StreamWriter errors = new StreamWriter(#"S:\Testing\Errors.txt", true);
var filesCount = new Dictionary<string, int>();
dataGridView1.Rows.Clear();
try
{
foreach (string files2 in paths)
{
string filenameOnly = System.IO.Path.GetFileName(files2);
string pathOnly = System.IO.Path.GetDirectoryName(files2);
string sanitizedFileName = regExPattern.Replace(filenameOnly, replacement);
string sanitized = System.IO.Path.Combine(pathOnly, sanitizedFileName);
if (!System.IO.File.Exists(sanitized))
{
DataGridViewRow clean = new DataGridViewRow();
clean.CreateCells(dataGridView1);
clean.Cells[0].Value = pathOnly;
clean.Cells[1].Value = filenameOnly;
clean.Cells[2].Value = sanitizedFileName;
dataGridView1.Rows.Add(clean);
System.IO.File.Move(files2, sanitized);
}
else
{
if (filesCount.ContainsKey(sanitized))
{
filesCount[sanitized]++;
}
else
{
filesCount.Add(sanitized, 1);
}
string newFileName = String.Format("{0}{1}{2}",
System.IO.Path.GetFileNameWithoutExtension(sanitized),
filesCount[sanitized].ToString(),
System.IO.Path.GetExtension(sanitized));
string newFilePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(sanitized), newFileName);
System.IO.File.Move(files2, newFilePath);
sanitized = newFileName;
DataGridViewRow clean = new DataGridViewRow();
clean.CreateCells(dataGridView1);
clean.Cells[0].Value = pathOnly;
clean.Cells[1].Value = filenameOnly;
clean.Cells[2].Value = newFileName;
dataGridView1.Rows.Add(clean);
}
}
}
catch (Exception e)
{
errors.Write(e);
}
}
private void SanitizeFileNames_Load(object sender, EventArgs e)
{ }
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
The problem is, that not all files after a rename will have the same amount of blankspaces. As in, i could have Deal A&B.txt which after a rename would become Deal A B.txt (1 space b/w A and B--this is fine). But i will also have files that are like: Deal A & B & C.txt which after a rename is: Deal A   B   C.txt (3 spaces between A,B and C--not acceptable).
Does anybody have any ideas/code for how to accomplish this?
Do the local equivalent of:
s/\s+/ /g;
Just add a space to your regPattern. Any collection of invalid characters and spaces will be replaced with a single space. You may waste a little bit of time replacing a space with a space, but on the other hand you won't need a second string manipulation call.
Does this help?
var regex = new System.Text.RegularExpressions.Regex("\\s{2,}");
var result = regex.Replace("Some text with a lot of spaces, and 2\t\ttabs.", " ");
Console.WriteLine(result);
output is:
Some text with a lot of spaces, and 2 tabs.
It just replaces any sequence of 2 or more whitespace characters with a single space...
Edit:
To clarify, I would just perform this regex right after your existing one:
public void Sanitizer(List<string> paths)
{
string regPattern = (#"[~#&$!%+{}]+");
string replacement = " ";
Regex regExPattern = new Regex(regPattern);
Regex regExPattern2 = new Regex(#"\s{2,}");
and:
foreach (string files2 in paths)
{
string filenameOnly = System.IO.Path.GetFileName(files2);
string pathOnly = System.IO.Path.GetDirectoryName(files2);
string sanitizedFileName = regExPattern.Replace(filenameOnly, replacement);
sanitizedFileName = regExPattern2.Replace(sanitizedFileName, replacement); // clean up whitespace
string sanitized = System.IO.Path.Combine(pathOnly, sanitizedFileName);
I hope that makes more sense.
you can perform another regex replace after your first one
#" +" -> " "
As Fosco said, with formatting:
while (mystring.Contains(" ")) mystring = mystring.Replace(" "," ");
// || || |
After you're done sanitizing it your way, simply replace 2 spaces with 1 space, while 2 spaces exist in the string.
while (mystring.Contains(" ")) mystring = mystring.Replace(" "," ");
I think that's the right syntax...

Categories