I have the following query:
Locations.ForEach(a => a.Name = a.Name.Replace(#"/", #" ").Replace(#"\", #" ");
What I like to do is if the Name has a length of greater than 31 characters, I like to trim upto the 31st character. Is there each easy way to do this with the .ForEach I have.
One way to accomplish this is to use a combination of the System.Linq extension method Take to "take" the number of characters you want from the beginning of the string and string.Concat to combine those characters back to a string, and then use this for the new value.
Using your example:
int maxLength = 31;
Locations.ForEach(a =>
a.Name = string.Concat(a.Name.Replace(#"/", #" ").Replace(#"\", #" ").Take(maxLength));
Or a full compile-able example:
public class Location
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
private static void Main()
{
var locations = new List<Location>();
var maxLength = 5;
// Populate our list with locations that have an ever growing Name length
for (int i = 0; i < 10; i++)
{
locations.Add(new Location {Name = new string('*', i)});
}
Console.WriteLine("Beginning values:");
locations.ForEach(Console.WriteLine);
// Trim the values
locations.ForEach(l => l.Name = string.Concat(l.Name.Take(maxLength)));
Console.WriteLine("\nEnding values:");
locations.ForEach(Console.WriteLine);
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
Or example with Substring:
const int maxLength = 31;
Locations.ForEach(a => a.Name = a.Name
.Substring(0, Math.Min(a.Name.Length, maxLength))
.Replace(#"/", #" ")
.Replace(#"\", #" "));
Related
Problem: I want to write a method that takes a message/index pair like this:
("Hello, I am *Name1, how are you doing *Name2?", 2)
The index refers to the asterisk delimited name in the message. So if the index is 1, it should refer to *Name1, if it's 2 it should refer to *Name2.
The method should return just the name with the asterisk (*Name2).
I have attempted to play around with substrings, taking the first delimited * and ending when we reach a character that isn't a letter, number, underscore or hyphen, but the logic just isn't setting in.
I know this is similar to a few problems on SO but I can't find anything this specific. Any help is appreciated.
This is what's left of my very vague attempt so far. Based on this thread:
public string GetIndexedNames(string message, int index)
{
int strStart = message.IndexOf("#") + "#".Length;
int strEnd = message.LastIndexOf(" ");
String result = message.Substring(strStart, strEnd - strStart);
}
If you want to do it the old school way, then something like:
public static void Main(string[] args)
{
string message = "Hello, I am *Name1, how are you doing *Name2?";
string name1 = GetIndexedNames(message, "*", 1);
string name2 = GetIndexedNames(message, "*", 2);
Console.WriteLine(message);
Console.WriteLine(name1);
Console.WriteLine(name2);
Console.ReadLine();
}
public static string GetIndexedNames(string message, string singleCharDelimiter, int index)
{
string valid = "abcdefghijlmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
string[] parts = message.Split(singleCharDelimiter.ToArray());
if (parts.Length >= index)
{
StringBuilder sb = new StringBuilder();
for(int i = 0; i < parts[index].Length; i++)
{
string character = parts[index].Substring(i, 1);
if (valid.Contains(character))
{
sb.Append(character);
}
else
{
return sb.ToString();
}
}
return sb.ToString();
}
return "";
}
You can try using regular expressions to match the names. Assuming that name is a sequence of word characters (letters or digits):
using System.Linq;
using System.Text.RegularExpressions;
...
// Either name with asterisk *Name or null
// index is 1-based
private static ObtainName(string source, int index) => Regex
.Matches(source, #"\*\w+")
.Cast<Match>()
.Select(match => match.Value)
.Distinct() // in case the same name repeats several times
.ElementAtOrDefault(index - 1);
Demo:
string name = ObtainName(
"Hello, I am *Name1, how are you doing *Name2?", 2);
Console.Write(name);
Outcome:
*Name2
Perhaps not the most elegant solution, but if you want to use IndexOf, use a loop:
public static string GetIndexedNames(string message, int index, char marker='*')
{
int lastFound = 0;
for (int i = 0; i < index; i++) {
lastFound = message.IndexOf(marker, lastFound+1);
if (lastFound == -1) return null;
}
var space = message.IndexOf(' ', lastFound);
return space == -1 ? message.Substring(lastFound) : message.Substring(lastFound, space - lastFound);
}
This question already has answers here:
how can i split a string by multiple delimiters and keep the delimiters?
(1 answer)
RegEx - Match Numbers of Variable Length
(4 answers)
Closed 4 years ago.
I have a text file. One of the columns contains a field which contains text along with numbers.
I'm trying to figure out the best way to split the numbers and text.
Below is an example of the typical values in the field.
.2700 Aqr sh./Tgt sh.
USD 2.4700/Tgt sh.
Currently I'm making use of the Split function (code below) however feel there is probably a smarter way of doing this.
My assumption is there will only ever be one number in the text (I'm 99% sure this is the case) however I have only seen a few examples so its possible my code below will not work.
I have read a little on regex. But not sure I tested it properly as it didn't quite get the output I wanted. For example
string input = "USD 2.4700/Tgt sh.";
string[] numbers = Regex.Split(input, #"\D+");
foreach (string value in numbers)
{
if (!string.IsNullOrEmpty(value))
{
int i = int.Parse(value);
Console.WriteLine("Number: {0}", i);
}
}
But the output is,
2
47
Whereas I was expecting 2.47 and I also don't want to lose the text. My desired result is
myText = "USD Tgt sh."
myNum = 2.47
For the other example
myText = "Aqr sh./Tgt sh."
myNum = 0.27
My Code
string[] sData = sTerms.Split(' ');
double num;
bool isNum = double.TryParse(sData[0], out num);
if(isNum)
{
ma.StockTermsNum = num;
StringBuilder sb = new StringBuilder();
for (int i = 1; i < sData.Length; i++)
sb = sb.Append(sData[i] + " ");
ma.StockTerms = sb.ToString();
}
else
{
string[] sNSplit = sData[1].Split('/');
ma.StockTermsNum = Convert.ToDouble(sNSplit[0]);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < sData.Length; i++)
{
if (i == 1)
sb = sb.Append(sNSplit[i] + " ");
else
sb = sb.Append(sData[i] + " ");
}
ma.StockTerms = sb.ToString();
}
I suggest spliting by group, (...) in order to preserve delimiter:
string source = #".2700 Aqr sh./Tgt sh.";
//string source = "USD 2.4700/Tgt sh.";
// please, notice "(...)" in the pattern - group
string[] parts = Regex.Split(source, #"([0-9]*\.?[0-9]+)");
// combining all texts
string myText = string.Concat(parts.Where((v, i) => i % 2 == 0));
// combining all numbers
string myNumber = string.Concat(parts.Where((v, i) => i % 2 != 0));
Tests:
string[] tests = new string[] {
#".2700 Aqr sh./Tgt sh.",
#"USD 2.4700/Tgt sh.",
};
var result = tests
.Select(test => new {
text = test,
parts = Regex.Split(test, #"([0-9]*\.?[0-9]+)"),
})
.Select(item => new {
text = item.text,
myText = string.Concat(item.parts.Where((v, i) => i % 2 == 0)),
myNumber = string.Concat(item.parts.Where((v, i) => i % 2 != 0)),
})
.Select(item => $"{item.text,-25} : {item.myNumber,-15} : {item.myText}");
Console.WriteLine(string.Join(Environment.NewLine, result));
Outcome:
.2700 Aqr sh./Tgt sh. : Aqr sh./Tgt sh. : .2700
USD 2.4700/Tgt sh. : USD /Tgt sh. : 2.4700
Could by something like this regex:
string input = "USD 2.4700/Tgt sh.";
var numbers = Regex.Matches(input, #"[\d]+\.?[\d]*");
foreach (Match res in numbers)
{
if (!string.IsNullOrEmpty(res.Value))
{
decimal i = decimal.Parse(res.Value);
Console.WriteLine("Number: {0}", i);
}
}
I would suggest you to use System.Text.RegularExpressions.RegEx. Here is example how you can achieve it:
static void Main(string[] args)
{
string a1 = ".2700 Aqr sh./Tgt sh.";
string a2 = "USD 2.4700/Tgt sh.";
var firstStringNums = GetNumbersFromString(ref a1);
Console.Write("My Text: {0}",a1);
Console.Write("myNums: ");
foreach(double a in firstStringNums)
{
Console.Write(a +"\t");
}
var secondStringNums = GetNumbersFromString(ref a2);
Console.Write("My Text: {0}", a2);
Console.Write("myNums: ");
foreach (double a in secondStringNums)
{
Console.Write(a + "\t");
}
}
public static List<double> GetNumbersFromString(ref string input)
{
List<double> result = new List<double>();
Regex r = new Regex("[0-9.,]+");
var numsFromString = r.Matches(input);
foreach(Match a in numsFromString)
{
if(double.TryParse(a.Value,out double val))
{
result.Add(val);
input =input.Replace(a.Value, "");
}
}
return result;
}
The pattern is just an example and off course will not cover every case that you will imagine.
help i want to get the specific string from my string x="Glass 1 1000"; i want to get the string "Glass" only and save it to my string type.
int[] quanvalue = new int[2];
int x1 = 0;
string type = "";
string x="Glass 1 1000";
string[] numbers = Regex.Split(x, #"\D+");
foreach (string value in numbers)
{
if (!string.IsNullOrEmpty(value))
{
int ib = int.Parse(value);
quanvalue[x1] = ib;
MessageBox.Show(quanvalue[0].ToString() + " " + quanvalue[1].ToString());
x1++;
}
else
{
// i want to get string from here
}
string sub = x.Substring(0, 5);
You can use a substring function to fetch the first 5 characters from x.
And save it in x itself
Given a string, I need to replace substrings according to a given mapping. The mapping determines where to start the replacement, the length of text to be replaced and the replacement string. The mapping is according to the following scheme:
public struct mapItem
{
public int offset;
public int length;
public string newString;
}
For example: given a mapping {{0,3,"frog"},{9,3,"kva"}} and a string
"dog says gav"
we replace starting at position 0 a substring of the length 3 to the "frog", i.e.
dog - > frog
and starting the position 9 a substring of the length 3 to the "kva", i.e.
gav->kva
The new string becomes:
"frog says kva"
How can I do it efficiently?
You have to take care that replacements take into account the shift produced by preceding replacements. Also using a StringBuilder is more efficient, as is doesn't allocate new memory at each operation as string operations do. (Strings are invariant, which means that a completely new string is created at each string operation.)
var maps = new List<MapItem> { ... };
var sb = new StringBuilder("dog says gav");
int shift = 0;
foreach (MapItem map in maps.OrderBy(m => m.Offset)) {
sb.Remove(map.Offset + shift, map.Length);
sb.Insert(map.Offset + shift, map.NewString);
shift += map.NewString.Length - map.Length;
}
string result = sb.ToString();
The OrderBy makes sure that the replacements are executed from left to right. If you know that the mappings are provided in this order, you can drop the OrderBy.
Another simpler way is to begin with the replacements at the right end and work backwards, so that the character shifts do not alter the positions of not yet executed replacements:
var sb = new StringBuilder("dog says gav");
foreach (MapItem map in maps.OrderByDescending(m => m.Offset)) {
sb.Remove(map.Offset, map.Length);
sb.Insert(map.Offset, map.NewString);
}
string result = sb.ToString();
In case the mappings are already ordered in ascending order, a simple reverse for-statement seems appropriate:
var sb = new StringBuilder("dog says gav");
for (int i = maps.Count - 1; i >= 0; i--) {
MapItem map = maps[i];
sb.Remove(map.Offset, map.Length);
sb.Insert(map.Offset, map.NewString);
}
string result = sb.ToString();
You can write an Extension Method like below:
public static class ExtensionMethod
{
public static string ReplaceSubstringByMap(this string str, List<mapItem> map)
{
int offsetShift = 0;
foreach (mapItem mapItem in map.OrderBy(x => x.offset))
{
str = str.Remove(mapItem.offset + offsetShift, mapItem.length).Insert(mapItem.offset + offsetShift, mapItem.newString);
offsetShift += mapItem.newString.Length - mapItem.length;
}
return str;
}
}
And invoke it like below:
var map = new List<mapItem>
{
new mapItem
{
offset = 0,
length = 1,
newString = "frog"
},
new mapItem
{
offset = 9,
length = 1,
newString = "kva"
}
};
string str = "dog says gav";
var result = str.ReplaceSubstringByMap(map);
We tried a few solutions now that try and use XML parsers. All fail because the strings are not always 100% valid XML. Here's our problem.
We have strings that look like this:
var a = "this is a testxxx of my data yxxx and of these xxx parts yxxx";
var b = "hello testxxx world yxxx ";
"this is a testxxx3yxxx and of these xxx1yxxx";
"hello testxxx1yxxx ";
The key here is that we want to do something to the data between xxx and yxxx. In the example above I would need a function that counts words and replaces the strings with a word count.
Is there a way we can process the string a and apply a function to change the data that's between the xxx and yxxx? Any function right now as we're just trying to get an idea of how to code this.
You can use Split method:
var parts = a.Split(new[] {"xxx", "yxxx"}, StringSplitOptions.None)
.Select((s, index) =>
{
string s1 = index%2 == 1 ? string.Format("{0}{2}{1}", "xxx", "yxxx", s + "1") : s;
return s1;
});
var result = string.Join("", parts);
If it always going to xxx and yxxx, you can use regex as suggested.
var stringBuilder = new StringBuilder();
Regex regex = new Regex("xxx(.*?)yxxx");
var splitGroups = Regex.Match(a);
foreach(var group in splitGroups)
{
var value = splitGroupsCopy[i];
// do something to value and then append it to string builder
stringBuilder.Append(string.Format("{0}{1}{2}", "xxx", value, "yxxx"));
}
I suppose this is as basic as it gets.
Using Regex.Replace will replace all the matches with your choice of text, something like this:
Regex rgx = new Regex("xxx.+yxxx");
string cleaned = rgx.Replace(a, "replacementtext");
This code will process each of the parts delimited by "xxx". It preserves the "xxx" separators. If you do not want to preserve the "xxx" separators, remove the two lines that say "result.Append(separator);".
Given:
"this is a testxxx of my data yxxx and there are many of these xxx parts yxxx"
It prints:
"this is a testxxx>> of my data y<<xxx and there are many of these xxx>> parts y<<xxx"
I'm assuming that's the kind of thing you want. Add your own processing to "processPart()".
using System;
using System.Text;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
string text = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
string separator = "xxx";
var result = new StringBuilder();
int index = 0;
while (true)
{
int start = text.IndexOf(separator, index);
if (start < 0)
{
result.Append(text.Substring(index));
break;
}
result.Append(text.Substring(index, start - index));
int end = text.IndexOf(separator, start + separator.Length);
if (end < 0)
{
throw new InvalidOperationException("Unbalanced separators.");
}
start += separator.Length;
result.Append(separator);
result.Append(processPart(text.Substring(start, end-start)));
result.Append(separator);
index = end + separator.Length;
}
Console.WriteLine(result);
}
private static string processPart(string part)
{
return ">>" + part + "<<";
}
}
}
[EDIT] Here's the code amended to work with two different separators:
using System;
using System.Text;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
string text = "this is a test<pre> of my data y</pre> and there are many of these <pre> parts y</pre>";
string separator1 = "<pre>";
string separator2 = "</pre>";
var result = new StringBuilder();
int index = 0;
while (true)
{
int start = text.IndexOf(separator1, index);
if (start < 0)
{
result.Append(text.Substring(index));
break;
}
result.Append(text.Substring(index, start - index));
int end = text.IndexOf(separator2, start + separator1.Length);
if (end < 0)
{
throw new InvalidOperationException("Unbalanced separators.");
}
start += separator1.Length;
result.Append(separator1);
result.Append(processPart(text.Substring(start, end-start)));
result.Append(separator2);
index = end + separator2.Length;
}
Console.WriteLine(result);
}
private static string processPart(string part)
{
return "|" + part + "|";
}
}
}
The indexOf() function will return to you the index of the first occurrence of a given substring.
(My indices might be a bit off, but) I would suggest doing something like this:
var searchme = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
var startindex= searchme.indexOf("xxx");
var endindex = searchme.indexOf("yxxx") + 3; //added 3 to find the index of the last 'x' instead of the index of the 'y' character
var stringpiece = searchme.substring(startindex, endindex - startindex);
and you can repeat that while startindex != -1
Like I said, the indices might be slightly off, you might have to add a +1 or -1 somewhere, but this will get you along nicely (I think).
Here is a little sample program that counts chars instead of words. But you should just need to change the processor function.
var a = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
a = ProcessString(a, CountChars);
string CountChars(string a)
{
return a.Length.ToString();
}
string ProcessString(string a, Func<string, string> processor)
{
int idx_start, idx_end = -4;
while ((idx_start = a.IndexOf("xxx", idx_end + 4)) >= 0)
{
idx_end = a.IndexOf("yxxx", idx_start + 3);
if (idx_end < 0)
break;
var string_in_between = a.Substring(idx_start + 3, idx_end - idx_start - 3);
var newString = processor(string_in_between);
a = a.Substring(0, idx_start + 3) + newString + a.Substring(idx_end, a.Length - idx_end);
idx_end -= string_in_between.Length - newString.Length;
}
return a;
}
I would use Regex Groups:
Here my solution to get the parts in the string:
private static IEnumerable<string> GetParts( string searchFor, string begin, string end ) {
string exp = string.Format("({0}(?<searchedPart>.+?){1})+", begin, end);
Regex regex = new Regex(exp);
MatchCollection matchCollection = regex.Matches(searchFor);
foreach (Match match in matchCollection) {
Group #group = match.Groups["searchedPart"];
yield return #group.ToString();
}
}
you can use it like to get the parts:
string a = "this is a testxxx of my data yxxx and there are many of these xxx parts yxxx";
IEnumerable<string> parts = GetParts(a, "xxx", "yxxx");
To replace the parts in the original String you can use the Regex Group to determine Length and StartPosition (#group.Index, #group.Length).