Related
I tried doing this
using System;
using System.Collections.Generic;
using System.Text;
namespace UrlsDetector
{
class UrlDetector
{
public static string RemoveUrl(string input)
{
var words = input;
while(words.Contains("https://"))
{
string urlToRemove = words.Substring("https://", #" ");
words = words.Replace("https://" + urlToRemove , #"");
}
}
}
class Program
{
static void Main()
{
Console.WriteLine(UrlDetector.RemoveUrl(
"I saw a cat and a horse on https://www.youtube.com/"));
}
}
}
but it doesn't work
what I want to achieve is remove the entire "https://www.youtube.com/" and display "I saw a cat and a horse on"
I also want to display a message like "the sentence you input doesn't have url" if the sentence doesn't have any url. but as you can I didnt put any code to do that I just need to fix this code first but if you want to help me do that too, I gladly appreciated it.
thanks for responses.
If you are looking for a non RegEx way to do this, here you go. But the method I encoded below assumes that a URL begins with "http://" or "https://", which means it will not work with URL's that begin with something like ftp:// or file://, although the code below can be easily modified to support that. Also, it assumes the URL path continues until it reaches either the end of the string or a white space character (like a space or a tab or a new line). Again, this can easily be modified if your requirements are different.
Also, if the string contains no URL, currently it just returns a blank string. You can modify this easily too!
using System;
public class Program
{
public static void Main()
{
string str = "I saw a cat and a horse on https://www.youtube.com/";
UrlExtraction extraction = RemoveUrl(str);
Console.WriteLine("Original Text: " + extraction.OriginalText);
Console.WriteLine();
Console.WriteLine("Url: " + extraction.ExtractedUrl);
Console.WriteLine("Text: " + extraction.TextWithoutUrl);
}
private static UrlExtraction RemoveUrl(string str)
{
if (String.IsNullOrWhiteSpace(str))
{
return new UrlExtraction("", "", "");
}
int startIndex = str.IndexOf("https://",
StringComparison.InvariantCultureIgnoreCase);
if (startIndex == -1)
{
startIndex = str.IndexOf("http://",
StringComparison.InvariantCultureIgnoreCase);
}
if (startIndex == -1)
{
return new UrlExtraction(str, "", "");
}
int endIndex = startIndex;
while (endIndex < str.Length && !IsWhiteSpace(str[endIndex]))
{
endIndex++;
}
return new UrlExtraction(str, str.Substring(startIndex, endIndex - startIndex),
str.Remove(startIndex, endIndex - startIndex));
}
private static bool IsWhiteSpace(char c)
{
return
c == '\n' ||
c == '\r' ||
c == ' ' ||
c == '\t';
}
private class UrlExtraction
{
public string ExtractedUrl {get; set;}
public string TextWithoutUrl {get; set;}
public string OriginalText {get; set;}
public UrlExtraction(string originalText, string extractedUrl,
string textWithoutUrl)
{
OriginalText = originalText;
ExtractedUrl = extractedUrl;
TextWithoutUrl = textWithoutUrl;
}
}
}
A simplified version of what you're doing. Instead of using SubString or IndexOf, I split the input into a list of strings, and remove the items that contain a URL. I iterate over the list in reverse as removing an item in a forward loop direction will skip an index.
public static string RemoveUrl(string input)
{
List<string> words = input.Split(" ").ToList();
for (int i = words.Count - 1; i >= 0; i--)
{
if (words[i].StartsWith("https://")) words.RemoveAt(i);
}
return string.Join(" ", words);
}
This methods advantage is avoiding SubString and Replace methods that essentially create new Strings each time they're used. In a loop this excessive string manipulation can put pressure on the Garbage Collector and bloat the Managed Heap. A Split and Join has less performance cost in comparison especially when used in a loop like this with a lot of data.
#Moshi is correct with large amounts of data, so this is more of a Production Code Base example:
public static class Ext
{
public static LinkedList<T> RemoveAll<T>(this LinkedList<T> list, Predicate<T> match)
{
if (list == null)
{
throw new ArgumentNullException("list");
}
if (match == null)
{
throw new ArgumentNullException("match");
}
var count = 0;
var node = list.First;
while (node != null)
{
var next = node.Next;
if (match(node.Value))
{
list.Remove(node);
count++;
}
node = next;
}
return list;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var s= "I saw a https://www.youtube.com/cat and a https://www.youtube.com/horse on https://www.youtube.com/";
//Uncomment for second run
//s= #"I saw a https://www.youtube.com/cat and a https://www.youtube.com/horse on https://www.youtube.com/
//but it doesnt work
//what I want to achieve is remove the entire https://www.youtube.com/ and display I saw a cat and a horse on
//I also want to display a message like the sentence you input doesn't have url if the sentence doesn't have any url. but as you can I didnt put any code to do that I just need to fix this code first but if you want to help me do that too, I gladly appreciated it.
//thanks for responses.";
Stopwatch watch = new Stopwatch();
watch.Start();
var resultList = RemoveUrl(s);
watch.Stop(); Debug.WriteLine(watch.Elapsed.ToString());
watch.Reset(); watch.Start();
var wordsLL = new LinkedList<string>(s.Split(' '));
var result = string.Join(' ', wordsLL.RemoveAll(x => x.StartsWith("https://")));
watch.Stop(); Debug.WriteLine(watch.Elapsed.ToString());
}
}
var s one line:
watch.Elapsed = {00:00:00.0116388}
watch.Elapsed = {00:00:00.0134778}
var s multilines:
watch.Elapsed = {00:00:00.0013588}
watch.Elapsed = {00:00:00.0009252}
Using basic string manipulation will never get you where you want to be.
Using regular expressions makes this very easy for you.
search for a piece of text that looks like
"http(s)?:\/\/\S*[^\s\.]":
http: the text block http
(s)?: the optional (?) letter s
:\/\/: the characters ://
\S*: any amount (*) non white characters (\S)
[^\s\.]: any character that is not (^) in the list ([ ]) of characters being white characters (\s) or dot (\.). This allows you to exclude the dot at the end of a sentence from your url.
using System;
using System.Text.RegularExpressions;
namespace UrlsDetector
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine(UrlDetector.RemoveUrl(
"I saw a cat and a horse on https://www.youtube.com/ and also on http://www.example.com."));
Console.ReadLine();
}
}
class UrlDetector
{
public static string RemoveUrl(string input)
{
var regex = new Regex($#"http(s)?:\/\/\S*[^\s.]");
return regex.Replace(input, "");
}
}
}
Using regular expressions you can also detect matches Regex.Match(...) which allows you to detect any urls in your text.
Better way to use, split and StringBuilder. Code will be look like this. StringBuilder is optimized this kind of situation.
Pseudocode:
var words = "I saw a cat and a horse on https://www.youtube.com/".Split(" ").ToList();
var sb = new StringBuilder();
foreach(var word in words){
if(!word.StartsWith("https://")) sb.Append(word + " ");
}
return sb.ToString();
I am trying to convert camel case to snake case.
Like this:
"LiveKarma" -> "live_karma"
"youGO" -> "you_g_o"
I cannot seem to get the second example working like that. It always outputs as 'you_go' . How can I get it to output 'you_g_o'
My code:
(Regex.Replace(line, "(?<=[a-z0-9])[A-Z]", "_$0", RegexOptions.Compiled)).ToLowerInvariant()
Here is an extension method that transforms the text into a snake case:
using System.Text;
public static string ToSnakeCase(this string text)
{
if(text == null) {
throw new ArgumentNullException(nameof(text));
}
if(text.Length < 2) {
return text;
}
var sb = new StringBuilder();
sb.Append(char.ToLowerInvariant(text[0]));
for(int i = 1; i < text.Length; ++i) {
char c = text[i];
if(char.IsUpper(c)) {
sb.Append('_');
sb.Append(char.ToLowerInvariant(c));
} else {
sb.Append(c);
}
}
return sb.ToString();
}
Put it into a static class somewhere (named for example StringExtensions) and use it like this:
string text = "LiveKarma";
string snakeCaseText = text.ToSnakeCase();
// snakeCaseText => "live_karma"
Since the option that converts abbreviations as separate words is not suitable for many, I found a complete solution in the EF Core codebase.
Here are a couple of examples of how the code works:
TestSC -> test_sc
testSC -> test_sc
TestSnakeCase -> test_snake_case
testSnakeCase -> test_snake_case
TestSnakeCase123 -> test_snake_case123
_testSnakeCase123 -> _test_snake_case123
test_SC -> test_sc
I rewrote it a bit so you can copy it as a ready-to-use string extension:
using System;
using System.Globalization;
using System.Text;
namespace Extensions
{
public static class StringExtensions
{
public static string ToSnakeCase(this string text)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
var builder = new StringBuilder(text.Length + Math.Min(2, text.Length / 5));
var previousCategory = default(UnicodeCategory?);
for (var currentIndex = 0; currentIndex < text.Length; currentIndex++)
{
var currentChar = text[currentIndex];
if (currentChar == '_')
{
builder.Append('_');
previousCategory = null;
continue;
}
var currentCategory = char.GetUnicodeCategory(currentChar);
switch (currentCategory)
{
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.TitlecaseLetter:
if (previousCategory == UnicodeCategory.SpaceSeparator ||
previousCategory == UnicodeCategory.LowercaseLetter ||
previousCategory != UnicodeCategory.DecimalDigitNumber &&
previousCategory != null &&
currentIndex > 0 &&
currentIndex + 1 < text.Length &&
char.IsLower(text[currentIndex + 1]))
{
builder.Append('_');
}
currentChar = char.ToLower(currentChar, CultureInfo.InvariantCulture);
break;
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.DecimalDigitNumber:
if (previousCategory == UnicodeCategory.SpaceSeparator)
{
builder.Append('_');
}
break;
default:
if (previousCategory != null)
{
previousCategory = UnicodeCategory.SpaceSeparator;
}
continue;
}
builder.Append(currentChar);
previousCategory = currentCategory;
}
return builder.ToString();
}
}
}
You can find the original code here:
https://github.com/efcore/EFCore.NamingConventions/blob/main/EFCore.NamingConventions/Internal/SnakeCaseNameRewriter.cs
UPD 27.04.2022:
Also, you can use Newtonsoft library if you're looking for a ready to use third party solution. The output of the code is the same as the code above.
// using Newtonsoft.Json.Serialization;
var snakeCaseStrategy = new SnakeCaseNamingStrategy();
var snakeCaseResult = snakeCaseStrategy.GetPropertyName(text, false);
Simple Linq based solution... no idea if its faster or not. basically ignores consecutive uppercases
public static string ToUnderscoreCase(this string str)
=> string.Concat((str ?? string.Empty).Select((x, i) => i > 0 && i < str.Length - 1 && char.IsUpper(x) && !char.IsUpper(str[i-1]) ? $"_{x}" : x.ToString())).ToLower();
using Newtonsoft package
public static string? ToCamelCase(this string? str) => str is null
? null
: new DefaultContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() }.GetResolvedPropertyName(str);
public static string? ToSnakeCase(this string? str) => str is null
? null
: new DefaultContractResolver() { NamingStrategy = new SnakeCaseNamingStrategy() }.GetResolvedPropertyName(str);
RegEx Solution
A quick internet search turned up this site which has an answer using RegEx, which I had to modify to grab the Value portion in order for it to work on my machine (but it has the RegEx you're looking for). I also modified it to handle null input, rather than throwing an exception:
public static string ToSnakeCase2(string str)
{
var pattern =
new Regex(#"[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+");
return str == null
? null
: string
.Join("_", pattern.Matches(str).Cast<Match>().Select(m => m.Value))
.ToLower();
}
Non-RegEx Solution
For a non-regex solution, we can do the following:
Reduce all whitespace to a single space by
using string.Split to split with an empty array as the first parameter to split on all whitespace
joining those parts back together with the '_' character
Prefix all upper-case characters with '_' and lower-case them
Split and re-join the resulting string on the _ character to remove any instances of multiple concurrent underscores ("__") and to remove any leading or trailing instances of the character.
For example:
public static string ToSnakeCase(string str)
{
return str == null
? null
: string.Join("_", string.Concat(string.Join("_", str.Split(new char[] {},
StringSplitOptions.RemoveEmptyEntries))
.Select(c => char.IsUpper(c)
? $"_{c}".ToLower()
: $"{c}"))
.Split(new[] {'_'}, StringSplitOptions.RemoveEmptyEntries));
}
pseudo code below. In essence check if each char is upper case, then if it is add a _, then add the char to lower case
var newString = s.subString(0,1).ToLower();
foreach (char c in s.SubString(1,s.length-1))
{
if (char.IsUpper(c))
{
newString = newString + "_";
}
newString = newString + c.ToLower();
}
if you're into micro-optimaizations and want to prevent unneccessary conversions wherever possible, this one might also work:
public static string ToSnakeCase(this string text)
{
static IEnumerable<char> Convert(CharEnumerator e)
{
if (!e.MoveNext()) yield break;
yield return char.ToLower(e.Current);
while (e.MoveNext())
{
if (char.IsUpper(e.Current))
{
yield return '_';
yield return char.ToLower(e.Current);
}
else
{
yield return e.Current;
}
}
}
return new string(Convert(text.GetEnumerator()).ToArray());
}
There is a well maintained EF Core community project that implements a number of naming convention rewriters called EFCore.NamingConventions. The rewriters don't have any internal dependencies, so if you don't want to bring in an EF Core related package you can just copy the rewriter code out.
Here is the snake case rewriter: https://github.com/efcore/EFCore.NamingConventions/blob/main/EFCore.NamingConventions/Internal/SnakeCaseNameRewriter.cs
May has well toss this one out. Very simple and worked for me.
public static string ToSnakeCase(this string text)
{
text = Regex.Replace(text, "(.)([A-Z][a-z]+)", "$1_$2");
text = Regex.Replace(text, "([a-z0-9])([A-Z])", "$1_$2");
return text.ToLower();
}
Testing it with some samples (borrowed from #GeekInside's answer):
var samples = new List<string>() { "TestSC", "testSC", "TestSnakeCase", "testSnakeCase", "TestSnakeCase123", "_testSnakeCase123", "test_SC" };
var results = new List<string>() { "test_sc", "test_sc", "test_snake_case", "test_snake_case", "test_snake_case123", "_test_snake_case123", "test_sc" };
for (int i = 0; i < samples.Count; i++)
{
Console.WriteLine("Test success: " + (val.ToSnakeCase() == results[i] ? "true" : "false"));
}
Produced the following output:
Test success: true
Test success: true
Test success: true
Test success: true
Test success: true
Test success: true
Test success: true
This question already has answers here:
How do I create a terminable while loop in console application?
(7 answers)
Closed 3 years ago.
I started a C# course now and there is an assignment where I have to create a "palindrome detector" program. Point is that user inputs some word or sentence, then I have to remove chars like ,.;:!? and space from it. I have done that with two different methods, because char method can not remove space so I wrote another method for it.
After "cleaning" operation program reversing input what user gave, and comparing original user input and reversed user input to each other. It they are same it prints "It is palindrome", if they are different it prints "It is not palindrome". That is working fine, BUT THE PROBLEM IS I have to put them in for loop. It have to ask input again and again, until user give empty.
This would be very easy, but somehow I can not do it.
Here is my code:
using System;
namespace Palindromi
{
class Program
{
static void Main()
{
Console.WriteLine("Hei! Tervetuloa palindromin tunnistusohjelmaan. Tämä tunnistaa, onko syöttämäsi sana sama toisinpäin!");
Console.Write("Anna teksti (tyhjä lopettaa): ");
string userinput = Console.ReadLine(); //userinput is user's input, this is what you have to modify. remove some chars and reverse it.
if (userinput == "")
{
Console.ReadLine();//when i have loop this have to be "break". This meant to break for loop when i have it.
}
char[] removechars = { '.', ':', ';', ',', '!', '?' };//this is the list of "have to be removed" chars
string userinput_without_chars = userinput.Trim(removechars); //this method remove chars which are listed
string userinput_without_chars_space = userinput_without_chars.Replace( " ", ""); //replace space with empty
string reverse_string, reversed;
reverse_string = userinput_without_chars_space;
reversed = "";
int len;
len = userinput_without_chars_space.Length - 1;
while (len >= 0)
{
reversed = reversed + reverse_string[len];
len--;
}
Console.WriteLine("Sana käännettynä on {0}", reversed); //tells user input reversed
if (userinput_without_chars_space == reversed)//check is the userinput same than reversed user input
{
Console.Write("On palindromi.");//it is palindrome
}
else
{
Console.Write("Ei ole palindromi.");//it is not palindrome
}
}
}
}
You could potentially do something along these lines:
var running = true;
while(running)
{
var input = Console.ReadLine().ToLower();
var phrase = input.Sanitize(new List<string>() {".", ",", "?", "!", "'", "&", "%", "$", " "});
if(phrase.IsPalindrome())
Console.Writeline("Input was palindrome.");
}
public static string Sanitize(this string input, IList<string> punctuation) =>
String.Join(String.Empty, input.Where(character => punctuation.Contains(character) == false));
public static bool IsPalindrome(this string sentence)
{
for (int l = 0, r = sentence.Length - 1; l < r; l++, r--)
if (sentence[l] != sentence[r])
return false;
return true;
}
public static void Close(string input)
{
// Some logic to see if the application should stop.
}
You could create another method that looks for commands, or keystrokes, then sets the boolean to run as false. Which would break the infinite loop. You could also do an abrupt close with Environment.Exit.
The very simplest approach is replace your Console.ReadLine() where you want to break to return.
Alternatively, you could wrap the logic in another while loop.
while (userinput != "")
{
// Remove chars
// rest of your logic
/* IMPORTANT */
userinput = Console.Readline();
}
To remove the symbols from the input, you can use the Regex.Replace method. In this case, you can be sure, that the specified symbols will be correctly removed from the input string. Note, that you can handle the whitespaces along with other characters you mentioned, like in the code snippet below:
var CharactersToRemove { get; set; } = " ,.;:!?";
var processedInput = Regex.Replace(input.ToLower(), $"[{CharactersToRemove}]", string.Empty);
Note, that here I used input.ToLower() to convert the input to a lowercase string. This will make the palindrome tests case-insensitive. Should you need case-sensitive palindrome tests, just remove the .ToLower() part.
There is no need to reverse the input string to check if it is a palindrome. You can check this within one for loop as follows:
bool CheckForBeingaAPalindrome(string input)
{
var frontIndex = 0;
var tailIndex = input.Length - 1;
for (; frontIndex < tailIndex;)
{
if (input[frontIndex] != input[tailIndex])
return false;
++frontIndex;
--tailIndex;
}
return true;
}
Note, that in this case you only iterate over the elements of the input string once. This approach will give you al least 4 times better performance than the one you used.
Below, you can find a complete minimal working solution to your problem.
using System.Text.RegularExpressions;
using static System.Console;
namespace Assignment
{
public static class PalindromeFinder
{
public static string CharactersToRemove { get; set; } = " ,.;:!?";
public static bool IsPalindrome(string input)
{
var processedInput = RemoveUnnecessaryCharacters(input);
return CheckForBeingAPalindrome(processedInput);
}
private static string RemoveUnnecessaryCharacters(string input)
{
return Regex.Replace(input.ToLower(), $"[{CharactersToRemove}]", string.Empty);
}
private static bool CheckForBeingAPalindrome(string input)
{
var frontIndex = 0;
var tailIndex = input.Length - 1;
for (; frontIndex < tailIndex;)
{
if (input[frontIndex] != input[tailIndex])
return false;
++frontIndex;
--tailIndex;
}
return true;
}
}
public class Program
{
private static void Main(string[] args)
{
ContinuouslyCheckUserInputForBeingAPalindrome();
}
private static void ContinuouslyCheckUserInputForBeingAPalindrome()
{
while (FetchUserInputFromConsole() is string input
&& !string.IsNullOrWhiteSpace(input))
{
var isPalindrome = PalindromeFinder.IsPalindrome(input);
var modifier = isPalindrome ? "a" : "not a";
WriteLine($"It is {modifier} palindrome");
}
}
private static string FetchUserInputFromConsole()
{
Write("Enter a string: ");
return ReadLine();
}
}
}
I have a string like this;
string text = "6A7FEBFCCC51268FBFF";
And I have one method for which I want to insert the logic for appending the hyphen after 4 characters to 'text' variable. So, the output should be like this;
6A7F-EBFC-CC51-268F-BFF
Appending hyphen to above 'text' variable logic should be inside this method;
public void GetResultsWithHyphen
{
// append hyphen after 4 characters logic goes here
}
And I want also remove the hyphen from a given string such as 6A7F-EBFC-CC51-268F-BFF. So, removing hyphen from a string logic should be inside this method;
public void GetResultsWithOutHyphen
{
// Removing hyphen after 4 characters logic goes here
}
How can I do this in C# (for desktop app)?
What is the best way to do this?
Appreciate everyone's answer in advance.
GetResultsWithOutHyphen is easy (and should return a string instead of void
public string GetResultsWithOutHyphen(string input)
{
// Removing hyphen after 4 characters logic goes here
return input.Replace("-", "");
}
for GetResultsWithHyphen, there may be slicker ways to do it, but here's one way:
public string GetResultsWithHyphen(string input)
{
// append hyphen after 4 characters logic goes here
string output = "";
int start = 0;
while (start < input.Length)
{
output += input.Substring(start, Math.Min(4,input.Length - start)) + "-";
start += 4;
}
// remove the trailing dash
return output.Trim('-');
}
Use regex:
public String GetResultsWithHyphen(String inputString)
{
return Regex.Replace(inputString, #"(\w{4})(\w{4})(\w{4})(\w{4})(\w{3})",
#"$1-$2-$3-$4-$5");
}
and for removal:
public String GetResultsWithOutHyphen(String inputString)
{
return inputString.Replace("-", "");
}
Here's the shortest regex I could come up with. It will work on strings of any length. Note that the \B token will prevent it from matching at the end of a string, so you don't have to trim off an extra hyphen as with some answers above.
using System;
using System.Text.RegularExpressions;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string text = "6A7FEBFCCC51268FBFF";
for (int i = 0; i <= text.Length;i++ )
Console.WriteLine(hyphenate(text.Substring(0, i)));
}
static string hyphenate(string s)
{
var re = new Regex(#"(\w{4}\B)");
return re.Replace (s, "$1-");
}
static string dehyphenate (string s)
{
return s.Replace("-", "");
}
}
}
var hyphenText = new string(
text
.SelectMany((i, ch) => i%4 == 3 && i != text.Length-1 ? new[]{ch, '-'} : new[]{ch})
.ToArray()
)
something along the lines of:
public string GetResultsWithHyphen(string inText)
{
var counter = 0;
var outString = string.Empty;
while (counter < inText.Length)
{
if (counter % 4 == 0)
outString = string.Format("{0}-{1}", outString, inText.Substring(counter, 1));
else
outString += inText.Substring(counter, 1);
counter++;
}
return outString;
}
This is rough code and may not be perfectly, syntactically correct
public static string GetResultsWithHyphen(string str) {
return Regex.Replace(str, "(.{4})", "$1-");
//if you don't want trailing -
//return Regex.Replace(str, "(.{4})(?!$)", "$1-");
}
public static string GetResultsWithOutHyphen(string str) {
//if you just want to remove the hyphens:
//return input.Replace("-", "");
//if you REALLY want to remove hyphens only if they occur after 4 places:
return Regex.Replace(str, "(.{4})-", "$1");
}
For removing:
String textHyphenRemoved=text.Replace('-',''); should remove all of the hyphens
for adding
StringBuilder strBuilder = new StringBuilder();
int startPos = 0;
for (int i = 0; i < text.Length / 4; i++)
{
startPos = i * 4;
strBuilder.Append(text.Substring(startPos,4));
//if it isn't the end of the string add a hyphen
if(text.Length-startPos!=4)
strBuilder.Append("-");
}
//add what is left
strBuilder.Append(text.Substring(startPos, 4));
string textWithHyphens = strBuilder.ToString();
Do note that my adding code is untested.
GetResultsWithOutHyphen method
public string GetResultsWithOutHyphen(string input)
{
return input.Replace("-", "");
}
GetResultsWithOutHyphen method
You could pass a variable instead of four for flexibility.
public string GetResultsWithHyphen(string input)
{
string output = "";
int start = 0;
while (start < input.Length)
{
char bla = input[start];
output += bla;
start += 1;
if (start % 4 == 0)
{
output += "-";
}
}
return output;
}
This worked for me when I had a value for a social security number (123456789) and needed it to display as (123-45-6789) in a listbox.
ListBox1.Items.Add("SS Number : " & vbTab & Format(SSNArray(i), "###-##-####"))
In this case I had an array of Social Security Numbers. This line of code alters the formatting to put a hyphen in.
Callee
public static void Main()
{
var text = new Text("THISisJUSTanEXAMPLEtext");
var convertText = text.Convert();
Console.WriteLine(convertText);
}
Caller
public class Text
{
private string _text;
private int _jumpNo = 4;
public Text(string text)
{
_text = text;
}
public Text(string text, int jumpNo)
{
_text = text;
_jumpNo = jumpNo < 1 ? _jumpNo : jumpNo;
}
public string Convert()
{
if (string.IsNullOrEmpty(_text))
{
return string.Empty;
}
if (_text.Length < _jumpNo)
{
return _text;
}
var convertText = _text.Substring(0, _jumpNo);
int start = _jumpNo;
while (start < _text.Length)
{
convertText += "-" + _text.Substring(start, Math.Min(_jumpNo, _text.Length - start));
start += _jumpNo;
}
return convertText;
}
}
The below code is from my other questions that I have asked here on SO. Everyone has been so helpful and I almost have a grasp with regards to RegEx but I ran into another hurdle.
This is what I basically need to do in a nutshell. I need to take this line that is in a text file that I load into my content variable:
X17.8Y-1.Z0.1G0H1E1
I need to do a wildcard search for the X value, Y value, Z value, and H value. When I am done, I need this written back to my text file (I know how to create the text file so that is not the problem).
X17.8Y-1.G54G0T2
G43Z0.1H1M08
I have code that the kind users here have given me, except I need to create the T value at the end of the first line, and use the value from the H and increment it by 1 for the T value. For example:
X17.8Y-1.Z0.1G0H5E1
would translate as:
X17.8Y-1.G54G0T6
G43Z0.1H5M08
The T value is 6 because the H value is 5.
I have code that does everything (does two RegEx functions and separates the line of code into two new lines and adds some new G values). But I don't know how to add the T value back into the first line and increment it by 1 of the H value. Here is my code:
StreamReader reader = new StreamReader(fDialog.FileName.ToString());
string content = reader.ReadToEnd();
reader.Close();
content = Regex.Replace(content, #"X[-\d.]+Y[-\d.]+", "$0G54G0");
content = Regex.Replace(content, #"(Z(?:\d*\.)?\d+)[^H]*G0(H(?:\d*\.)?\d+)\w*", "\nG43$1$2M08"); //This must be created on a new line
This code works great at taking:
X17.8Y-1.Z0.1G0H5E1
and turning it into:
X17.8Y-1.G54G0
G43Z0.1H5M08
but I need it turned into this:
X17.8Y-1.G54G0T6
G43Z0.1H5M08
(notice the T value is added to the first line, which is the H value +1 (T = H + 1).
Can someone please modify my RegEx statement so I can do this automatically? I tried to combine my two RegEx statements into one line but I failed miserably.
Update1: Stephen in the comments below suggests, "there's no arithmetic operators in regex, you'll need to use a group to pull out the H value, turn it into an int, add one and build a new string.". But I have no idea on how to do this in C# code.
The easiest way to do this is with a simple program that uses a few Regex patterns that capture (named) groups, I had a little spare time so here you go:
Program.cs
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const string InputFileName = #"input.txt";
const string OutputFileName = #"output.txt";
List<Line> parsedLineList = new List<Line>();
using (StreamReader sr = new StreamReader(InputFileName))
{
string inputLine;
int lineNum = 0;
while ((inputLine = sr.ReadLine()) != null)
{
lineNum++;
Line parsedLine = new Line(inputLine);
if (parsedLine.IsMatch)
{
parsedLineList.Add(parsedLine);
}
else
{
Debug.WriteLine("Line {0} did not match pattern {1}", lineNum, inputLine);
}
}
}
using (StreamWriter sw = new StreamWriter(OutputFileName))
{
foreach (Line line in parsedLineList)
{
sw.WriteLine(line.ToString());
}
}
}
}
}
With input.txt containing:
X17.8Y-1.Z0.1G0H1E1
this program creates output.txt containing:
X17.8Y-1.G54G0T2G43Z0.1H1M08
The above code in Program.cs requires the following simple Line and Fragment class definitions:
Line.cs
namespace Fragments
{
class Line
{
private readonly static Regex Pattern =
new Regex(#"^(?<X>X[^Y]+?)(?<Y>Y[^Z]+?)(?<Z>Z[^G]+?)(?<G>G[^H]+?)(?<H>H[^E]+?)(?<E>E[^$])$");
public readonly string OriginalText;
public string Text
{
get
{
return this.X.ToString() + this.Y.ToString() + this.G54.ToString() + this.G.ToString() + this.T.ToString() + Environment.NewLine +
this.G43.ToString() + this.Z.ToString() + this.H.ToString() + this.M08.ToString();
}
}
public readonly bool IsMatch;
public Fragment X { get; set; }
public Fragment Y { get; set; }
public readonly Fragment G54 = new Fragment("G54");
public Fragment G { get; set; }
public Fragment T { get; set; }
public readonly Fragment G43 = new Fragment("G43");
public Fragment Z { get; set; }
public Fragment H { get; set; }
public readonly Fragment M08 = new Fragment("M08");
public Fragment E { get; set; }
public Line(string text)
{
this.OriginalText = text;
Match match = Line.Pattern.Match(text);
this.IsMatch = match.Success;
if (match.Success)
{
this.X = new Fragment(match.Groups["X"].Value);
this.Y = new Fragment(match.Groups["Y"].Value);
this.G = new Fragment(match.Groups["G"].Value);
this.Z = new Fragment(match.Groups["Z"].Value);
this.H = new Fragment(match.Groups["H"].Value);
this.E = new Fragment(match.Groups["E"].Value);
this.T = new Fragment('T', this.H.Number + 1.0);
}
}
public override string ToString()
{
return this.Text;
}
}
}
Fragment.cs
namespace Fragments
{
class Fragment
{
private readonly static Regex Pattern =
new Regex(#"^(?<Letter>[A-Z]{1})(?<Number>.+)$");
public readonly string Text;
public readonly bool IsMatch;
public readonly char Letter;
public readonly double Number;
public Fragment(string text)
{
this.Text = text;
Match match = Fragment.Pattern.Match(text);
this.IsMatch = match.Success;
if (match.Success)
{
this.Letter = match.Groups["Letter"].Value[0];
string possibleNumber = match.Groups["Number"].Value;
double parsedNumber;
if (double.TryParse(possibleNumber, out parsedNumber))
{
this.Number = parsedNumber;
}
else
{
Debug.WriteLine("Couldn't parse double from input {0}", possibleNumber);
}
}
else
{
Debug.WriteLine("Fragment {0} did not match fragment pattern", text);
}
}
public Fragment(char letter, double number)
{
this.Letter = letter;
this.Number = number;
this.Text = letter + number.ToString();
this.IsMatch = true;
}
public override string ToString()
{
return this.Text;
}
}
}
Create a new C# Console Application project, add these three files, update your using statements and you're ready to go. You can very easily alter the code in Program.cs to read the input and output filenames from Main's command line arguments to make the program reusable.
I'm not sure you can do this just with Regular Expressions, and even in case you can, thinking on maintainability of the code, I wouldn't implement it that way. What you can easily do with RegEx, is to capture the pieces you need into groups, and from those create the output expression.
Here's the code for that:
System.Text.StringBuilder content = new System.Text.StringBuilder();
using (var reader = new StreamReader(fDialog.FileName.ToString()))
{
string line = reader.ReadLine();
while (line != null)
{
var matchingExpression = Regex.Match(line, #"(X[-\d.]+)(Y[-\d.]+)(Z(?:\d*\.)?\d+)[^H]*G0H((?:\d*\.)?\d+)\w*");
content.AppendFormat(
System.Globalization.CultureInfo.InvariantCulture,
"{0}{1}G54G0T{2}\n",
matchingExpression.Groups[0].Value,
matchingExpression.Groups[1].Value,
Int32.Parse(matchingExpression.Groups[3].Value) + 1);
content.AppendFormat(
System.Globalization.CultureInfo.InvariantCulture,
"G43{0}H{1}M08\n",
matchingExpression.Groups[2].Value,
matchingExpression.Groups[3].Value);
line = reader.ReadLine();
}
}
And to get the output string you should do:
content.ToString();