Get a part of that String C# - c#

:barbosza!barbosza#barbosza.tmi.twitch.tv PRIVMSG #pggamesbr :My text
I want the part after the second ':', but i can't split by ':' because sometimes contains it too.

You can split and specify the maximum number of items, so that everything after the second colon ends up in the third item:
string[] parts = str.Split(new char[]{':'}, 3);
The part after the second colon is now in parts[2].

I guess that "he contains it too" means "My text contains it too".
In that case, do this
string toFind = "#pggamesbr :";
string myText = myString.Substring(myString.IndexOf(toFind) + toFind.Length);

I like Guffa's simple Split solution, and would go with that if this is all you need here. But, just for fun...
If you run into a lot of odd cases like this -- things you wish were easier to do with strings -- you can consider adding extension methods to handle them. E.g.,
using System;
public static MyStringExtentions
{
public static string After(this string orig, char delimiter)
{
int p = orig.indexOf(delimiter);
if (p == -1)
return string.Empty;
else
return orig.Substring(p + 1);
}
}
And then, in your existing code, as long as you have a using directive to include reference access to MyStringExtentions's definition:
string afterPart = myString.After(':').After(':');
Disclaimer: I didn't actually test this. Some tuning may be required. And it could probably be tuned to be more efficient, etc.
Again, this is probably overkill for this one problem. (See Guffa's perfectly good simple answer for that.) Just tossing it out for when you find yourself with lots of these and want a common way to make them available.
Ref. Extension Methods (C# Programming Guide)

Related

C#: How to show a newline within a MessageBox

I often display messages using MessageBox.Show("This is my message");.
Sometimes I need a newline within a longer text.
Usually I use a variant which put one string per line:
MessageBox.Show("Line1" + Environment.NewLine + "Line2" + Environment.NewLine + "Line3");
But I don't like the "overhead". So I found the following solution:
MessageBox.Show(string.Format("Line1{0}Line2{0}Line3", Environment.NewLine));
Is there a better solution with less overhead?
You have pretty much found both ways to do it. A third one would be with string interpolation (to avoid the "extra" string.Format call.
$"Line1 {Environment.NewLine} Line2 {Environment.NewLine} Line3"
Fiddler example: https://dotnetfiddle.net/16Wy57
The string.Format way is pretty much the example at the official format docs
I prefer interpolation as it avoids the extra function in my code, but really it all comes down to preference.
There really is no "overhead" in any of the options, as the operation is trivial and at the end of the day, you concatenate strings.
If you want a growable solution with less overhead, here's a solution that is clean and scalable:
using System;
var messageLines = new string[]
{
"Some line here",
"More lines",
"Could be loaded from a database",
"With each row as a string"
};
MessageBox.Show(string.Join(Environment.NewLine, messageLines));
Verbatim strings, just make sure you don't have identations on the string.
string v =
#"Hello,
! Today is
, it's
now.";
Console.WriteLine(v);
Output:
Hello,
! Today is
, it's
now.
Demo: https://dotnetfiddle.net/WvjGfC
MessageBox.Show("First Line\nSecond Line\nThird Line");
\n inserts a new line in a string which works for MessageBox.
Be careful with this within files. There you find \r\n, \n\r, \r, \n - it depends on the operating system or file format.
Luke Parker's example can be slicked up a bit if you make your own MessageBox class (sadly it can't be done as an extension method because there isn't an available instance of MessageBox):
namespace System.Windows.Forms
{
public static class MessageBoxLines
{
public static void Show(string[] lines)
{
MessageBox.Show(string.Join(Environment.NewLine, lines));
}
}
}
You'd use it like:
MessageBoxLines.Show(new[] { "Line1", "Line2"} );
You'd add more overloads or optional parameters for the other forms (those that take a caption, specify the buttons etc
This could be tidied further if you use a params array:
public static void Show(params string[] lines)
{
MessageBox.Show(string.Join(Environment.NewLine, lines));
}
Which you'd use like:
MessageBoxLines.Show("Line1", "Line2" );
But using params would possibly interfere with the standard pattern for the other overloads (buttons etc) so you'd have to get more cute working that one out, either by rearranging the order so params is last or by putting all the args into a params object and digging them out / casting them back to what they should be (which clowns intellisense a bit)

Split string to array when some elements are empty

I need to process a large amount of csv data in real time as it is spat out by a TCP port. Here is an example as displayed by Putty:
MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,8000,,,51.26582,-0.33783,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,
MSG,1,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,BAW469,,,,,,,,,,,
MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,8000,,,51.26559,-0.33835,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,
I need to put each line of data in string (line) into an array (linedata[]) so that I can read and process certain elements, but linedata = line.Split(','); seems to ignore the many empty elements, with the result that linedata[20], for example, may or may not exist, and if it doesn't I get an error if I try to read it. Even if element 20 in the line contains a value it won't necessarily be the 20th element in the array. And that's no good.
I can work out how to parse line character by character into linedata[], inserting an empty string where appropriate, but surely there must be a better way ? Have I missed something obvious ?
Many Thanks. Perhaps I'd better add that I'm quite new to C#, my past experience is all with Delphi 7. I really miss stringlists.
Edited: sorry, this is now resolved with the help of MSDN's documentation. This code works: lineData = line.Split(separators, StringSplitOptions.None); after setting "string[] separators = { "," };". My big mistake was to follow examples found on tutorial sites which didn't give any clues that the .split method had any options.
https://msdn.microsoft.com/en-us/library/system.stringsplitoptions(v=vs.110).aspx
That link has an example section, look at example 1b specifically. There is an extra parameter to Split called StringSplitOptions which does this.
For Example:
string[] linedata = line.Split(charSeparators, StringSplitOptions.None);
foreach (string line in linedata)
{
Console.Write("<{0}>", line);
}
Console.Write("\n\n");
The way to find this sort of information is to start with the Reference Documentation for the function, and hope it has an option or a link to a similar function.
If you want to also start validating types, handling variants in the format etc... you could move up to a CSV library. If you do not need that functionality, this is the easiest way and efficient for small files.
Some of the overloads for String.Split() take a StringSplitOptions argument, and if you use the RemoveEmptyEntries option, it will...remove the empty entries. So you can specify the None option:
linedata = line.Split(new [] { ',' }, StringSplitOptions.None);
Or better yet, use the overload that doesn't take a StringSplitOptions, which treats it as None by default:
linedata = line.Split(',');
The code in your question indicates that you are doing this, but your description of the problem suggests that you are not.
However, you're probably better off using an actual CSV parser, which would handle things like unescaping and so on.
The StringReader class provides methods for reading lines, characters, or blocks of characters from a string. Hope this could be the clue
string str = #"MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,8000,,,51.26582,-0.33783,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,
MSG,1,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,BAW469,,,,,,,,,,,
MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,8000,,,51.26559,-0.33835,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,";
using (StringReader reader = new StringReader(str))
do
{
string[] linedata = reader.ReadLine().Split(',');
} while (reader.Read() != -1);
While you should look into the various ways the String class can help you here, sometimes the quick and dirty "MAKE it fit" option is called for. In this case, that'd be to roll through the strings in advance and ensure you have at least one character between the commas.
public static string FixIt(string s)
{
return s.Replace(",,", ", ,");
}
You should be able to:
var lineData = FixIt(line).Split(',');
Edit: In response to the question below, I'm not sure what you meant, but if you mean doing it without creating a helper method, you can do so easily. The code will be harder to read and troubleshoot if you do it in one line though. My personal rule is, if you have to do it a LOT, it should probably be a method. If you only had to do it once, this is particularly clean. I'd actually do it this way and just wrap it in a method that does all the work for you.
var lineData = line.Replace(",,", ", ,").Split(',');
As a method, it'd be:
public static string[] GiveMeAnArray(string s)
{
return s.Replace(",,", ", ,").Split(',');
}

String.Contains and String.LastIndexOf C# return different result?

I have this problem where String.Contains returns true and String.LastIndexOf returns -1. Could someone explain to me what happened? I am using .NET 4.5.
static void Main(string[] args)
{
String wikiPageUrl = #"http://it.wikipedia.org/wiki/ʿAbd_Allāh_al-Sallāl";
if (wikiPageUrl.Contains("wikipedia.org/wiki/"))
{
int i = wikiPageUrl.LastIndexOf("wikipedia.org/wiki/");
Console.WriteLine(i);
}
}
While #sa_ddam213's answer definitely fixes the problem, it might help to understand exactly what's going on with this particular string.
If you try the example with other "special characters," the problem isn't exhibited. For example, the following strings work as expected:
string url1 = #"http://it.wikipedia.org/wiki/»Abd_Allāh_al-Sallāl";
Console.WriteLine(url1.LastIndexOf("it.wikipedia.org/wiki/")); // 7
string url2 = #"http://it.wikipedia.org/wiki/~Abd_Allāh_al-Sallāl";
Console.WriteLine(url2.LastIndexOf("it.wikipedia.org/wiki/")); // 7
The character in question, "ʿ", is called a spacing modifier letter1. A spacing modifier letter doesn't stand on its own, but modifies the previous character in the string, this case a "/". Another way to put this is that it doesn't take up its own space when rendered.
LastIndexOf, when called with no StringComparison argument, compares strings using the current culture.
When strings are compared in a culture-sensitive manner, the "/" and "ʿ" characters are not seen as two distinct characters--they're processed into one character, which does not match the parameter passed in to LastIndexOf.
When you pass in StringComparison.Ordinal to LastIndexOf, the characters are treated as distinct, due to the nature of Ordinal comparison.
Another way to make this work would be to use CompareInfo.LastIndexOf and supply the CompareOptions.IgnoreNonSpace option:
Console.WriteLine(
CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(
wikiPageUrl, #"it.wikipedia.org/wiki/", CompareOptions.IgnoreNonSpace));
// 7
Here we're saying that we don't want combining characters included in our string comparison.
As a sidenote, this means that #Partha's answer and #Noctis' answer only work because the character is being applied to a character that doesn't appear in the search string that's passed to LastIndexOf.
Contrast this with the Contains method, which by default performs an Ordinal (case sensitive and culture insensitive) comparison. This explains why Contains returns true and LastIndexOf returns false.
For a fantastic overview of how strings should be manipulated in the .NET framework, check out this article.
1: Is this different than a combining character or is it a type of combining character? would appreciate if someone would clear that up for me.
Try using StringComparison.Ordinal
This will compare the string by evaluating the numeric values of the corresponding chars in each string, this should work with the special chars you have in that example string
string wikiPageUrl = #"http://it.wikipedia.org/wiki/ʿAbd_Allāh_al-Sallāl";
int i = wikiPageUrl.LastIndexOf("http://it.wikipedia.org/wiki/", StringComparison.Ordinal);
// returns 0;
The thing is C# lastindexof looks from behind.
And wikipedia.org/wiki/ is followed by ' which it takes as escape sequence. So either remove ' after wiki/ or have an # there too.
The following syntax will work( anyone )
string wikiPageUrl = #"http://it.wikipedia.org/wiki/Abd_Allāh_al-Sallāl";
string wikiPageUrl = #"http://it.wikipedia.org/wiki/#ʿAbd_Allāh_al-Sallāl";
int i = wikiPageUrl.LastIndexOf("wikipedia.org/wiki");
All 3 works
If you want a generalized solution for this problem replace ' with #' in your string before you perform any operations.
the ' characters throws it off.
This should work, when you escape the ' as \':
wikiPageUrl = #"http://it.wikipedia.org/wiki/\'Abd_Allāh_al-Sallāl";
if (wikiPageUrl.Contains("wikipedia.org/wiki/"))
{
"contains".Dump();
int i = wikiPageUrl.LastIndexOf("wikipedia.org/wiki/");
Console.WriteLine(i);
}
figure out what you want to do (remove the ', escape it, or dig deeper :) ).

Trimming strings between two points

I have a string as follows:
ListViewSubItem: {Debian6/Debian6.vmx }
What would be the most efficient way of getting the following output:
Debian6/Debian6.vmx
int beginidx = haystack.IndexOf('{');
string needle = haystack.SubString(beginidx,
haystack.IndexOf('}') - beginidx + 1).Trim();
string result = Regex.Match("ListViewSubItem: {Debian6/Debian6.vmx }", #"(?<={)(.+?)(?=})").Value;
You can use a regex:
\{\s*(.*)\s*\}
The desired string will be in the first captured group (Match.Groups[1]).
Example:
string output = Regex.Match(input, #"\{\s*(.*?)\s*\}").Groups[1].Value;
As was pointed out, regexes are slower than plain string manipulation. If performance is an issue, and the string extraction is in a tight loop, then it may be better to use an optimized method. Otherwise, regex vs string is IMHO a matter of personal preference.
I'm going to guess you didn't generate that string yourself. So the question is, why parse it? This looks like the sort of question where you should take a step back and think of what you're trying to solve, and if another method might be cleaner.
I am not familiar with the ListViewSubItem class, but it seems like you have one of these and you have called ToString() on it. Now you are parsing the output of ToString() to see the model object your sub-item represents.
Does this not seem backwards to you? Maybe you should deal with the ListViewSubItem object itself (from a brief look at the documentation, what you want seems to be the Text property), rather than fiddling with what ToString() returns, which seems volatile and dependent on the implementation of that class's ToString() method (which, though likely not, could theoretically change from release to release). Not to mention corner cases like "what if the string contains the } character?"
For this in other reasons, as a general rule I think you should not have to deal with serialization when your data source is in memory the whole time.
So, in summary, if you have something like this:
ListViewSubItem item = /* ... */;
string s = item.ToString();
// TODO: parse {Debian} out of ListViewSubItem {Debian}
Why not this instead:
ListViewSubItem item = /* ... */;
string OS = item.Text;

C# Extracting a name from a string

I want to extract 'James\, Brown' from the string below but I don't always know what the name will be. The comma is causing me some difficuly so what would you suggest to extract James\, Brown?
OU=James\, Brown,OU=Test,DC=Internal,DC=Net
Thanks
A regex is likely your best approach
static string ParseName(string arg) {
var regex = new Regex(#"^OU=([a-zA-Z\\]+\,\s+[a-zA-Z\\]+)\,.*$");
var match = regex.Match(arg);
return match.Groups[1].Value;
}
You can use a regex:
string input = #"OU=James\, Brown,OU=Test,DC=Internal,DC=Net";
Match m = Regex.Match(input, "^OU=(.*?),OU=.*$");
Console.WriteLine(m.Groups[1].Value);
A quite brittle way to do this might be...
string name = #"OU=James\, Brown,OU=Test,DC=Internal,DC=Net";
string[] splitUp = name.Split("=".ToCharArray(),3);
string namePart = splitUp[1].Replace(",OU","");
Console.WriteLine(namePart);
I wouldn't necessarily advocate this method, but I've just come back from a departmental Christmas lyunch and my brain is not fully engaged yet.
I'd start off with a regex to split up the groups:
Regex rx = new Regex(#"(?<!\\),");
String test = "OU=James\\, Brown,OU=Test,DC=Internal,DC=Net";
String[] segments = rx.Split(test);
But from there I would split up the parameters in the array by splitting them up manually, so that you don't have to use a regex that depends on more than the separator character used. Since this looks like an LDAP query, it might not matter if you always look at params[0], but there is a chance that the name might be set as "CN=". You can cover both cases by just reading the query like this:
String name = segments[0].Split('=', 2)[1];
That looks suspiciously like an LDAP or Active Directory distinguished name formatted according to RFC 2253/4514.
Unless you're working with well known names and/or are okay with a fragile hackaround (like the regex solutions) - then you should start by reading the spec.
If you, like me, generally hate implementing code according to RFCs - then hope this guy did a better job following the spec than you would. At least he claims to be 2253 compliant.
If the slash is always there, I would look at potentially using RegEx to do the match, you can use a match group for the last and first names.
^OU=([a-zA-Z])\,\s([a-zA-Z])
That RegEx will match names that include characters only, you will need to refine it a bit for better matching for the non-standard names. Here is a RegEx tester to help you along the way if you go this route.
Replace \, with your own preferred magic string (perhaps & #44;), split on remaining commas or search til the first comma, then replace your magic string with a single comma.
i.e. Something like:
string originalStr = #"OU=James\, Brown,OU=Test,DC=Internal,DC=Net";
string replacedStr = originalStr.Replace("\,", ",");
string name = replacedStr.Substring(0, replacedStr.IndexOf(","));
Console.WriteLine(name.Replace(",", ","));
Assuming you're running in Windows, use PInvoke with DsUnquoteRdnValueW. For code, see my answer to another question: https://stackoverflow.com/a/11091804/628981
If the format is always the same:
string line = GetStringFromWherever();
int start = line.IndexOf("=") + 1;//+1 to get start of name
int end = line.IndexOf("OU=",start) -1; //-1 to remove comma
string name = line.Substring(start, end - start);
Forgive if syntax is not quite right - from memory. Obviously this is not very robust and fails if the format ever changes.

Categories

Resources