I want to make a program which takes a string you entered and turn it to different string, so if for example I input "Hello World" every char will turn into a string and the console will output something like "Alpha Beta Gamma Gamma Zeta Foxtrot Dona Rama Lana Zema" - making every char a word.
I tried doing it like this :
static string WordMap(string value)
{
char[] buffer = value.ToCharArray();
for (int i = 0; i < buffer.Length; i++)
{
if (letter = "a")
{
letter = ("Alpha");
}
//and so on
buffer[i] = letter;
}
return new string(buffer);
}
but I just can't get it to work.
can anyone give me a tip or point me at the right direction?
What you need is a Dictionary<char,string>
var words = new Dictionary<char, string>();
words.Add('a', "Alpha");
words.Add('b',"Beta");
...
string input = Console.ReadLine();
string[] contents = new string[input.Length];
for (int i = 0; i < input.Length; i++)
{
if (words.ContainsKey(input[i]))
{
contents[i] = words[input[i]];
}
}
string result = string.Join(" ", contents);
Or LINQ way:
var result = string.Join(" ", input.Where(words.ContainsKey).Select(c => words[c]));
First off, the buffer is a char array. Arrays have a fixed size and to expand them you need to create a new one. To overcome this cumbersome work, there is a StringBuilder class that does this automatically.
Secondly, if you keep these 'Alpha', 'Beta', ... strings in if statements you will have a very long piece of code. You can replace this by using a dictionary, or create it from a single string or text file.
To put this into practice:
class MyClass
{
static Dictionary<char, string> _map = new Dictionary<char, string>();
static MyClass()
{
_map.Add('a', "Alpha");
_map.Add('b', "Beta");
// etc
}
static string WordMap(string data)
{
var output = new StringBuilder();
foreach (char c in data)
{
if (_map.ContainsKey(c))
{
output.Append(_map[c]);
output.Append(' ');
}
}
return output.ToString();
}
}
Solution without a dictionary:
static string WordMap(string data)
{
const string words = "Alpha Beta Gamma Delta ...";
string[] wordMap = words.Split(' ');
var output = new StringBuilder();
foreach (char c in data)
{
int index = c - 'a';
if (index >= 0 && index < wordMap.Length)
{
output.Append(wordMap[index]);
output.Append(' ');
}
}
return output.ToString();
}
With LINQ and String.Join it's short and readable. Since you want to have a new word for special chards you need a word-map. I would use a Dictionary<char, string>:
static Dictionary<char, string> wordMap = new Dictionary<char, string>()
{
{'a', "Alpha"}, {'b', "Beta"},{'c', "Gamma"}, {'d', "Delta"}
};
static string WordMap(string value)
{
var strings = value
.Select(c =>
{
string word;
if(!wordMap.TryGetValue(c, out word))
word = c.ToString();
return word;
});
return string.Join("", strings);
}
Test:
string newString = WordMap("abcdeghj"); // AlphaBetaGammaDeltaeghj
Tips:
You don't have to create character array from string, you can easily access single characters in string by indexer:
char some = "123"[2];
When you use "" then you create string not char therefore you should use '' to create character for comparison:
if (some == 'a') Console.WriteLine("character is a, see how you compare chars!!!");
A good solution ...
string[] words = { "Alpha", "Beta", "C_word", "D_Word" }; // ....
string myPhrase = "aBC";
myPhrase.Replace(" ", string.Empty).ToList().ForEach(a =>
{
int asciiCode = (int)a;
/// ToUpper()
int index = asciiCode >= 97 ? asciiCode - 32 : asciiCode;
Console.WriteLine(words[index - 65]); // ascii --> 65-a , 66-b ...
});
One more variation of answer which contains not found option.
static Dictionary<char, string> Mapping =
new Dictionary<char, string>()
{ { 'a', "alpha" }, { 'b', "beta" }, { 'c', "gamma" }, { 'd', "zeta" } };
static void Main(string[] args)
{
string test = "abcx";
Console.WriteLine(string.Join(" ", test.Select(t => GetMapping(t))));
//output alpha beta gamma not found
Console.ReadKey();
}
static string GetMapping(char key)
{
if (Mapping.ContainsKey(key))
return Mapping.First(a => a.Key == key).Value;
else
return "not found";
}
Just declare a second variable where you build up your result.
And I think you have some syntax problems, you need to have "==" in a
condition, otherwise it's an assignment.
static string WordMap(string value)
{
string result = string.Empty;
char[] buffer = value.ToCharArray();
for (int i = 0; i < buffer.Length; i++)
{
if (letter == "a")
{
result += ("Alpha");
}
//and so on
}
return result;
}
But I would only do this that way, if this is "just for fun" code,
as it will not be very fast.
Building up the result like I did is slow, a better way would be
result = string.Concat(result, "(Alpha)");
And an even faster way is using a StringBuilder (s. documentation for that),
which offers you fast and convenient methods to deal with bigger strings.
Only downfall here is, that you need to know a little bit, how big the result
will be in characters, as you need to provide a starting dimension.
And here you should not start with simply 1, or 100. Each time, when the StringBuilder
is full, it creates a new bigger instance and copies the values, so multiple instances
of that will fill your memory, which can cause an out of memory exception,
when dealing with some ten thousands of characters.
But as said, for just for fun code, all of that does not matter...
And of course, you need to be aware, that if you do it like that, your result will
be in one straight line, no breaks. If you want line breaks add "\n" at the end of
the strings. Or add anything elese you need.
Regards,
Markus
Related
I want to mix 2 string in 1 randomly using foreach but I don't know how I delete the part I used on the string for the foreach like:
string s = "idontknow";
string sNew = "";
foreach(char ss in s){
s = s + ss;
ss.Delete(s); //don't exist
}
Full code here i'm trying to do:
do
{
if (state == 0)
{
for (int i = 0; random.Next(1, 5) > variable.Length; i++)
{
foreach (char ch in variable)
{
fullString = fullString + ch;
}
}
state++;
}
else if (state == 1)
{
for (int i = 0; random.Next(1, 5) > numbers.Length; i++)
{
foreach (char n in numbers)
{
fullString = fullString + n;
}
}
state--;
}
} while (variable.Length != 0 && numbers.Length != 0);
I'm pretty confident, that in your first code snippet, you are creating an infinite loop, since you are appending the used char back to the string while removing it from the first position.
Regarding your specification to shuffle two stings together, this code sample might do the job:
public static string ShuffleStrings(string s1, string s2){
List<char> charPool = new();
foreach (char c in s1) {
charPool.Add(c);
}
foreach (char c in s2) {
charPool.Add(c);
}
Random rand = new();
char[] output = new char[charPool.Count];
for(int i = 0; i < output.Length; i++) {
int randomIndex = rand.Next(0, charPool.Count);
output[i] = charPool[randomIndex];
charPool.RemoveAt(randomIndex);
}
return new string(output);
}
In case you just want to shuffle one string into another string, just use an empty string as the first or second parameter.
Example:
string shuffled = ShuffleStrings("TEST", "string");
Console.WriteLine(shuffled);
// Output:
// EgsTtSnrTi
There are possibly other solutions, which are much shorter, but I think this code is pretty easy to read and understand.
Concerning the performance, the code above should works both for small stings and large strings.
Since strings are immutable, each modify-operation on any string, e.g. "te" + "st" or "test".Replace("t", ""), will allocate and create a new string in the memory, which is - in a large scale - pretty bad.
For that very reason, I initialized a char array, which will then be filled.
Alternatively, you can use:
using System.Text;
StringBuilder sb = new();
// append each randomly picked char
sb.Append(c);
// Create a string from appended chars
sb.ToString();
And if your question was just how to remove the first char of a string:
string myStr = "Test";
foreach (char c in myStr) {
// do with c whatever you want
myStr = myStr[1..]; // assign a substring exluding first char (start at index 1)
Console.WriteLine($"c = {c}; myStr = {myStr}");
}
// Output:
// c = T; myStr = est
// c = e; myStr = st
// c = s; myStr = t
// c = t; myStr =
Whilst working on a simple project I was trying to come up with a very basic cryptographic system which will allow me to switch letters in a message to another pre-chosen letter. So far I have tried a few ways but have so far been unsuccessful in my attempts. (*NOTE: This is not for anything other then learning the basics of c# so randomness and security is not important in this case, I simply want to turn one letter into another for the sake of learning how to do it)
so first I started by defining some strings such as this
string a = "a";
string b = "b";
string c = "d";
..... //continues to string z = "z"
next I tried to create a new string based on the values that have been input in to a textbox called PlainTextBox and place them inside a separate textbox called ChangedTextBox. this code is triggered with a button click event.
string str = PlainTextBox.Text;
char[] array = str.ToCharArray();
array[int.Parse(a)] = 'x';
array[int.Parse(b)] = 'y';
array[int.Parse(c)] = 'z';
.......// continues to (z)
str = new string(array);
ChangedTextBox.Text = str;
but this code throws an exception because the input is not a valid integer. the basic Idea is that if the user types "abc" in the PlainTextBox and pushes a button, the ChangedTextBox should show "xyz" but should be inclusive of the whole text in PlainTextBox, switching every letter in the message to its chosen counterpart.
Besides the error I receive, this code seems very cumbersome and inefficient.
Is there a faster way to achieve this result?
Just for completeness I will also include information, that what you are doing is called Caesar cipher
You could define yourself a proper Dictionary
var mapping = new Dictionary<char, char>()
{
{ 'a', 'x' },
{ 'b', 'y' },
{ 'c', 'z' }
// other letters
}
in which you would assign every original letter the letter it should be converted to. Then you could use this dictionary
ChangedTextBox.Text = new string(PlainTextBox.Text.Select(letter => mapping[letter].ToArray());
You've chosen wrong collection type (array) for mapping; dictionary is much more convenient
private static Dictionary<char, char> m_Mapping = new Dictionary<char, char>() {
{'a', 'x'}, // a -> x
{'b', 'y'}, // b -> y
...
};
Then implement the encoding
// I'd rather used Linq
private static String Encode(String value) {
// Simplest: we don't check if the actual character can be mapped
return String.Concat(value.Select(c => m_Mapping[c]));
}
But your (amended) implementation is good enough:
private static String Encode(string str) {
char[] array = str.ToCharArray();
for (int i = 0; i < array.Length; ++i) {
// Simplest: we don't check if the actual character can be mapped
array[i] = m_Mapping[array[i]];
}
return new string(array);
}
Finally, add up UI:
ChangedTextBox.Text = Encode(PlainTextBox.Text);
Edit: in general case, when m_Mapping doesn't contain records for some characters (e.g. for new line \n) and so we want to preserve these characters intact we can't use direct m_Mapping[...] but should implement, say, EncodeChar.
private static char EncodeChar(char value) {
char result;
if (m_Mapping.TryGetValue(value, out result))
return result;
else
return value;
}
And put EncodeChar(...) instead of m_Mapping[...]
private static String Encode(String value) {
return String.Concat(value.Select(EncodeChar(c)));
}
Your version
private static String Encode(string str) {
char[] array = str.ToCharArray();
for (int i = 0; i < array.Length; ++i) {
array[i] = EncodeChar(array[i]);
return new string(array);
}
Probably the best solution is using a Dictionary, as other answers had said. But if I understand what you want, you want to just change one letter by that letter plus an offset in a kind of "circular" way. This solution would do something like that (with "abcd" as input it would return "xyza"):
string input = "abcd";
char startChar = 'x';
char lastChar = 'z';
char firstChar = 'a';
byte[] asciiBytes=Encoding.ASCII.GetBytes(input);
byte[] encriptedByteArray = new byte[asciiBytes.Length];
int val = (int)startChar-(int)firstChar;
int i = 0;
foreach(byte b in asciiBytes)
{
var a=b + val;
if (a>(int)lastChar)
{
a = firstChar+(a-lastChar)-1;
}
encriptedByteArray[i] = (byte)a;
i++;
}
string encriptedArray = System.Text.Encoding.UTF8.GetString(encriptedByteArray);
With this solution you can change the offsety easily (changing startChar). It has room for improvement though, for example it only works on lower letters from a-z, it could be changed to be more extensive.
int.Parse(a)
will of course throw InvalidCastException because a is a string as you've defined it
string a = "a";
I have * strings and I want the only unknown character and its position. For instance, I want character 'a' or 'b' or 'c' or anything (unknown) and its positions in the strings below:
1) "******a***" // I want 'a'
2) "b****" // I want 'b'
3) "*******c" // I want 'c'
The strings are always have * characters. Sometimes I have 'a', sometimes 'n', sometimes 'x', and so on. I don't know what character coming inside stars (*).
How can I do this in C#?
Try this:
// the string
var str = "******a***";
// the character
var chr = str.Single(x => x != '*');
// the position
var pos = str.IndexOf(chr);
Please be aware that Single will throw an exception in case nothing found. Use it only when you're certain there's one (and only one) unknown character. If you're not sure use SingleOrDefault and check for Char.MinValue.
With regex
public static char GetUnknownChar(string s, char knownChar)
{
const string mask = "[^{0}]";
var match = Regex.Match(s, string.Format(mask, knownChar));
return match.Value[0];
}
with LINQ:
public static char GetUnknownChar(string s, char knownChar)
{
return s.First(c => c != knownChar);
}
with multiple known chars:
public static char GetUnknownChar(string s, IEnumerable<char> knownChars)
{
var knownSet = new HashSet<char>(knownChars);
return s.First(knownSet.Contains);
}
You can use String.IndexOf to get the positions
How about this:
string foo = "**x*";
char[] knownChars = new char[]{'*','!','?'};
int i = 0;
for (; i < foo.Length; i++)
if (knownChars.Contains(foo[i]))
break;
Here's a more primitive way:
public class Program
{
public static void Main(string[] args)
{
FindUnknown("******a***");
FindUnknown("b****");
FindUnknown("*******c");
}
private static void FindUnknown(string myString)
{
var tag = '*';
var unknown = ' ';
var unknownIndex = -1;
var currentIndex = 0;
foreach (var character in myString)
{
if (character != tag)
{
unknown = character;
unknownIndex = currentIndex;
break;
}
currentIndex++;
}
Console.WriteLine(String.Format("Unknown character: {0}", unknown));
Console.WriteLine(String.Format("Unknown character index: {0}", unknownIndex));
}
}
Here's a relatively novel yet understandable LINQ solution:
var strings = new[] {"******a***", "b****", "*******c"};
foreach (var str in strings)
{
var prefix = str.TakeWhile(c => c.Equals('*'));
var postPrefixIndex = prefix.Count();
var unknownCharacter = str[postPrefixIndex];
Console.WriteLine("{0} at {1}", unknownCharacter, postPrefixIndex);
}
// a at 6
// b at 0
// c at 7
// Press any key to continue . . .
I have a string on which I need to do some replacements. I have a Dictionary<string, string> where I have search-replace pairs defined. I have created following extension methods to perform this operation:
public static string Replace(this string str, Dictionary<string, string> dict)
{
StringBuilder sb = new StringBuilder(str);
return sb.Replace(dict).ToString();
}
public static StringBuild Replace(this StringBuilder sb,
Dictionary<string, string> dict)
{
foreach (KeyValuePair<string, string> replacement in dict)
{
sb.Replace(replacement.Key, replacement.Value);
}
return sb;
}
Is there a better way of doing that?
If the data is tokenized (i.e. "Dear $name$, as of $date$ your balance is $amount$"), then a Regex can be useful:
static readonly Regex re = new Regex(#"\$(\w+)\$", RegexOptions.Compiled);
static void Main() {
string input = #"Dear $name$, as of $date$ your balance is $amount$";
var args = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase) {
{"name", "Mr Smith"},
{"date", "05 Aug 2009"},
{"amount", "GBP200"}
};
string output = re.Replace(input, match => args[match.Groups[1].Value]);
}
However, without something like this, I expect that your Replace loop is probably about as much as you can do, without going to extreme lengths. If it isn't tokenized, perhaps profile it; is the Replace actually a problem?
Do this with Linq:
var newstr = dict.Aggregate(str, (current, value) =>
current.Replace(value.Key, value.Value));
dict is your search-replace pairs defined Dictionary object.
str is your string which you need to do some replacements with.
Seems reasonable to me, except for one thing: it's order-sensitive. For instance, take an input string of "$x $y" and a replacement dictionary of:
"$x" => "$y"
"$y" => "foo"
The results of the replacement are either "foo foo" or "$y foo" depending on which replacement is performed first.
You could control the ordering using a List<KeyValuePair<string, string>> instead. The alternative is to walk through the string making sure you don't consume the replacements in further replace operations. That's likely to be a lot harder though.
Here's a lightly re-factored version of #Marc's great answer, to make the functionality available as an extension method to Regex:
static void Main()
{
string input = #"Dear $name$, as of $date$ your balance is $amount$";
var args = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
args.Add("name", "Mr Smith");
args.Add("date", "05 Aug 2009");
args.Add("amount", "GBP200");
Regex re = new Regex(#"\$(\w+)\$", RegexOptions.Compiled);
string output = re.replaceTokens(input, args);
// spot the LinqPad user // output.Dump();
}
public static class ReplaceTokensUsingDictionary
{
public static string replaceTokens(this Regex re, string input, IDictionary<string, string> args)
{
return re.Replace(input, match => args[match.Groups[1].Value]);
}
}
when using Marc Gravell's RegEx solution, first check if a token is available using i.e. ContainsKey, this to prevent KeyNotFoundException errors :
string output = re.Replace(zpl, match => { return args.ContainsKey(match.Groups[1].Value) ? arg[match.Groups[1].Value] : match.Value; });
when using the following slightly modified sample code (1st parameter has different name):
var args = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase)
{
{"nameWRONG", "Mr Smith"},
{"date", "05 Aug 2009"},
{"AMOUNT", "GBP200"}
};
this produces the following:
"Dear $name$, as of 05 Aug 2009 your balance is GBP200"
The correct tool for this particular task is Mustache, a simple standard logicless templating language.
There are many implementations. For this example, I'm going to use stubble:
var stubble = new StubbleBuilder().Build();
var dataHash = new Dictionary<string, Object>()
{
{"Foo","My Foo Example"},
{"Bar",5}
};
var output = stubble.Render(
"Hey, watch me replace this: {{Foo}} ... with example text. Also {{bar}} is 5"
, dataHash
);
Here you are:
public static class StringExm
{
public static String ReplaceAll(this String str, KeyValuePair<String, String>[] map)
{
if (String.IsNullOrEmpty(str))
return str;
StringBuilder result = new StringBuilder(str.Length);
StringBuilder word = new StringBuilder(str.Length);
Int32[] indices = new Int32[map.Length];
for (Int32 characterIndex = 0; characterIndex < str.Length; characterIndex++)
{
Char c = str[characterIndex];
word.Append(c);
for (var i = 0; i < map.Length; i++)
{
String old = map[i].Key;
if (word.Length - 1 != indices[i])
continue;
if (old.Length == word.Length && old[word.Length - 1] == c)
{
indices[i] = -old.Length;
continue;
}
if (old.Length > word.Length && old[word.Length - 1] == c)
{
indices[i]++;
continue;
}
indices[i] = 0;
}
Int32 length = 0, index = -1;
Boolean exists = false;
for (int i = 0; i < indices.Length; i++)
{
if (indices[i] > 0)
{
exists = true;
break;
}
if (-indices[i] > length)
{
length = -indices[i];
index = i;
}
}
if (exists)
continue;
if (index >= 0)
{
String value = map[index].Value;
word.Remove(0, length);
result.Append(value);
if (word.Length > 0)
{
characterIndex -= word.Length;
word.Length = 0;
}
}
result.Append(word);
word.Length = 0;
for (int i = 0; i < indices.Length; i++)
indices[i] = 0;
}
if (word.Length > 0)
result.Append(word);
return result.ToString();
}
}
Why not just check whether such key exists?
if exists, then remove the pair, otherwise, skip this step;
add the same key, but now with newly desired value.
// say, you have the following collection
var fields = new Dictionary<string, string>();
fields.Add("key1", "value1");
fields.Add("key2", "value2");
fields.Add("key3", "value3");
// now, you want to add a pair "key2"/"value4"
// or replace current value of "key2" with "value4"
if (fields.ContainsKey("key2"))
{
fields.Remove("key2");
}
fields.Add("key2", "value4");
I would like to split a string with delimiters but keep the delimiters in the result.
How would I do this in C#?
If the split chars were ,, ., and ;, I'd try:
using System.Text.RegularExpressions;
...
string[] parts = Regex.Split(originalString, #"(?<=[.,;])")
(?<=PATTERN) is positive look-behind for PATTERN. It should match at any place where the preceding text fits PATTERN so there should be a match (and a split) after each occurrence of any of the characters.
If you want the delimiter to be its "own split", you can use Regex.Split e.g.:
string input = "plum-pear";
string pattern = "(-)";
string[] substrings = Regex.Split(input, pattern); // Split on hyphens
foreach (string match in substrings)
{
Console.WriteLine("'{0}'", match);
}
// The method writes the following to the console:
// 'plum'
// '-'
// 'pear'
So if you are looking for splitting a mathematical formula, you can use the following Regex
#"([*()\^\/]|(?<!E)[\+\-])"
This will ensure you can also use constants like 1E-02 and avoid having them split into 1E, - and 02
So:
Regex.Split("10E-02*x+sin(x)^2", #"([*()\^\/]|(?<!E)[\+\-])")
Yields:
10E-02
*
x
+
sin
(
x
)
^
2
Building off from BFree's answer, I had the same goal, but I wanted to split on an array of characters similar to the original Split method, and I also have multiple splits per string:
public static IEnumerable<string> SplitAndKeep(this string s, char[] delims)
{
int start = 0, index;
while ((index = s.IndexOfAny(delims, start)) != -1)
{
if(index-start > 0)
yield return s.Substring(start, index - start);
yield return s.Substring(index, 1);
start = index + 1;
}
if (start < s.Length)
{
yield return s.Substring(start);
}
}
Just in case anyone wants this answer aswell...
Instead of string[] parts = Regex.Split(originalString, #"(?<=[.,;])") you could use string[] parts = Regex.Split(originalString, #"(?=yourmatch)") where yourmatch is whatever your separator is.
Supposing the original string was
777- cat
777 - dog
777 - mouse
777 - rat
777 - wolf
Regex.Split(originalString, #"(?=777)") would return
777 - cat
777 - dog
and so on
This version does not use LINQ or Regex and so it's probably relatively efficient. I think it might be easier to use than the Regex because you don't have to worry about escaping special delimiters. It returns an IList<string> which is more efficient than always converting to an array. It's an extension method, which is convenient. You can pass in the delimiters as either an array or as multiple parameters.
/// <summary>
/// Splits the given string into a list of substrings, while outputting the splitting
/// delimiters (each in its own string) as well. It's just like String.Split() except
/// the delimiters are preserved. No empty strings are output.</summary>
/// <param name="s">String to parse. Can be null or empty.</param>
/// <param name="delimiters">The delimiting characters. Can be an empty array.</param>
/// <returns></returns>
public static IList<string> SplitAndKeepDelimiters(this string s, params char[] delimiters)
{
var parts = new List<string>();
if (!string.IsNullOrEmpty(s))
{
int iFirst = 0;
do
{
int iLast = s.IndexOfAny(delimiters, iFirst);
if (iLast >= 0)
{
if (iLast > iFirst)
parts.Add(s.Substring(iFirst, iLast - iFirst)); //part before the delimiter
parts.Add(new string(s[iLast], 1));//the delimiter
iFirst = iLast + 1;
continue;
}
//No delimiters were found, but at least one character remains. Add the rest and stop.
parts.Add(s.Substring(iFirst, s.Length - iFirst));
break;
} while (iFirst < s.Length);
}
return parts;
}
Some unit tests:
text = "[a link|http://www.google.com]";
result = text.SplitAndKeepDelimiters('[', '|', ']');
Assert.IsTrue(result.Count == 5);
Assert.AreEqual(result[0], "[");
Assert.AreEqual(result[1], "a link");
Assert.AreEqual(result[2], "|");
Assert.AreEqual(result[3], "http://www.google.com");
Assert.AreEqual(result[4], "]");
A lot of answers to this! One I knocked up to split by various strings (the original answer caters for just characters i.e. length of 1). This hasn't been fully tested.
public static IEnumerable<string> SplitAndKeep(string s, params string[] delims)
{
var rows = new List<string>() { s };
foreach (string delim in delims)//delimiter counter
{
for (int i = 0; i < rows.Count; i++)//row counter
{
int index = rows[i].IndexOf(delim);
if (index > -1
&& rows[i].Length > index + 1)
{
string leftPart = rows[i].Substring(0, index + delim.Length);
string rightPart = rows[i].Substring(index + delim.Length);
rows[i] = leftPart;
rows.Insert(i + 1, rightPart);
}
}
}
return rows;
}
This seems to work, but its not been tested much.
public static string[] SplitAndKeepSeparators(string value, char[] separators, StringSplitOptions splitOptions)
{
List<string> splitValues = new List<string>();
int itemStart = 0;
for (int pos = 0; pos < value.Length; pos++)
{
for (int sepIndex = 0; sepIndex < separators.Length; sepIndex++)
{
if (separators[sepIndex] == value[pos])
{
// add the section of string before the separator
// (unless its empty and we are discarding empty sections)
if (itemStart != pos || splitOptions == StringSplitOptions.None)
{
splitValues.Add(value.Substring(itemStart, pos - itemStart));
}
itemStart = pos + 1;
// add the separator
splitValues.Add(separators[sepIndex].ToString());
break;
}
}
}
// add anything after the final separator
// (unless its empty and we are discarding empty sections)
if (itemStart != value.Length || splitOptions == StringSplitOptions.None)
{
splitValues.Add(value.Substring(itemStart, value.Length - itemStart));
}
return splitValues.ToArray();
}
Recently I wrote an extension method do to this:
public static class StringExtensions
{
public static IEnumerable<string> SplitAndKeep(this string s, string seperator)
{
string[] obj = s.Split(new string[] { seperator }, StringSplitOptions.None);
for (int i = 0; i < obj.Length; i++)
{
string result = i == obj.Length - 1 ? obj[i] : obj[i] + seperator;
yield return result;
}
}
}
I'd say the easiest way to accomplish this (except for the argument Hans Kesting brought up) is to split the string the regular way, then iterate over the array and add the delimiter to every element but the last.
To avoid adding character to new line try this :
string[] substrings = Regex.Split(input,#"(?<=[-])");
result = originalString.Split(separator);
for(int i = 0; i < result.Length - 1; i++)
result[i] += separator;
(EDIT - this is a bad answer - I misread his question and didn't see that he was splitting by multiple characters.)
(EDIT - a correct LINQ version is awkward, since the separator shouldn't get concatenated onto the final string in the split array.)
Iterate through the string character by character (which is what regex does anyway.
When you find a splitter, then spin off a substring.
pseudo code
int hold, counter;
List<String> afterSplit;
string toSplit
for(hold = 0, counter = 0; counter < toSplit.Length; counter++)
{
if(toSplit[counter] = /*split charaters*/)
{
afterSplit.Add(toSplit.Substring(hold, counter));
hold = counter;
}
}
That's sort of C# but not really. Obviously, choose the appropriate function names.
Also, I think there might be an off-by-1 error in there.
But that will do what you're asking.
veggerby's answer modified to
have no string items in the list
have fixed string as delimiter like "ab" instead of single character
var delimiter = "ab";
var text = "ab33ab9ab"
var parts = Regex.Split(text, $#"({Regex.Escape(delimiter)})")
.Where(p => p != string.Empty)
.ToList();
// parts = "ab", "33", "ab", "9", "ab"
The Regex.Escape() is there just in case your delimiter contains characters which regex interprets as special pattern commands (like *, () and thus have to be escaped.
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
string input = #"This;is:a.test";
char sep0 = ';', sep1 = ':', sep2 = '.';
string pattern = string.Format("[{0}{1}{2}]|[^{0}{1}{2}]+", sep0, sep1, sep2);
Regex regex = new Regex(pattern);
MatchCollection matches = regex.Matches(input);
List<string> parts=new List<string>();
foreach (Match match in matches)
{
parts.Add(match.ToString());
}
}
}
}
I wanted to do a multiline string like this but needed to keep the line breaks so I did this
string x =
#"line 1 {0}
line 2 {1}
";
foreach(var line in string.Format(x, "one", "two")
.Split("\n")
.Select(x => x.Contains('\r') ? x + '\n' : x)
.AsEnumerable()
) {
Console.Write(line);
}
yields
line 1 one
line 2 two
I came across same problem but with multiple delimiters. Here's my solution:
public static string[] SplitLeft(this string #this, char[] delimiters, int count)
{
var splits = new List<string>();
int next = -1;
while (splits.Count + 1 < count && (next = #this.IndexOfAny(delimiters, next + 1)) >= 0)
{
splits.Add(#this.Substring(0, next));
#this = new string(#this.Skip(next).ToArray());
}
splits.Add(#this);
return splits.ToArray();
}
Sample with separating CamelCase variable names:
var variableSplit = variableName.SplitLeft(
Enumerable.Range('A', 26).Select(i => (char)i).ToArray());
I wrote this code to split and keep delimiters:
private static string[] SplitKeepDelimiters(string toSplit, char[] delimiters, StringSplitOptions splitOptions = StringSplitOptions.None)
{
var tokens = new List<string>();
int idx = 0;
for (int i = 0; i < toSplit.Length; ++i)
{
if (delimiters.Contains(toSplit[i]))
{
tokens.Add(toSplit.Substring(idx, i - idx)); // token found
tokens.Add(toSplit[i].ToString()); // delimiter
idx = i + 1; // start idx for the next token
}
}
// last token
tokens.Add(toSplit.Substring(idx));
if (splitOptions == StringSplitOptions.RemoveEmptyEntries)
{
tokens = tokens.Where(token => token.Length > 0).ToList();
}
return tokens.ToArray();
}
Usage example:
string toSplit = "AAA,BBB,CCC;DD;,EE,";
char[] delimiters = new char[] {',', ';'};
string[] tokens = SplitKeepDelimiters(toSplit, delimiters, StringSplitOptions.RemoveEmptyEntries);
foreach (var token in tokens)
{
Console.WriteLine(token);
}