Say I have a string such as:
ma, 100 or, ma, word, or even ma. , *+ etc.
How can I find the position of the first character that is not some form of punctuation (i.e full stop, comma, colon, semi-colon) or whitespace, after an index. So, in the last example above, I'd want to get the position of * when I pass in 1 as a start index (zero-based).
Create an array of the characters that you want to match and call String.IndexOfAny
For example:
const string GoodCharsStr =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxy";
readonly char[] GoodChars = GoodCharsStr.ToCharArray();
string search = "ma, 100";
int position = search.IndexOfAny(GoodChars, 1);
if (position == -1)
{
// not found
}
char foundChar = search[position];
You'll need to define what exactly a special character is.
If it's a non-consecutive set (according to ASCII ordering, see http://www.asciitable.com/) then you'll need to define a new allowed character set and check against that set.
Something like this should work:
public const string allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.,";
public int RetrieveIndex(string input, int startIndex)
{
for (var x = startIndex; x < input.length; x++)
{
if (allowed.IndexOf(input[x])==-1)
{
return x;
}
}
return -1;
}
However, if it is a consecutive set as defined by the ASCII standard:
Just figure out which range is considered acceptable or special and check against that by converting the character to an integer and checking if it lies within the range. This would prove faster than the calls to allowed.IndexOf(...).
You can use a method like this
public static int GetFirstNonPunctuationCharIndex(string input, int startIndex, char[] punctuation)
{
//Move the startIndex forward one because we ignore the index user set
startIndex = startIndex + 1 < input.Length ? startIndex + 1 : input.Length;
for (int i = startIndex ; i < input.Length; i++)
{
if (!punctuation.Contains(input[i]) && !Char.IsWhiteSpace(input[i]))
{
return i;
}
}
return -1;
}
You would call it by passing in the string, starting index, and an array of characters you consider to be punctuation.
string myString = #"ma. , *+";
char[] puncArray = new char[4] { '.', ',', ';', ':' };
int index = GetFirstNonPunctuationCharIndex(myString, 1, puncArray)
Normally I'd use the Char.IsPunctuation method but apparently it considers * to be a punctuation character so you'll have to roll your own like above.
Related
⦁ Replace and/or re-arrange characters of this given string to get the lexicographically smallest string possible. For this, you can perform the following two operations any number of times.
⦁ Swap any two characters in the string. This operation costs 1 point. (any two, need not be adjacent)
⦁ Replace a character in the string with any other lower case English letter. This operation costs 2 points.
Obtain the lexicographically smallest string possible, by using at most P points.
Input:
⦁ Two lines of input, first-line containing two integers N, P.
⦁ The second line contains a string S consisting of N characters.
Output:
Lexicographically smallest string obtained.
for e.g
Sample Input:
3 3
bba
Sample Output:
aab
Ive tried this but it doesnt contain P points i dont know how to do that can you guys please help me with that:
namespace Lexicographical
{
class GFG
{
// Function to return the lexicographically
// smallest String that can be formed by
// swapping at most one character.
// The characters might not necessarily
// be adjacent.
static String findSmallest(char[] s)
{
int len = s.Length;
// Store last occurrence of every character
int[] loccur = new int[26];
// Set -1 as default for every character.
for (int i = 0; i < 26; i++)
loccur[i] = -1;
for (int i = len - 1; i >= 0; --i)
{
// char index to fill
// in the last occurrence array
int chI = s[i] - 'a';
if (loccur[chI] == -1)
{
// If this is true then this
// character is being visited
// for the first time from the last
// Thus last occurrence of this
// character is stored in this index
loccur[chI] = i;
}
}
char[] sorted_s = s;
Array.Sort(sorted_s);
for (int i = 0; i < len; ++i)
{
if (s[i] != sorted_s[i])
{
// char to replace
int chI = sorted_s[i] - 'a';
// Find the last occurrence
// of this character.
int last_occ = loccur[chI];
// Swap this with the last occurrence
char temp = s[last_occ];
s[last_occ] = s[i];
s[i] = temp;
break;
}
}
return String.Join("", s);
}
// Driver code
public static void Main(String[] args)
{
String s = "abb";
Console.Write(findSmallest(s.ToCharArray()));
}
}
}
The output for this is abb but it should be aab...
I want to know how i can use above Question in this
I want to make a simple generator of strings.
User inputs a "template" for string. Template can have placeholders in any place in it.
Then he inputs possible characters that can fit into any placeholder in string.
How it should work:
INPUT:
a.b.
123
OUTPUT:
[
"a1b1", "a1b2", "a1b3",
"a2b1", "a2b2", "a2b3",
"a3b1", "a3b2", "a3b3"
]
I found some of my old python code, but i don't understand it at all.
I split the input string to array of strings and array of dots.
Then I tried to increment just dots and each time just concat those two arrays in the right way.
But I found a new trouble.
string[] splitted = kt_NonCur.Split('.'); // array of constant strings
char[] nch = new char[splitted.Length - 1]; // array of new chars (generated)
char lgc = goodLetters.Last( ); // last good char
for( int i = 0; i < nch.Length - 1; i++ ) // set up all nch to first letter
nch[i] = goodLetters[0];
while( nch.Last( ) != lgc ) { // until last nch is set to last good char
outputData.Add($"{concatsplit(splitted, nch)}"); // concatsplit(s,n) concatenates two arrays into string
nch[0] = up(nch[0]); // up(char) gets next character from goodLetters. If there is no next, it returns first letter.
if( nch[0] == goodLetters[0] ) {
nch[1] = up(nch[1]);
if(nch[1] == goodLetters[0]){
nch[2] = up(nch[2]);
// .
// .
// .
}
}
}
And the problem is: I am facing a dilemma. Either find better way, or limit number of placeholders so that the code ladder is not too long. Of course I would add then some code that checks if it is the last and stop executing code for others, but I still would have to make
You can look at your problem this way: if you have P placeholders in your input string and the number of replacement characters is R, to construct every possibe output string you need at each step P numbers [0...R-1] (which can then serve as index into the replacement character list). Well, this is the definition of an integer with P digits in base R.
So let's write a helper class representing such integers:
class NDigitNumber
{
int[] _digits;
int _base;
// construct an integer with the specified numer of digits in the specified base
public NDigitNumber(int digits, int #base)
{
_digits = new int[digits];
_base = #base;
}
// get the digit at the specified position
public int this[int index] => _digits[index];
// increment the number, returns false on overflow
public bool Increment()
{
for (var pos = 0; pos < _digits.Length; pos++)
{
if (++_digits[pos] < _base)
break;
if (pos == _digits.Length-1)
return false;
for (var i = 0; i <= pos; i++)
_digits[i] = 0;
}
return true;
}
}
The Increment methods works like these mechanical counter devices where each digit wheel, when rotated from its maximum digit to the next, resets itself and all lower wheels to 0 and increments the next higher wheel.
Then we only have to iterate over all possible such integers to get the desired output:
var input = "a.b.";
var placeholder = '.';
var replacements = new[] { '1', '2', '3' };
// determine positions of placeholder in string
var placeholderPositions = new List<int>();
for (var i = 0; i < input.Length; i++)
{
if (input[i] == placeholder)
placeholderPositions.Add(i);
}
// iterate over all possible integers with
// placeholderPositions.Count digits
// in base replacements.Length
var number = new NDigitNumber(placeholderPositions.Count, replacements.Length);
do
{
var result = new StringBuilder(input);
for (var i = 0; i < placeholderPositions.Count; i++)
result[placeholderPositions[i]] = replacements[number[i]];
Console.WriteLine(result.ToString());
} while(number.Increment());
Output:
a1b1
a2b1
a3b1
a1b2
a2b2
a3b2
a1b3
a2b3
a3b3
Based on accepted answer of this post:
public static IEnumerable<string> Combinations(string template, string str, char placeholder)
{
int firstPlaceHolder = template.IndexOf(placeholder);
if (firstPlaceHolder == -1)
return new string[] { template };
string prefix = template.Substring(0, firstPlaceHolder);
string suffix = template.Substring(firstPlaceHolder + 1);
var recursiveCombinations = Combinations(suffix, str, placeholder);
return
from chr in str
from recSuffix in recursiveCombinations
select prefix + chr + recSuffix;
}
Usage:
List<string> combinations = Combinations("a.b.", "123", '.').ToList();
I'm trying to cycle through chars in a string.
string cycleMe = "Hi StackOverflow! Here is my string."
However, I want to skip over certain ranges of indexes. The ranges I want to skip over are stored in a List of objects, delims.
List<Delim> delims = delimCreator();
To retrieve each starting index and ending index for a range, I have to write a loop that accesses each "delim":
delims[0].getFirstIndex() //results in, say, index 2
delims[0].getLastIndex() //results in, say, index 4
delims[1].getFirstIndex() //results in, say, index 5
delims[1].getLastIndex() //results in, say, index 7
(there can be infinitely many "delim" objects in play)
If the above were my list, I'd want to print the string cycleMe, but skip all the chars between 2 and 4 (inclusive) and 5 and 7 (inclusive).
Expected output using the numbers above:
HiOverflow! Here is my string.
Here is the code I have written so far. It loops far more often than I'd expect (it loops ~x2 the number of characters in the string). Thanks in advance! =)
List<Delim> delims = delimAggregateInator(displayTextRaw);
for (int x = 0; x < cycleMe.Length;x++){
for (int i = 0; i < delims.Count; i++){
if (!(x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex())){
Debug.Log("test");
}
}
I assume that by skipping you meant you want to omit those characters from the original string. If that is the case, you can try Aggregate extension method like below.
string result = delims.Aggregate<Delim, string>(cycleMe, (str, d) => cycleMe = cycleMe.Remove(d.FirstIndex, (d.LastIndex - d.FirstIndex) + 1));
Make sure that the delim list is in the proper order.
Solution might be converting the string to char array, replacing the desired parts to spaces, and converting the output back to string.
Here is the modified version of your code:
string cycleMe = "Hi StackOverflow! Here is my string."
var charArray = cycleMe.ToCharArray(); // Converting to char array
List<Delim> delims = delimAggregateInator(displayTextRaw);
for (int x = 0; x < cycleMe.Length;x++){
for (int i = 0; i < delims.Count; i++){
// ORIGINAL: if (!(x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex())){
if (x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex()){
Debug.Log("test");
charArray[x] = ' '; // Replacing the item with space
}
}
string output = new string(charArray); // Converting back to string
P.S. This is probably not the most optimal solution but at least it should work.
You should use LINQ for that
struct Delim
{
public int First { get; set; }
public int Last { get; set; }
}
static void Main(string[] args)
{
string cycleMe = "Hi StackOverflow! Here is my string.";
var delimns = new List<Delim> { new Delim { First=2, Last=4}, new Delim { First = 5, Last = 7 } };
var cut = cycleMe.Where((c, i) =>
!delimns.Any(d => i >= d.First && i <= d.Last));
Console.WriteLine(new string(cut.ToArray());
}
That means I am basically only selecting letters, at positions which are not part of any cutting range.
Also: Fix your naming. A delimiter is a character, not a position (numeric)
I want to know how can I replace a character of a string with condition of "except last number characters"?
Example:
string = "4111111111111111";
And I want to make it that
new_string = "XXXXXXXXXXXXX1111"
In this example I replace the character to "X" except the last 4 characters.
How can I possibly achieve this?
Would that suit you?
var input = "4111111111111111";
var length = input.Length;
var result = new String('X', length - 4) + input.Substring(length - 4);
Console.WriteLine(result);
// Ouput: XXXXXXXXXXXX1111
How about something like...
new_string = new String('X', YourString.Length - 4)
+ YourString.Substring(YourString.Length - 4);
create a new string based on the length of the current string -4 and just have it all "X"s. Then add on the last 4 characters of the original string
Here's a way to think through it. Call the last number characters to leave n:
How many characters will be replaced by X? The length of the string minus n.
How can we replace characters with other characters? You can't directly modify a string, but you can build a new one.
How to get the last n characters from the original string? There's a couple ways to do this, but the simplest is probably Substring, which allows us to grab part of a string by specifying the starting point and optionally the ending point.
So it would look something like this (where n is the number of characters to leave from the original, and str is the original string - string can't be the name of your variable because it's a reserved keyword):
// 2. Start with a blank string
var new_string = "";
// 1. Replace first Length - n characters with X
for (var i = 0; i < str.Length - n; i++)
new_string += "X";
// 3. Add in the last n characters from original string.
new_string += str.Substring(str.Length - n);
This might be a little Overkill for your ask. But here is a quick extension method that does this.
it defaults to using x as the masking Char but can be changed with an optional char
public static class Masking
{
public static string MaskAllButLast(this string input, int charsToDisplay, char maskingChar = 'x')
{
int charsToMask = input.Length - charsToDisplay;
return charsToMask > 0 ? $"{new string(maskingChar, charsToMask)}{input.Substring(charsToMask)}" : input;
}
}
Here a unit tests to prove it works
using Xunit;
namespace Tests
{
public class MaskingTest
{
[Theory]
[InlineData("ThisIsATest", 4, 'x', "xxxxxxxTest")]
[InlineData("Test", 4, null, "Test")]
[InlineData("ThisIsATest", 4, '*', "*******Test")]
[InlineData("Test", 16, 'x', "Test")]
[InlineData("Test", 0, 'y', "yyyy")]
public void Testing_Masking(string input, int charToDisplay, char maskingChar, string expected)
{
//Act
string actual = input.MaskAllButLast(charToDisplay, maskingChar);
//Assert
Assert.Equal(expected, actual);
}
}
}
StringBuilder sb = new StringBuilder();
Char[] stringChar = string.toCharArray();
for(int x = 0; x < stringChar.length-4; x++){
sb.append(stringChar[x]);
}
sb.append(string.substring(string.length()-4));
string = sb.toString();
I guess you could use Select with index
string input = "4111111111111111";
string new_string = new string(input.Select((c, i) => i < input.Length - 4 ? 'X' : c).ToArray());
Some of the other concise answers here did not account for strings less than n characters. Here's my take:
var length = input.Length;
input = length > 4 ? new String('*', length - 4) + input.Substring(length - 4) : input;
lui,
Please Try this one...
string dispString = DisplayString("4111111111111111", 4);
Create One function with pass original string and no of digit.
public string DisplayString(string strOriginal,int lastDigit)
{
string strResult = new String('X', strOriginal.Length - lastDigit) + strOriginal.Substring(strOriginal.Length - lastDigit);
return strResult;
}
May be help you....
Try this:
String maskedString = "...."+ (testString.substring(testString.length() - 4, testString.length()));
Late to the party but I also wanted to mask all but the last 'x' characters, but only mask numbers or letters so that any - ( ), other formatting, etc would still be shown. Here's my quick extension method that does this - hopefully it helps someone. I started with the example from Luke Hammer, then changed the guts to fit my needs.
public static string MaskOnlyChars(this string input, int charsToDisplay, char maskingChar = 'x')
{
StringBuilder sbOutput = new StringBuilder();
int intMaskCount = input.Length - charsToDisplay;
if (intMaskCount > 0) //only mask if string is longer than requested unmasked chars
{
for (var intloop = 0; intloop < input.Length; intloop++)
{
char charCurr = Char.Parse(input.Substring(intloop, 1));
byte[] charByte = Encoding.ASCII.GetBytes(charCurr.ToString());
int intCurrAscii = charByte[0];
if (intloop <= (intMaskCount - 1))
{
switch (intCurrAscii)
{
case int n when (n >= 48 && n <= 57):
//0-9
sbOutput.Append(maskingChar);
break;
case int n when (n >= 65 && n <= 90):
//A-Z
sbOutput.Append(maskingChar);
break;
case int n when (n >= 97 && n <= 122):
//a-z
sbOutput.Append(maskingChar);
break;
default:
//Leave other characters unmasked
sbOutput.Append(charCurr);
break;
}
}
else
{
//Characters at end to remain unmasked
sbOutput.Append(charCurr);
}
}
}
else
{
//if not enough characters to mask, show unaltered input
return input;
}
return sbOutput.ToString();
}
Let's say I have a text and I want to locate the positions of each comma. The string, a shorter version, would look like this:
string s = "A lot, of text, with commas, here and,there";
Ideally, I would use something like:
int[] i = s.indexOf(',');
but since indexOf only returns the first comma, I instead do:
List<int> list = new List<int>();
for (int i = 0; i < s.Length; i++)
{
if (s[i] == ',')
list.Add(i);
}
Is there an alternative, more optimized way of doing this?
Here I got a extension method for that, for the same use as IndexOf:
public static IEnumerable<int> AllIndexesOf(this string str, string searchstring)
{
int minIndex = str.IndexOf(searchstring);
while (minIndex != -1)
{
yield return minIndex;
minIndex = str.IndexOf(searchstring, minIndex + searchstring.Length);
}
}
so you can use
s.AllIndexesOf(","); // 5 14 27 37
https://dotnetfiddle.net/DZdQ0L
You could use Regex.Matches(string, string) method. This will return a MatchCollection and then you could determine the Match.Index. MSDN has a good example,
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = #"\b\w+es\b";
string sentence = "Who writes these notes?";
foreach (Match match in Regex.Matches(sentence, pattern))
Console.WriteLine("Found '{0}' at position {1}",
match.Value, match.Index);
}
}
// The example displays the following output:
// Found 'writes' at position 4
// Found 'notes' at position 17
IndexOf also allows you to add another parameter for where to start looking. You can set that parameter to be the last known comma location +1. For example:
string s = "A lot, of text, with commas, here and, there";
int loc = s.IndexOf(',');
while (loc != -1) {
Console.WriteLine(loc);
loc = s.IndexOf(',', loc + 1);
}
You could use the overload of the IndexOf method that also takes a start index to get the following comma, but you would still have to do that in a loop, and it would perform pretty much the same as the code that you have.
You could use a regular expression to find all commas, but that produces quite some overhead, so that's not more optimised than what you have.
You could write a LINQ query to do it in a different way, but that also has some overhead so it's not more optimised than what you have.
So, there are many alternative ways, but not any way that is more optimised.
A bit unorthodox, but why not use a split? Might be less aggressive than iterating over the entire string
string longString = "Some, string, with, commas.";
string[] splitString = longString.Split(",");
int numSplits = splitString.Length - 1;
Debug.Log("number of commas "+numSplits);
Debug.Log("first comma index = "+GetIndex(splitString, 0)+" second comma index = "+GetIndex(splitString, 1));
public int GetIndex(string[] stringArray, int num)
{
int charIndex = 0;
for (int n = num; n >= 0; n--)
{
charIndex+=stringArray[n].Length;
}
return charIndex + num;
}