Replacing word in different ways - c#

For first I have phrase like ice z comp. In the output I need List<string> which have this word with dashes for example like this: ice-z comp, ice z-comp, ice-z-comp
For now I have this:
var synonymFromSynonym = new List<string>();
var countOfSpaces = word.Count(Char.IsWhiteSpace);
for (int x = 0; x < countOfSpaces; x++)
{
// What here for my output ?
}

The problem can be solved using the following implementation. First, the input is split into the individual words.
var Words = word.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
Then, the words are assembled again, considering two cases, namely whether the connection is done with a space or with a hyphen. This can be done recursively as follows, where a call like GenerateResult("", 0 ) would start the calculation.
List<string> Result = new List<string>();
void GenerateResult(string Intermediate, int Position)
{
if (Position == Words.Count() - 1) // base case, nothing to append
{
Result.Add(Intermadiate);
}
else
{
GenerateResult( Intermediate + " ", Position + 1 ); // space case
GenerateResult( Intermediate + "-", Position + 1 ); // hyphen case
}
}

Related

Add all chars of a string to a list but merge numbers to one entry C#

You have given a string like "(32+5)*20" and you need to create a list with all chars, but need to merge the numbers to one entry like "32" and "20". How to do it?
I have already tried
string formel = "(32+5)*20";
char[] zeichen = formel.ToCharArray();
var liste = new List<string>();
for(int i = 0; i < zeichen.Length ; i++)
{
if(Char.IsNumber(zeichen[i]) && formel.Substring(i, i + 1).Any(char.IsNumber))
{
liste.Add(zeichen[i].ToString() + zeichen[i+1].ToString());
}
else
{
liste.Add(zeichen[i].ToString());
}
}
You could use regex to return the groups of numbers:
var formel = "(32+5)*20";
var pattern = "([0-9]+)";
var match = Regex.Match(formel, pattern);
and then use
match.Groups
The list as posted will still contain the same number of elements as the string did, since you added to it on every loop iteration. To "merge" into one entry, you need to not add on some iterations, but instead add that digit to the previous number.
This is easier if you check if the previous item was a number, rather than the next.
for (int i = 0; i < zeichen.Length; i++)
{
if (i > 0 && Char.IsNumber(zeichen[i]) && Char.IsNumber(zeichen[i - 1])) // if got a number, and had a number before
{
// Then dont Add to the list, instead add to the number/string
liste[list.Count - 1] += zeichen[i];
//liste.Add(zeichen[i].ToString() + zeichen[i + 1].ToString());
}
else
liste.Add(zeichen[i].ToString());
}
// "(", "32", "+", "5", ")", "*", "20"
You can do it by using Regex and LINQ
var dateText = "(32 + 5) * 20";
string splitPattern = #"[^\d]";
var result = Regex.Split(dateText, splitPattern).Where(x=>x!="");
I hope this will help you #Janic

Alternatively upper- and lowercase words in a string

I use Visual Studio 2010 ver.
I have array strings [] = { "eat and go"};
I display it with foreach
I wanna convert strings like this : EAT and GO
Here my code:
Console.Write( myString.First().ToString().ToUpper() + String.Join("",myString].Skip(1)).ToLower()+ "\n");
But the output is : Eat and go . :D lol
Could you help me? I would appreciate it. Thanks
While .ToUpper() will convert a string to its upper case equivalent, calling .First() on a string object actually returns the first element of the string (since it's effectively a char[] under the hood). First() is actually exposed as a LINQ extension method and works on any collection type.
As with many string handling functions, there are a number of ways to handle it, and this is my approach. Obviously you'll need to validate value to ensure it's being given a long enough string.
using System.Text;
public string CapitalizeFirstAndLast(string value)
{
string[] words = value.Split(' '); // break into individual words
StringBuilder result = new StringBuilder();
// Add the first word capitalized
result.Append(words[0].ToUpper());
// Add everything else
for (int i = 1; i < words.Length - 1; i++)
result.Append(words[i]);
// Add the last word capitalized
result.Append(words[words.Length - 1].ToUpper());
return result.ToString();
}
If it's always gonna be a 3 words string, the you can simply do it like this:
string[] mystring = {"eat and go", "fast and slow"};
foreach (var s in mystring)
{
string[] toUpperLower = s.Split(' ');
Console.Write(toUpperLower.First().ToUpper() + " " + toUpperLower[1].ToLower() +" " + toUpperLower.Last().ToUpper());
}
If you want to continuously alternate, you can do the following:
private static string alternateCase( string phrase )
{
String[] words = phrase.split(" ");
StringBuilder builder = new StringBuilder();
//create a flag that keeps track of the case change
book upperToggle = true;
//loops through the words
for(into i = 0; i < words.length; i++)
{
if(upperToggle)
//converts to upper if flag is true
words[i] = words[i].ToUpper();
else
//converts to lower if flag is false
words[i] = words[i].ToLower();
upperToggle = !upperToggle;
//adds the words to the string builder
builder.append(words[i]);
}
//returns the new string
return builder.ToString();
}
Quickie using ScriptCS:
scriptcs (ctrl-c to exit)
> var input = "Eat and go";
> var words = input.Split(' ');
> var result = string.Join(" ", words.Select((s, i) => i % 2 == 0 ? s.ToUpperInvariant() : s.ToLowerInvariant()));
> result
"EAT and GO"

Find and replace text in a string using C#

Anyone know how I would find & replace text in a string? Basically I have two strings:
string firstS = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDABQODxIPDRQSERIXFhQYHzMhHxwcHz8tLyUzSkFOTUlBSEZSXHZkUldvWEZIZoxob3p9hIWET2ORm4+AmnaBhH//2wBDARYXFx8bHzwhITx/VEhUf39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3//";
string secondS = "abcdefg2wBDABQODxIPDRQSERIXFh/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/abcdefg";
I want to search firstS to see if it contains any sequence of characters that's in secondS and then replace it. It also needs to be replaced with the number of replaced characters in squared brackets:
[NUMBER-OF-CHARACTERS-REPLACED]
For example, because firstS and secondS both contain "2wBDABQODxIPDRQSERIXFh" and "/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/" they would need to be replaced. So then firstS becomes:
string firstS = "/9j/4AAQSkZJRgABAQEAYABgAAD/[22]QYHzMhHxwcHz8tLyUzSkFOTUlBSEZSXHZkUldvWEZIZoxob3p9hIWET2ORm4+AmnaBhH//2wBDARYXFx8bHzwhITx/VEhUf39[61]f3//";
Hope that makes sense. I think I could do this with Regex, but I don't like the inefficiency of it. Does anyone know of another, faster way?
Does anyone know of another, faster way?
Yes, this problem actually has a proper name. It is called the Longest Common Substring, and it has a reasonably fast solution.
Here is an implementation on ideone. It finds and replaces all common substrings of ten characters or longer.
// This comes straight from Wikipedia article linked above:
private static string FindLcs(string s, string t) {
var L = new int[s.Length, t.Length];
var z = 0;
var ret = new StringBuilder();
for (var i = 0 ; i != s.Length ; i++) {
for (var j = 0 ; j != t.Length ; j++) {
if (s[i] == t[j]) {
if (i == 0 || j == 0) {
L[i,j] = 1;
} else {
L[i,j] = L[i-1,j-1] + 1;
}
if (L[i,j] > z) {
z = L[i,j];
ret = new StringBuilder();
}
if (L[i,j] == z) {
ret.Append(s.Substring( i-z+1, z));
}
} else {
L[i,j]=0;
}
}
}
return ret.ToString();
}
// With the LCS in hand, building the answer is easy
public static string CutLcs(string s, string t) {
for (;;) {
var lcs = FindLcs(s, t);
if (lcs.Length < 10) break;
s = s.Replace(lcs, string.Format("[{0}]", lcs.Length));
}
return s;
}
You need to be very careful between "Longest common substring and "longest common subsequence"
For Substring: http://en.wikipedia.org/wiki/Longest_common_substring_problem
For SubSequence: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
I would suggest you to also see few videos on youtube on these two topics
http://www.youtube.com/results?search_query=longest+common+substring&oq=longest+common+substring&gs_l=youtube.3..0.3834.10362.0.10546.28.17.2.9.9.2.225.1425.11j3j3.17.0...0.0...1ac.lSrzx8rr1kQ
http://www.youtube.com/results?search_query=longest+common+subsequence&oq=longest+common+s&gs_l=youtube.3.0.0l6.2968.7905.0.9132.20.14.2.4.4.0.224.2038.5j2j7.14.0...0.0...1ac.4CYZ1x50zpc
you can find c# implementation of longest common subsequence here:
http://www.alexandre-gomes.com/?p=177
http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Longest_common_subsequence
I have a similar issue, but for word occurrences! so, I hope this can help. I used SortedDictionary and a binary search tree
/* Application counts the number of occurrences of each word in a string
and stores them in a generic sorted dictionary. */
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class SortedDictionaryTest
{
public static void Main( string[] args )
{
// create sorted dictionary
SortedDictionary< string, int > dictionary = CollectWords();
// display sorted dictionary content
DisplayDictionary( dictionary );
}
// create sorted dictionary
private static SortedDictionary< string, int > CollectWords()
{
// create a new sorted dictionary
SortedDictionary< string, int > dictionary =
new SortedDictionary< string, int >();
Console.WriteLine( "Enter a string: " ); // prompt for user input
string input = Console.ReadLine();
// split input text into tokens
string[] words = Regex.Split( input, #"\s+" );
// processing input words
foreach ( var word in words )
{
string wordKey = word.ToLower(); // get word in lowercase
// if the dictionary contains the word
if ( dictionary.ContainsKey( wordKey ) )
{
++dictionary[ wordKey ];
}
else
// add new word with a count of 1 to the dictionary
dictionary.Add( wordKey, 1 );
}
return dictionary;
}
// display dictionary content
private static void DisplayDictionary< K, V >(
SortedDictionary< K, V > dictionary )
{
Console.WriteLine( "\nSorted dictionary contains:\n{0,-12}{1,-12}",
"Key:", "Value:" );
/* generate output for each key in the sorted dictionary
by iterating through the Keys property with a foreach statement*/
foreach ( K key in dictionary.Keys )
Console.WriteLine( "{0,- 12}{1,-12}", key, dictionary[ key ] );
Console.WriteLine( "\nsize: {0}", dictionary.Count );
}
}
This is probably dog slow, but if you're willing to incur some technical debt and need something now for prototyping, you could use LINQ.
string firstS = "123abc";
string secondS = "456cdeabc123";
int minLength = 3;
var result =
from subStrCount in Enumerable.Range(0, firstS.Length)
where firstS.Length - subStrCount >= 3
let subStr = firstS.Substring(subStrCount, 3)
where secondS.Contains(subStr)
select secondS.Replace(subStr, "[" + subStr.Length + "]");
Results in
456cdeabc[3]
456cde[3]123

C# - Read/Copy/Replace Lines In Text

I have a text file that I am opening up and it is in a similar format to this:
10 SOME TEXT
20 T A40
B B5, C45, D48
30 B E25
40 B F17, G18
60 T H20, I23,
B J6, K7, L8, M9, N10, O11, P12,
Q31, R32, S33, T34, U35, V36,
W37, X38, Y39
100 T Z65
360 B A1, B4, C5, D6, E7, F10
2000 T SOME TEXT
423 TEXT
With this text I need to be able to read it and replace values accordingly. If a ReadLine begins with a number (ie, 10, 20, 30, 40, 60, 100, 360, 2000, 423) I need to to check if there is a T, B, or text after it. The only case that I need to change/reformat the lines when they come in and output them differently.
Example: 10 is fine except for I would like to add zeros in front of every number to make them 4 digits long (ie, 10 turns to 0010, 360 turns to 0360, 2000 stays the same). When the string "B B5, C45, D48" is read (this is the third line in the text) I need to change it to say "20A B5, C45, D48". I need to grab the number above the "B" and concat it to the "B" and replace the "B" with an "A". If instead of a "B" there is a "T" I simply need to remove the "T". Also, if a line does not start with a number or a "B" (ie, Q31 or W37) I need to concat that line with the previous line.
So after the changes take place it should look like this:
0010 SOME TEXT
0020 A40
0020A B5, C45, D48
0030A E25
0040A F17, G18
0060 H20, I23,
0060A J6, K7, L8, M9, N10, O11, P12, Q31, R32, S33, T34, U35, V36, W37, X38, Y39
0100 Z65
0360A A1, B4, C5, D6, E7, F10
2000 SOME TEXT
0423 TEXT
I am currently trying to use Regex to do this but I have been told that there is an easier way to do this and I am not sure how. So far I have been able to add the zeros in front of the numbers. Also, my code is adding an "A" to the end of everything as well as keeping the original number on the next line and I am not grabbing the lines that begin with anything but a digit.
This is what my current output is turning out to look like:
0010A
0010
0020A
0020
0030A
0030
0060A
0060
0100A
0100
0360A
0360
2000
2000
0423A
0423
I am obviously doing something wrong using Regex.
Here is my current code:
private void openRefsButton_Click(object sender, EventArgs e)
{
// Initialize the OpenFileDialog to specify the .txt extension as well as
// its intial directory for the file.
openRefs.DefaultExt = "*.txt";
openRefs.Filter = ".txt Files|*.txt";
openRefs.InitialDirectory = "C:\\";
openRefs.RestoreDirectory = true;
try
{
// Open the contents of the file into the originalTextRichTextBox.
if (openRefs.ShowDialog() == DialogResult.OK && openRefs.FileName.Length > 0)
refsTextRichTextBox.LoadFile(openRefs.FileName, RichTextBoxStreamType.PlainText);
// Throws a FileNotFoundException otherwise.
else
throw new FileNotFoundException();
StreamReader refsInput = File.OpenText(openRefs.FileName);
string regExpression = #"^[\d]+";
string findNewBottomRegex = #"^B\s";
StringBuilder buildNumberText = new StringBuilder();
StringBuilder formatMatchText = new StringBuilder();
foreach (string allLines in File.ReadAllLines(openRefs.FileName))
{
Match newBottomMatch = Regex.Match(allLines, findNewBottomRegex);
Match numberStartMatch = Regex.Match(allLines, regExpression);
int counter = 0;
if (counter < numberStartMatch.Length)
{
if (numberStartMatch.Value.Length == 2)
{
if (refsTextRichTextBox.Text.Contains(newBottomMatch.ToString()))
{
finalTextRichTextBox.AppendText("00" + numberStartMatch + "A\n");
}
finalTextRichTextBox.AppendText("00" + numberStartMatch + "\n");
}
else if (numberStartMatch.Value.Length == 3)
{
if (refsTextRichTextBox.Text.Contains(newBottomMatch.ToString()))
{
finalTextRichTextBox.AppendText("0" + numberStartMatch + "A\n");
}
finalTextRichTextBox.AppendText("0" + numberStartMatch + "\n");
}
else
{
if (refsTextRichTextBox.Text.Contains(newBottomMatch.ToString()))
{
finalTextRichTextBox.AppendText(numberStartMatch + "A\n");
}
finalTextRichTextBox.AppendText(numberStartMatch + "\n");
}
counter++;
}
}
}
// Catches an exception if the file was not opened.
catch (Exception)
{
MessageBox.Show("There was not a specified file path.", "Path Not Found Error",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
}
QUESTION(S):
What is a better way to go about doing this task?
Are there any recommendations on changing my code to be more efficient and cleaner?
How do I properly split each line into number, T/B, A40 when every line is not the same?
After the lines are properly split, how do I replace copy the line before if the current line begins with a "B"?
If the line begins with "Q31" or similar, how do I add that current line to the end of the previous one?
Once this happens, is there a way to concat everything to create the speficied format above?
WORK FLOW #jaywayco
Open Text File
Read file line by line
Save each line in a list of strings
Split each string by ' '
Find each line that starts with a digit
Replace that digit to make it 4 digits in length
Check the following text after the digit to see if it is a "B ", "T ", or "SOME TEXT"
if "B " copy the line above
Add an "A" to the end of the digit
if "T " remove the "T "
if "SOME TEXT" do nothing
Find each line that starts with a "B "
Copy the digits on the line above and concat to the front of the "B "
Follow step 4.b.i
Find each line that starts with (or similar to) "Q31"
Concat this line to the end of the previous line
...?
Here's a really lame, procedural solution:
using System.IO;
using System.Collections.Generic;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var list = new List<string>();
using (var reader = File.OpenText(#"c:\input.txt"))
{
while (true)
{
var line = reader.ReadLine();
if (string.IsNullOrEmpty(line)) break;
list.Add(line);
}
}
list = HandleRemoveTRequirement(list);
list = HandleFourDigitRequirement(list);
list = HandleConcatRequirement(list);
list = HandleStartsWithBRequirement(list);
list = HandleSecondElementIsBRequirement(list);
using (var output = new StreamWriter(#"c:\output.txt"))
{
foreach (var line in list)
{
output.WriteLine(line);
}
}
}
static List<string> HandleSecondElementIsBRequirement(List<string> list)
{
var result = new List<string>();
foreach (var line in list)
{
var parts = line.Split(' ');
if (parts[1].Equals("B"))
{
parts[0] += "A";
parts[1] = string.Empty;
result.Add(string.Join(" ", parts).Replace(" ", " "));
}
else
{
result.Add(line);
}
}
return result;
}
static List<string> HandleStartsWithBRequirement(List<string> list)
{
var result = new List<string>();
var i = 0;
foreach (var line in list)
{
var parts = line.Split(' ');
if (parts[0].Equals("B"))
{
parts[0] = string.Empty;
result.Add(list[i - 1].Split(' ')[0] + "A" + string.Join(" ", parts));
}
else
{
result.Add(line);
}
i++;
}
return result;
}
static List<string> HandleConcatRequirement(List<string> list)
{
var result = new List<string>();
foreach (var line in list)
{
var parts = line.Split(' ');
int test;
if (int.TryParse(parts[0], out test) || parts[0].Equals("B"))
{
result.Add(line);
}
else
{
result[result.Count -1] += line;
}
}
return result;
}
static List<string> HandleRemoveTRequirement(List<string> list)
{
var result = new List<string>();
foreach (var line in list)
{
var parts = line.Split(' ');
if (parts[1].Equals("T"))
{
parts[1] = string.Empty;
}
result.Add(string.Join(" ", parts).Replace(" ", " "));
}
return result;
}
static List<string> HandleFourDigitRequirement(List<string> list)
{
var result = new List<string>();
foreach (var line in list)
{
var parts = line.Split(' ');
int test;
if (int.TryParse(parts[0], out test))
{
parts[0] = parts[0].PadLeft(4, '0');
result.Add(string.Join(" ", parts));
}
else
{
result.Add(line);
}
}
return result;
}
}
}
These are pretty complicated requirements and I would be tempted to implement this as a workflow. This way you can separate out each of the logical steps and this will increase maintainability.
I would be tempted to represent the text file as an array of string arrays or even a data table. Then you can write general functions that concatenate/transform specific values
One way to possibly approach this is similiar to jaywayco's.
I'd start with placing each line split by spaces into it's own array. Place that array into an Array of arrays. From there you can consider your workflow. Your line array that is split by the spaces you can determine how to print it based off the first value, being a number or letter B etc... If it's a B, you know that it should start with array[i-1] first value, which would be the number etc. You'd have to think through the logic a bit, but I think you can understand where I am coming from. I'm not sure if this is the best approach or not, but I think this is the way I would tackle it. Good luck!
Edit: Here is some mock code...
var mainArray = new Array[textFile.Count];
//obviously get the count of number of lines set that to the size of your array object.
for(int i=0; i < mainArray.Length; i++)
{
var line = methodToGetLineFromTextFile[i];
string[] lineArray = line.Split(' ');
mainArray[i] = lineArray;
}
//Once you have everything loaded into your arrays, apply your workflow logic.
Hope this helps!
The way I would go about this task is to write a set of unit tests based on your requirements, then make them pass one at a time (having one test per requirement).
As jaywayco suggested, I would read the file into an array of lines, then implement each of your rules as a line transformation method which can be tested in isolation. I would probably separate out the method which can select which transformation(s) to apply. Then loop over the lines and apply the transformations.

C# split string but keep split chars / separators [duplicate]

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);
}

Categories

Resources