I'd like to modify a source string which is looking like
"one.two.three"
and transfer it into a string with slashes to use it as a folder string which has the following structure:
"one\one.two\one.two.three"
Do you know more elegant ways to realize this, than my solution below? I'm not very satisfied with my for-loops.
var folder = "one.two.three";
var folderParts = folder.Split('.');
var newFolder = new StringBuilder();
for (int i = 0; i < folderParts.Length; i++)
{
for (int j = 0; j < i; j++)
{
if (j == 0)
{
newFolder.Append("\\");
}
newFolder.Append($"{folderParts[j]}.");
}
newFolder.Append(folderParts[i]);
}
You can do this quite tersely with Regex
var newFolder = Regex.Replace(folder, #"\.", #"\$`.");
This matches on each period. Each time it finds a period, it inserts a backslash and then the entire input string before the match ($`). We have to add the period in again at the end.
So, steps are (< and > indicate text inserted by the substitution at that step):
Match on the 1st period. one<\one>.two.three
Match on the 2nd period. one\one.two<\one.two>.three
Result: one\one.two\one.two.three
For bonus points, use Path.DirectorySeparatorChar for cross-platform correctness.
var newFolder = Regex.Replace(folder, #"\.", $"{Path.DirectorySeparatorChar}$`.")
Here's another linqy way:
var a = "";
var newFolder = Path.Combine(folder.Split('.')
.Select(x => a += (a == "" ? "" : ".") + x).ToArray());
You can try Linq:
string folder = "one.two.three";
string[] parts = folder.Split('.');
string result = Path.Combine(Enumerable
.Range(1, parts.Length)
.Select(i => string.Join(".", parts.Take(i)))
.ToArray());
Console.Write(newFolder);
Outcome:
one\one.two\one.two.three
You can go forward-only in one loop like this:
var folder = "one.two.three";
var newFolder = new StringBuilder();
int index = -1;
while (index + 1 < folder.Length) {
index = folder.IndexOf('.', index + 1);
if (index < 0) {
newFolder.Append(folder);
break;
}
else {
newFolder.Append(folder, 0, index);
newFolder.Append(Path.DirectorySeparatorChar);
}
}
You can try it out here.
Instead of splitting the string first, I find it more elegant to start with what you have and reduce it:
var folder = "one.two.three";
var newFolder = string.Empty;
for (var f = folder; f.Any(); f = f.Remove(Math.Max(f.LastIndexOf('.'), 0)))
newFolder = Path.Combine(f, newFolder);
Console.WriteLine(newFolder);
Output:
one\one.two\one.two.three
Related
I am developing in C#.
I have a text file containing the following:
Sam
NYC
Mii
Peru
LEO
Argentina
I want to iterate through this file two line by two line, then print to the console the first line, second line (the Name and the Country) of each couple, so the output would be:
Sam, NYC
Mii, Peru
Here is what I have tried:
int linenum = 0;
foreach (string line in File.ReadLines("c:\\file.txt"))
{
string word = line;
string s = "";
string j = "";
linenum = linenum + 1;
if(linenum % 2 != 0) //impaire
{
s = line;
}
else
{
j = line;
}
Console.WriteLine((string.Concat(s, j));
}
But that's not working, I want to do:
int linenum = 0;
foreach( two lines in File.ReadLines("c:\\file.txt"))
{
linenum = linenum + 1;
//get the first line (linenum = 1) and store it in a string s
// then get the second line (linenum = 2) and store it in a string j
// then print the two strings together to the console like that
Console.WriteLine((string.Concat("S: " + s,"J: " j));
}
How can I do that ?
Use File.ReadAllLines to return an array of strings:
var lines = File.ReadAllLines(filePath);
for (int i = 0; i < lines.Length; i+=2)
{
var s = lines[i];
var j = lines[i+1];
Console.WriteLine($"S: {s} J: {s}");
}
You do your output with Console.WriteLine in every line, but you also should do that only for every second line. Furthermore, your variables s and j live inside the loop's scope, so they are recreated with every iteration and loose their prior value.
int i = 0; string prev = "";
foreach (string line in File.ReadLines("c:\\file.txt")) {
if (i++ % 2 == 0) prev = line;
else Console.WriteLine($"{prev}, {line}");
}
Another approach would be iterating the array you get from File.ReadAllLines with an for loop instead of foreach and increase the index by 2
var lines = File.ReadAllLines("c:\\file.txt");
//make sure, you have an even number of lines!
if (lines.Length% 2 == 0) for (int i = 0; i < lines.Length; i+=2) {
Console.WriteLine($"{lines[i]}, {lines[i+1]}");
}
You can write yourself a little helper method to return batches of lines.
This implementation handles files that are not a multiple of the batch size (2 in your case) by returning "" for the missing lines at the end of the file.
public static IEnumerable<string[]> BatchedLinesFromFile(string filename, int batchSize)
{
string[] result = Enumerable.Repeat("", batchSize).ToArray();
int count = 0;
foreach (var line in File.ReadLines(filename))
{
result[count++] = line;
if (count != batchSize)
continue;
yield return result;
count = 0;
result = Enumerable.Repeat("", batchSize).ToArray();
}
if (count > 0)
yield return result;
}
Note that this also returns a separate array for each result, in case you make a copy of it.
Given that code, you can use it like so:
foreach (var batch in BatchedLinesFromFile(filename, 2))
{
Console.WriteLine(string.Join(", ", batch));
}
Actually, you can use LINQ to get two lines in a time using Take
var twoLines = File.ReadLines(#"YourPath").Take(2));
As you can use Skip to skip the two lines you took and take the next two lines like :
var twoLines = File.ReadLines(#"YourPath").Skip(2).Take(2));
EDIT : Thanks for #derpirscher there were a performance issue so changed the code to the following :
first read the whole file and store it in a string array
then loop through it using LINQ to take two elements from the array in a time.
string[] myStringArray = File.ReadAllLines(#"YourFile.txt");
for (int i = 0; i < myStringArray.Length ; i+=2)
{
var twoLines = myStringArray.Skip(i).Take(2).ToArray();
}
Another one, using Enumerable.Repeat() and an interger selector incremented a [NumberOfLines / 2] times.
Could be interesting for the LINQ addicted (a for / foreach solution is probably better anyway).
string[] input = File.ReadAllLines([SourcePath]);
int Selector = -1;
string[] output = Enumerable.Repeat(0, input.Length / 2).Select(_ => {
Selector += 2;
return $"{input[Selector - 1]} {input[Selector]}";
}).ToArray();
The output is:
Sam NYC
Mii Peru
LEO Argentina
Use the right tool for the job. foreach() is not the right tool here.
Without giving up the memory efficiency of ReadLines() over ReadAll():
using (var lines = File.ReadLines("c:\\file.txt").GetEnumerator())
{
while (lines.MoveNext())
{
string firstLine = lines.Current;
if (!lines.MoveNext())
throw new InvalidOperationException("odd nr of lines");
string secondLine = lines.Current;
// use 2 lines
Console.WriteLine("S: " + firstLine ,"J: " + secondLine);
}
}
I have an requirement like search functionality,
Edit: Search is like set of varchars usually this ID contains values like C001,C002,...
so if user is entering the ranges like C001-C010 it should search all the rows in oracle db in between that C001-C010 this is what my requirement.
If user is entering some ranges like C001-C010 in text box this should be splited as two variables and it should search all the elements in between this range.
How to do this. I think using Enumerator range we can do this but i am facing problem like splitting strings needs to declared as separate variable and search between that. which I cant achieve.
sample code is below.
else if (!_search.MultipleId.Contains('-'))
{
filterExp = filterExp.And(x => x.Id.Contains(_search.MultipleId));
}
If the Id is in format of CXXX then you can do something like this:
if(_search.MultipleId.Contain("-"))
{
var range = _search.MultipleId.Split('-');
filterExp = filterExp.And(x => x.Id >= range[0] && x.Id <= range1);
}
Not knowing all of you business requirements, you could try something like this:
class Program
{
static void Main(string[] args)
{
var items = new List<string> { "C001", "C010" };
var firstChar = new List<string>();
var remainingChars = new List<string>();
items.ForEach(i =>
{
firstChar.Add(i[0].ToString());
remainingChars.Add(i.Substring(1));
});
firstChar.ForEach(f => { Console.Write(f + " "); });
Console.WriteLine();
remainingChars.ForEach(r => { Console.Write(r + " "); });
Console.WriteLine();
//Prints the following
//C C
//001 010
//Press any key to continue . . .
}
}
Something like this might help:
var pair = yourstring.Split('-')
.Select(a => new { one= a[0], two= a[1]});
I would cast the dataset to a list so I could use the indices of the start and end args.
var items = dataset.ToList();
var searchString = "B1, C10";
var removedWhitespace = Regex.Replace(searchString, #"\s+", "");
var rangeToSearch = removedWhitespace.Split(',');
var startPosition = items.FindIndex(x => x.ID == rangeToSearch.First());
var endPosition = items.FindIndex(x => x.ID == rangeToSearch.Last());
var selectedItems =
items.Skip(startPosition).Take(endPosition - startPosition + 1); // +1 to include the original id's
If you have to you can order the list, but one caveat with this method is that the list is ordered alphabetically, so you may have to do some additional processing to make sure that you return all the values in the range.
public static HashSet<string> getIdsFromRangeQuery(string multipleIds)
{
multipleIds = multipleIds.ToUpper().Replace(" ", "");
HashSet<string> inSet = new HashSet<string>();
string[] parts = multipleIds.Split(new[] { ";" }, StringSplitOptions.None);
foreach (string part in parts)
{
Regex rgx = new Regex(#"^M([0 - 9] +)C([0 - 9] +)$");
Regex rgxTwo = new Regex(#"^M([0-9]+)C([0-9]+)-M([0-9]+)C([0-9]+)$");
Regex rgxThree = new Regex(#"^[0-9]+$");
Regex rgxFour = new Regex(#"^([0-9]+)-([0-9]+)$");
if (rgx.IsMatch(part))
{
inSet.Add(part);
}
else if (rgxTwo.IsMatch(part))
{
string[] fromTo = part.Split(new[] { "-" }, StringSplitOptions.None);
int mFrom = int.Parse(fromTo[0].Substring(1, fromTo[0].IndexOf("C")));
int mTo = int.Parse(fromTo[1].Substring(1, fromTo[1].IndexOf("C")));
int cFrom = int.Parse(fromTo[0].Substring(fromTo[0].LastIndexOf("C") + 1));
int cTo = int.Parse(fromTo[1].Substring(fromTo[1].LastIndexOf("C") + 1));
for (int i = mFrom; i <= mTo; i++)
{
for (int j = cFrom; j <= cTo; j++)
{
inSet.Add("M" + i + "C" + j);
}
}
}
else if (rgxThree.IsMatch(part))
{
inSet.Add(part);
}
else if (rgxFour.IsMatch(part)
{
string[] fromTo = part.Split(new[] { "-" }, StringSplitOptions.None);
int from = int.Parse(fromTo[0]);
int to = int.Parse(fromTo[1]);
for (int i = from; i <= to; i++)
{
inSet.Add(i.ToString());
}
}
else
{
inSet.Add(part);
}
}
return inSet;
}
I want to increase the last number of the version (for Example: 1.0.0.0 -> 1.0.0.1).
I would prefer to kep this code :)
The actuall code looks like that:
private void UpdateApplicationVersion(string filepath)
{
string currentApplicationVersion = "1.2.3.4"
string newApplicationVersionDigit = ((currentApplicationVersion.Split('.')[3]) + 1).ToString();
string newApplicatonVersion = string.Empty;
for (int i = 0; i <= currentApplicationVersion.Length; i++)
{
if (i == 7)
{
newApplicatonVersion += newApplicationVersionDigit ;
}
else
{
newApplicatonVersion += currentApplicationVersion.ToCharArray()[i];
}
}
Do it simple way,
string v1 = "1.0.0.1";
string v2 = "1.0.0.4";
var version1 = new Version(v1);
var version2 = new Version(v2);
var result = version1.CompareTo(version2);
if (result > 0)
Console.WriteLine("version1 is greater");
else if (result < 0)
Console.WriteLine("version2 is greater");
else
Console.WriteLine("versions are equal");
I think it could be done by parsing all components of the version, manipulate the last one and put them together again as follows.
string[] Components = currentApplicationVersion.Split('.');
int Maj = Convert.ToInt32(Components[0]);
int Min = Convert.ToInt32(Components[1]);
int Revision = Convert.ToInt32(Components[2]);
int Build = Convert.ToInt32(Components[3]);
Build++;
string newApplicationVersion
= string.Format("{0}.{1}.{2}.{3}", Maj, Min, Revision, Build);
You can try Split and Join:
string currentApplicationVersion = "1.2.3.4";
int[] data = currentApplicationVersion.Split('.')
.Select(x => int.Parse(x, CultureInfo.InvariantCulture))
.ToArray();
// The last version component is data[data.Length - 1]
// so you can, say, increment it, e.g.
data[data.Length - 1] += 1;
// "1.2.3.5"
String result = String.Join(".", data);
There's a class build for working with version numbers. It's called Version and can be found in the System namespace
you can parse your current version by passing the string representing the version to the constructor
var currentApplicationVersion = new Version(currentApplicationVersionString);
and then get the new one with another of the constructors
var newApplicationVersion = new Version(
currentApplicationVersion.Major,
currentApplicationVersion.Minor,
currentApplicationVersion.Build,
currentApplicationVersion.Revision +1
);
and then simply call .ToString() if you need it as a string
I have a problem with C#.
I am writing code to search a text file until it finds a certain word, then the code should move three lines and read the fourth, then continue the search to find the certain word again.
Now I don't know how to navigate through the file (forward and backward) to the line I want.
Can anybody help?
You can do something like this:
var text = File.ReadAllLines("path"); //read all lines into an array
var foundFirstTime = false;
for (int i = 0; i < text.Length; i++)
{
//Find the word the first time
if(!foundFirstTime && text[i].Contains("word"))
{
//Skip 3 lines - and continue
i = Math.Min(i+3, text.Length-1);
foundFirstTime = true;
}
if(foundFirstTime && text[i].Contains("word"))
{
//Do whatever!
}
}
// read file
List<string> query = (from lines in File.ReadLines(this.Location.FullName, System.Text.Encoding.UTF8)
select lines).ToList<string>();
for (int i = 0; i < query.Count; i++)
{
if (query[i].Contains("TextYouWant"))
{
i = i + 3;
}
}
Your requirements state that you are searching for a specific word. If that is true and you are not instead looking for a specific string, then the checked answer on this is wrong. Instead you should use:
string[] lines = System.IO.File.ReadAllLines("File.txt");
int skip = 3;
string word = "foo";
string pattern = string.Format("\\b{0}\\b", word);
for (int i = 0; i < lines.Count(); i++)
{
var match = System.Text.RegularExpressions.Regex.IsMatch(lines[i], pattern);
System.Diagnostics.Debug.Print(string.Format("Line {0}: {1}", Array.IndexOf(lines, lines[i], i) + 1, match));
if (match) i += skip;
}
If you use the string.contains method and the word you are searching for is "man", while your text somewhere contains "mantle" and "manual", the string.contains method will return as true.
I have two paths: 'C:\ol\il\ek' and 'ek\mek\gr'. How can i cut the common part from these paths? I'm making one paths from them like that (h- first path, n - second path):
ee.Load(h + "\\" + n);
Assuming the "common part" is always the last segment of the first path, and the first segment of the second one, you can use Path.GetDirectoryName to strip this segment from the first path and combine the result with the second path using Path.Combine:
var result = Path.Combine(Path.GetDirectoryName(#"C:\ol\il\ek"), #"ek\mek\gr");
// result == #"C:\ol\il\ek\mek\gr"
I wrote following class, that I think could meet your needs. First I split paths in single directory parts. Than I check last parts of path1 with first of path2, increasing num of parts I compare, until I reach parts limit or get equal parts.
When I found equal parts in the two paths, I clear path2 parts.
static class Program
{
static void Main(string[] args)
{
Console.WriteLine(JoinPaths(#"C:\ol\il\ek", #"ek\mek\gr"));
Console.WriteLine(JoinPaths(#"C:\ol\il\ek", #"il\ek\mek\gr"));
Console.WriteLine(JoinPaths(#"C:\ol\il\ek", #"ol\il\ek\mek\gr"));
Console.ReadLine();
}
public static T[] Slice<T>(this T[] source, int start, int end)
{
if (end < 0)
end = source.Length + end;
var len = end - start;
var res = new T[len];
for (var i = 0; i < len; i++)
res[i] = source[i + start];
return res;
}
private static string JoinPaths(string path1, string path2)
{
var parts1 = path1.ToLower().Split(new char[] { '\\' });
var parts2 = path2.ToLower().Split(new char[] { '\\' });
int commonPartLen = 1;
while (commonPartLen<parts1.Length && commonPartLen<parts2.Length)
{
string slice1 = string.Join("\\", parts1.Slice(parts1.Length - commonPartLen, parts1.Length ));
string slice2 = string.Join("\\", parts2.Slice(0, commonPartLen));
if (slice1 == slice2)
{
for (var i = 0; i < commonPartLen; i++)
parts2[i] = "";
break;
}
commonPartLen++;
}
string firstPath = string.Join("\\", parts1.Where(a => !string.IsNullOrEmpty(a)));
string secondPath = string.Join("\\", parts2.Where(a => !string.IsNullOrEmpty(a)));
return firstPath + "\\"+secondPath; ;
}
}
I hope the code is clear.