Extracting digits from a string five at a time in C# - c#

I am attempting to take numbers (characters 0-9) in from a file and store them in memory.
Lets say we have a string called "register" (and can only (must) hold 5 chars max) and the register string will take in numbers that are read from the file so for example:
File1.txt:
The house number is 10 and the price is 4000 and 3.
So the register would be filled with the following: "10400"
Some logic would then be performed against the string and then the first char would be removed from string and everything would shift 1 to the left and another char (number) from the file would be added e.g.:
04000
and then...
40003
Hopefully somebody could shed some light on this and provide some ways of achieving this :)

Well, if you want to lop the first character off a string and add on one at the end, you can just say:
string s = "10400";
string t = s.Substring(1) + "0";
This gives t = "04000". Repeating:
string u = t.Substring(1) + "3";
This gives u = "40003".
So, what more do you want? Figuring out the logic of what to add to the end is your job.

OK...
First, a FileStream and associated StreamReaders will allow you to read from the file in pretty much any format you desire. This will be important because your specific algorithm will determine the retrieval method.
Boiling it down, you want to read characters from the file, and when that character is a number, store it in the register, continuing in this manner until you have five number characters in the register. Then, you'll do some logic that results in the first number no longer being useful, so you truncate it and get the next value.
How about something along these lines?
var register = new StringBuilder();
using(var stream = File.Open("File1.txt"))
{
bool ended, fileEnded;
int buffer;
while(!ended)
{
while(register.Length < 5 && !fileEnded)
{
buffer = stream.ReadByte();
if(buffer == -1)
{
fileEnded = true;
break;
}
var myChar = (char)buffer;
if(Char.IsNumber(myChar))
StringBuilder.Append(myChar);
}
//at this point you have 5 characters in register (or have run out of file).
//perform your logic, then remove the front character
register.Remove(0,1);
//repeat the loop. You won't get any more new characters once you reach the end of file,
//but the main loop will keep running until you set ended to true
if(WereDone())
ended=true;
}
stream.Close();
}
You could also read the entire file into a string variable, then apply a Regex that will find number characters, concatenate those into a large buffer, then fill your Register from that. That is a better approach for a small file, but this one will work for any file size.

You can create an extension method to List like so:
static class Helper
{
public static void Push<T>(this List<T> list, T item)
{
if (list.Count == 5)
list.RemoveAt(0);
list.Add(item);
}
}
And then you can use it like:
List<char> queue = new List<char>(5);
queue.Push('1');
queue.Push('0');
queue.Push('4');
queue.Push('0');
queue.Push('0');
Subsequent Call to Push will remove the first char and add the last
queue.Push('1');

I would likely put a method for fetching the correct string into a value. See below for an example:
static string FetchRegister(string Source, int Max, int StartIndex)
{
string Register = string.Empty;
int RegisterIndex = 0;
for (int i = 0; i < Source.Length; i++)
{
if (char.IsNumber(Source[i]))
{
if (RegisterIndex >= StartIndex)
{
Register += Source[i].ToString();
if (Register.Length == Max)
{
return Register;
}
}
RegisterIndex += 1;
}
}
return Register;
}

Related

How can I copy an Array to another minus user specified Elements AND write and replace the new list the a .txt file

Here is what I have so far, obviously you can subtract arrays the way i did. And I also need to know how to write the new list to a .txt file that i already have ("records.txt")
public static int deleteRecord(string num)
{
int amount;
int.TryParse(num, out amount);
string[] arrayRecords = File.ReadAllLines("Records.txt").ToArray();
string[] newArrayRecords = arrayRecords - arrayRecords[amount];
for (int i = 0; i < amount; i++)
{
Console.WriteLine(newArrayRecords[amount]);
}
Console.WriteLine(amount);
return amount;
}
I assume that you want to delete a particular value from a file and that is why you have chosen the "num" parameter to be a string.
If so then this will work:
public static void deleteRecord(string num)
{
var lines = File.ReadAllLines("Records.txt").ToList();
if (lines.Remove(num) == true)
{
File.WriteAllLines("Records.txt", lines.ToArray<string>());
}
}
There are a couple of things to point out in your code. Firstly in your example, if you couldn't convert num to an int then you would be trying to remove the value of 0 from your file - which you may not want.
Secondly File.ReadAllLines already returns an Array of strings, so you don't need the .ToArray() at the end. In fact that converts the string[] array to an object[] array - which is not what you want.
I've converted it to a List as they are easier to work with. I only save the file if the item has been removed.
Hope that helps...
I presume that you want to remove the line that contains specified amount, if so you can try this:
var lines = File.ReadLines("Records.txt")
.Where(x => !x.Contains(amount.ToString());
// this will replace all prev. lines with the new ones
File.WriteAllLines("Records.txt", lines);
If you want to remove all lines that comes before this line then you can try:
var allLines = File.ReadLines("Records.txt");
var line = allLines.Where(x => x.Contains(amount.ToString()).First();
var lineIndex = allLines.IndexOf(line);
File.WriteAllLines("Records.txt",lines.GetRange(lineIndex, allLines.Count - lineIndex));
Ofcourse that answer assumes that there is line that contains amount.If there isn't then second code snippet could possibly throw exception.

How to store every 2 characters in c#

Is there a way to store every 2 characters in a string?
e.g.
1+2-3-2-3+
So it would be "1+", "2-", "3-", "2-", "3+" as separate strings or in an array.
The simplest way would be to walk your string with a loop, and take two-character substrings from the current position:
var res = new List<string>();
for (int i = 0 ; i < str.Length ; i += 2)
res.Add(str.Substring(i, 2));
An advanced solution would do the same thing with LINQ, and avoid an explicit loop:
var res = Enumerable
.Range(0, str.Length/2)
.Select(i => str.Substring(2*i, 2))
.ToList();
The second solution is somewhat more compact, but it is harder to understand, at least to someone not closely familiar with LINQ.
This is a good problem for a regular expressio. You could try:
\d[+-]
Just find how to compile that regular expression (HINT) and call a method that returns all occurrences.
Use a for loop, and extract the characters using the string.Substring() method, ensuring you do not go over the length of the string.
e.g.
string x = "1+2-3-2-3+";
const int LENGTH_OF_SPLIT = 2;
for(int i = 0; i < x.Length(); i += LENGTH_OF_SPLIT)
{
string temp = null; // temporary storage, that will contain the characters
// if index (i) + the length of the split is less than the
// length of the string, then we will go out of bounds (i.e.
// there is more characters to extract)
if((LENGTH_OF_SPLIT + i) < x.Length())
{
temp = x.Substring(i, LENGTH_OF_SPLIT);
}
// otherwise, we'll break out of the loop
// or just extract the rest of the string, or do something else
else
{
// you can possibly just make temp equal to the rest of the characters
// i.e.
// temp = x.Substring(i);
break; // break out of the loop, since we're over the length of the string
}
// use temp
// e.g.
// Print it out, or put it in a list
// Console.WriteLine(temp);
}

Constantly Incrementing String

So, what I'm trying to do this something like this: (example)
a,b,c,d.. etc. aa,ab,ac.. etc. ba,bb,bc, etc.
So, this can essentially be explained as generally increasing and just printing all possible variations, starting at a. So far, I've been able to do it with one letter, starting out like this:
for (int i = 97; i <= 122; i++)
{
item = (char)i
}
But, I'm unable to eventually add the second letter, third letter, and so forth. Is anyone able to provide input? Thanks.
Since there hasn't been a solution so far that would literally "increment a string", here is one that does:
static string Increment(string s) {
if (s.All(c => c == 'z')) {
return new string('a', s.Length + 1);
}
var res = s.ToCharArray();
var pos = res.Length - 1;
do {
if (res[pos] != 'z') {
res[pos]++;
break;
}
res[pos--] = 'a';
} while (true);
return new string(res);
}
The idea is simple: pretend that letters are your digits, and do an increment the way they teach in an elementary school. Start from the rightmost "digit", and increment it. If you hit a nine (which is 'z' in our system), move on to the prior digit; otherwise, you are done incrementing.
The obvious special case is when the "number" is composed entirely of nines. This is when your "counter" needs to roll to the next size up, and add a "digit". This special condition is checked at the beginning of the method: if the string is composed of N letters 'z', a string of N+1 letter 'a's is returned.
Here is a link to a quick demonstration of this code on ideone.
Each iteration of Your for loop is completely
overwriting what is in "item" - the for loop is just assigning one character "i" at a time
If item is a String, Use something like this:
item = "";
for (int i = 97; i <= 122; i++)
{
item += (char)i;
}
something to the affect of
public string IncrementString(string value)
{
if (string.IsNullOrEmpty(value)) return "a";
var chars = value.ToArray();
var last = chars.Last();
if(char.ToByte() == 122)
return value + "a";
return value.SubString(0, value.Length) + (char)(char.ToByte()+1);
}
you'll probably need to convert the char to a byte. That can be encapsulated in an extension method like static int ToByte(this char);
StringBuilder is a better choice when building large amounts of strings. so you may want to consider using that instead of string concatenation.
Another way to look at this is that you want to count in base 26. The computer is very good at counting and since it always has to convert from base 2 (binary), which is the way it stores values, to base 10 (decimal--the number system you and I generally think in), converting to different number bases is also very easy.
There's a general base converter here https://stackoverflow.com/a/3265796/351385 which converts an array of bytes to an arbitrary base. Once you have a good understanding of number bases and can understand that code, it's a simple matter to create a base 26 counter that counts in binary, but converts to base 26 for display.

C# Check every other character is a '0'

I'm reading in a text file using BinaryReader, then doing what I want with it (stripping characters, etc..), then writing it out using BinaryWriter.
Nice and simple.
One of the things I need to do before I strip anything is to:
Check that the amount of characters in the file is even (obviously file.Length % 2) and
If the length is even, check that every preceding character is a zero.
For example:
0, 10, 0, 20, 0, 30, 0, 40.
I need to verify that every second character is a zero.
Any ideas? Some sort of clever for loop?
OKAY!
I need to be a lot more clear about what I'm doing. I have file.txt that contains 'records'. Let's just say it's a comma delimited file. Now, What my program needs to do is read through this file, byte by byte, and strip all of the characters we don't want. I have done that. But, some of the files that will be going through this program will be single byte, and some will be double byte. I need to deal with both of these possibilities. But, I need to figure out whether the file is single or double byte in the first place.
Now, obviously if the file is double byte:
The file length will be divisible by 2 and
Every preceding character will be a zero.
and THAT'S why I need to do this.
I hope this clears some stuff up..
UPDATE!
I'm just going to have a boolean in the arguments - is16Bit. Thanks for your help guys! I would have rather deleted the question but it won't let me..
Something like this in a static class:
public static IEnumerable<T> EveryOther(this IEnumerable<T> list)
{
bool send = true;
foreach(var item in list)
{
if (send) yield return item;
send = !send;
}
}
and then (using the namespace of the previous class)
bool everyOtherIsZero = theBytes.EveryOther().All(c => c == 0);
string[] foo = file.text.Split(new{','}, StringSplitOptions.RemoveEmptyEntries);
for(int i=0; i<foo .Length; i+=2)
{
if(file[i]!="0")
return false;
}
How about this
string content = File.ReadAllText(#"c:\test.txt");
if (content.Length % 2 != 0)
throw new Exception("not even");
for(int i = 0; i < content.Length; i+=2)
if (content[i] != '0')
throw new Exception("no zero found");

Testing for repeated characters in a string

I'm doing some work with strings, and I have a scenario where I need to determine if a string (usually a small one < 10 characters) contains repeated characters.
`ABCDE` // does not contain repeats
`AABCD` // does contain repeats, ie A is repeated
I can loop through the string.ToCharArray() and test each character against every other character in the char[], but I feel like I am missing something obvious.... maybe I just need coffee. Can anyone help?
EDIT:
The string will be sorted, so order is not important so ABCDA => AABCD
The frequency of repeats is also important, so I need to know if the repeat is pair or triplet etc.
If the string is sorted, you could just remember each character in turn and check to make sure the next character is never identical to the last character.
Other than that, for strings under ten characters, just testing each character against all the rest is probably as fast or faster than most other things. A bit vector, as suggested by another commenter, may be faster (helps if you have a small set of legal characters.)
Bonus: here's a slick LINQ solution to implement Jon's functionality:
int longestRun =
s.Select((c, i) => s.Substring(i).TakeWhile(x => x == c).Count()).Max();
So, OK, it's not very fast! You got a problem with that?!
:-)
If the string is short, then just looping and testing may well be the simplest and most efficient way. I mean you could create a hash set (in whatever platform you're using) and iterate through the characters, failing if the character is already in the set and adding it to the set otherwise - but that's only likely to provide any benefit when the strings are longer.
EDIT: Now that we know it's sorted, mquander's answer is the best one IMO. Here's an implementation:
public static bool IsSortedNoRepeats(string text)
{
if (text.Length == 0)
{
return true;
}
char current = text[0];
for (int i=1; i < text.Length; i++)
{
char next = text[i];
if (next <= current)
{
return false;
}
current = next;
}
return true;
}
A shorter alternative if you don't mind repeating the indexer use:
public static bool IsSortedNoRepeats(string text)
{
for (int i=1; i < text.Length; i++)
{
if (text[i] <= text[i-1])
{
return false;
}
}
return true;
}
EDIT: Okay, with the "frequency" side, I'll turn the problem round a bit. I'm still going to assume that the string is sorted, so what we want to know is the length of the longest run. When there are no repeats, the longest run length will be 0 (for an empty string) or 1 (for a non-empty string). Otherwise, it'll be 2 or more.
First a string-specific version:
public static int LongestRun(string text)
{
if (text.Length == 0)
{
return 0;
}
char current = text[0];
int currentRun = 1;
int bestRun = 0;
for (int i=1; i < text.Length; i++)
{
if (current != text[i])
{
bestRun = Math.Max(currentRun, bestRun);
currentRun = 0;
current = text[i];
}
currentRun++;
}
// It's possible that the final run is the best one
return Math.Max(currentRun, bestRun);
}
Now we can also do this as a general extension method on IEnumerable<T>:
public static int LongestRun(this IEnumerable<T> source)
{
bool first = true;
T current = default(T);
int currentRun = 0;
int bestRun = 0;
foreach (T element in source)
{
if (first || !EqualityComparer<T>.Default(element, current))
{
first = false;
bestRun = Math.Max(currentRun, bestRun);
currentRun = 0;
current = element;
}
}
// It's possible that the final run is the best one
return Math.Max(currentRun, bestRun);
}
Then you can call "AABCD".LongestRun() for example.
This will tell you very quickly if a string contains duplicates:
bool containsDups = "ABCDEA".Length != s.Distinct().Count();
It just checks the number of distinct characters against the original length. If they're different, you've got duplicates...
Edit: I guess this doesn't take care of the frequency of dups you noted in your edit though... but some other suggestions here already take care of that, so I won't post the code as I note a number of them already give you a reasonably elegant solution. I particularly like Joe's implementation using LINQ extensions.
Since you're using 3.5, you could do this in one LINQ query:
var results = stringInput
.ToCharArray() // not actually needed, I've left it here to show what's actually happening
.GroupBy(c=>c)
.Where(g=>g.Count()>1)
.Select(g=>new {Letter=g.First(),Count=g.Count()})
;
For each character that appears more than once in the input, this will give you the character and the count of occurances.
I think the easiest way to achieve that is to use this simple regex
bool foundMatch = false;
foundMatch = Regex.IsMatch(yourString, #"(\w)\1");
If you need more information about the match (start, length etc)
Match match = null;
string testString = "ABCDE AABCD";
match = Regex.Match(testString, #"(\w)\1+?");
if (match.Success)
{
string matchText = match.Value; // AA
int matchIndnex = match.Index; // 6
int matchLength = match.Length; // 2
}
How about something like:
string strString = "AA BRA KA DABRA";
var grp = from c in strString.ToCharArray()
group c by c into m
select new { Key = m.Key, Count = m.Count() };
foreach (var item in grp)
{
Console.WriteLine(
string.Format("Character:{0} Appears {1} times",
item.Key.ToString(), item.Count));
}
Update Now, you'd need an array of counters to maintain a count.
Keep a bit array, with one bit representing a unique character. Turn the bit on when you encounter a character, and run over the string once. A mapping of the bit array index and the character set is upto you to decide. Break if you see that a particular bit is on already.
/(.).*\1/
(or whatever the equivalent is in your regex library's syntax)
Not the most efficient, since it will probably backtrack to every character in the string and then scan forward again. And I don't usually advocate regular expressions. But if you want brevity...
I started looking for some info on the net and I got to the following solution.
string input = "aaaaabbcbbbcccddefgg";
char[] chars = input.ToCharArray();
Dictionary<char, int> dictionary = new Dictionary<char,int>();
foreach (char c in chars)
{
if (!dictionary.ContainsKey(c))
{
dictionary[c] = 1; //
}
else
{
dictionary[c]++;
}
}
foreach (KeyValuePair<char, int> combo in dictionary)
{
if (combo.Value > 1) //If the vale of the key is greater than 1 it means the letter is repeated
{
Console.WriteLine("Letter " + combo.Key + " " + "is repeated " + combo.Value.ToString() + " times");
}
}
I hope it helps, I had a job interview in which the interviewer asked me to solve this and I understand it is a common question.
When there is no order to work on you could use a dictionary to keep the counts:
String input = "AABCD";
var result = new Dictionary<Char, int>(26);
var chars = input.ToCharArray();
foreach (var c in chars)
{
if (!result.ContainsKey(c))
{
result[c] = 0; // initialize the counter in the result
}
result[c]++;
}
foreach (var charCombo in result)
{
Console.WriteLine("{0}: {1}",charCombo.Key, charCombo.Value);
}
The hash solution Jon was describing is probably the best. You could use a HybridDictionary since that works well with small and large data sets. Where the letter is the key and the value is the frequency. (Update the frequency every time the add fails or the HybridDictionary returns true for .Contains(key))

Categories