Find specific text in a file and replace that - c#

I'm building a WPF program that is editing save-files for a simulation program to make it easier to adjust certain parameters. The structure of that (txt)file looks like this
{Verdieping 1}
A bunch of data
'Max'
15
20
10
]);
More data
{Verdieping 2}
A bunch of data
'Max'
25
10
40
20
]);
More data
{Verdieping 3}
etc.
Because the name of the list I need is always 'Max' i cannot use Replace("Old_Value", "New_Value"); So I need someway to find the header {Verdieping 1} then look for 'Max" and write the values down until it reaches "]);" so i end up with something like this:
Verdieping_1[0] = 15
Verdieping_1[1] = 20
Verdieping_1[0] = 10
Verdieping_2[0] = 25
Verdieping_2[1] = 10
Verdieping_2[2] = 40
Verdieping_2[3] = 20
Then when I change these values I need to write them back into the file in the correct locations. Right now I'm using this code in order to read and ultimately write the new file:
var Model = System.IO.File.ReadAllText(#"C:\Base_Model.txt");
//format code here...
System.IO.File.WriteAllText(#"C:\New_Model.txt", Model);
Could somebody give me some hints or help as to how I could best solve this?
EDIT
This is the actual file structure of the file I want to eddit:
{Atom: Verdieping 2}
int015(0, 64, [
1
2
3
]);
int015(1, 64, [`ActivityID`
20
21
33
]);
int015(2, 64, [`vtp`
139830296
189540320
169919424
]);
int015(3, 64, [`%`
100
100
100
]);
int015(4, 64, [`Max`
15
5
20
]);
int015(5, 64, [`Fill (1 = fill, 0 = empty)`
1
1
0
]);
SetStatus(0);
int018;
{Atom: Verdieping 2}
int015(0, 64, [
1
2
3
]);
int015(1, 64, [`ActivityID`
22
23
24
]);
int015(2, 64, [`vtp`
166058172
165557860
155960564
]);
int015(3, 64, [`%`
100
100
100
]);
int015(4, 64, [`Max`
15
20
10
]);
int015(5, 64, [`Fill (1 = fill, 0 = empty)`
0
1
1
]);
SetStatus(0);
int018;
The 'Max' I was talking about earlier is actually called:
int015(4, 64, [Max
And as you can see that name is identical for each off the layers, the amount of variables are different tho.
Thanks for the help so far!

var result = Regex.Matches(File.ReadAllText(fileName), #"\[`Max`(.*?)\]", RegexOptions.Singleline)
.Cast<Match>()
.Select(m => m.Groups[1].Value.Split(" \t\n\r".ToArray(), StringSplitOptions.RemoveEmptyEntries))
.ToList();
foreach (var v in result)
{
Console.WriteLine(String.Join(", ", v));
}
OUTPUT:
15, 5, 20
15, 20, 10

Regex maybe? I made this fast answer, which shouldn't be copy pasted, however; you can consider the results.
In this answer I am expecting your array to be in chronological order to prevent the source code from being too long and messy.
The file I read for the 'DataContainer' objects look identical with the file below:
{Verdieping 1}
A bunch of data
'Max'
15
20
10
]);
More data
{Verdieping 2}
A bunch of data
'Max'
25
10
40
20
]);
First of all I find each 'Max' in the string
Then I take all the datas below and print them out
The regex used to find this is: \'Max\'.*?\]\);
Try this code out to see the results
// fill our container
string DataContainer = string.Empty;
using (StreamReader sr = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile.txt"))) {
DataContainer = sr.ReadToEnd();
}
// print data
Match regMatch = Regex.Match(DataContainer, #"\'Max\'.*?\]\);", RegexOptions.Singleline);
int verdiepc = 0;
while (regMatch.Success) {
string[] Data = regMatch.Value.Replace("\r", "").Split('\n');
for (int I = 1; I < Data.Length - 1; I++) {
Console.WriteLine("Verdieping_{0}[{1}] = {2}"
, verdiepc, I, Data[I]);
}
Console.Write("\n");
verdiepc ++;
regMatch = regMatch.NextMatch();
}
// prevent immidiate exit
Console.ReadKey();
The output will be:
Verdieping_0[1] = 15
Verdieping_0[2] = 20
Verdieping_0[3] = 10
Verdieping_1[1] = 25
Verdieping_2[2] = 10
........ etc..
I hope you can use this answer. It is a sketch of how you can use Regex to parse the file

string LoadedFile = File.ReadAllText(#"C:\Base_Model.txt");
StringBuilder result = new StringBuilder();
for (int i = 1; i < Int32.MaxValue; i++)
{
Match ma = Regex.Match(LoadedFile, "Verdieping " + i.ToString() + ".+?'Max'.*?([0-9].+?)[]][)];", RegexOptions.Singleline);
if(!ma.Success)
{
break;
}
string a = ma.Result("$1");
MatchCollection mc = Regex.Matches(a, "(\\d+?)\r\n", RegexOptions.Singleline);
for (int j = 0; j < mc.Count; j++)
{
result.AppendLine(String.Format("Verdieping_{0}[{1}] = {2}", i, j, mc[j].Result("$1")));
}
result.AppendLine(String.Empty);
}
File.WriteAllText(#"C:\New_Model.txt", result.ToString());

You may initialize a new List of string which will contain the required output. Then, read the file getting the information between the target section and inserting the values between [ and ]); into the List of string.
Example
static List<string> ReadFile(string Path, string Section)
{
#region Declare Variables
string[] Lines = File.ReadAllLines(Path); //Read Path into Lines
int CurrentValue = 1; //Declare CurrentValue as an Integer of value 1
List<string> RequiredValues = new List<string>(); //Initialize a new List of string of name RequiredValues
#endregion
#region Collect Information
//Look through the section getting the data between [ and ]);
for (int i = 0; i < Lines.Length; i++)
{
if (Lines[i].StartsWith("{" + Section + "}"))
{
i++;
while (!Lines[i].StartsWith("{") && !Lines[i].EndsWith("}"))
{
i++;
if (Lines.Length > i)
{
if (Lines[i].Contains('['))
{
while (true)
{
i++;
if (Lines[i].Contains("]);"))
{
break;
}
RequiredValues.Add(Section + "[" + CurrentValue.ToString() + "] = " + Lines[i]);
CurrentValue++;
}
}
RequiredValues.Add("");
}
else
{
break; //Reached the end of the document
}
}
}
}
#endregion
return RequiredValues; //Return RequiredValues
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<string> Contents = ReadFile(#"C:\Base_Model.txt", "Atom: Verdieping 2");
foreach (string str in Contents)
{
System.Diagnostics.Debug.WriteLine(str); //Write str to the trace listeners
}
}
Sample Input
{Atom: Verdieping 2}
int015(0, 64, [
4
5
6
]);
int015(1, 64, [`ActivityID`
20
21
33
]);
int015(2, 64, [`vtp`
139830296
189540320
169919424
]);
int015(3, 64, [`%`
100
100
100
]);
int015(4, 64, [`Max`
15
5
20
]);
int015(5, 64, [`Fill (1 = fill, 0 = empty)`
1
1
0
]);
SetStatus(0);
int018;
{Atom: Verdieping 1}
int015(0, 64, [
1
2
3
]);
int015(1, 64, [`ActivityID`
22
23
24
]);
int015(2, 64, [`vtp`
166058172
165557860
155960564
]);
int015(3, 64, [`%`
100
100
100
]);
int015(4, 64, [`Max`
15
20
10
]);
int015(5, 64, [`Fill (1 = fill, 0 = empty)`
0
1
1
]);
SetStatus(0);
int018;
Sample Output
The following was returned from reading Atom: Verdieping 2
Atom: Verdieping 2[1] = 4
Atom: Verdieping 2[2] = 5
Atom: Verdieping 2[3] = 6
Atom: Verdieping 2[4] = 20
Atom: Verdieping 2[5] = 21
Atom: Verdieping 2[6] = 33
Atom: Verdieping 2[7] = 139830296
Atom: Verdieping 2[8] = 189540320
Atom: Verdieping 2[9] = 169919424
Atom: Verdieping 2[10] = 100
Atom: Verdieping 2[11] = 100
Atom: Verdieping 2[12] = 100
Atom: Verdieping 2[13] = 15
Atom: Verdieping 2[14] = 5
Atom: Verdieping 2[15] = 20
Atom: Verdieping 2[16] = 1
Atom: Verdieping 2[17] = 1
Atom: Verdieping 2[18] = 0
The following output was returned from reading Atom: Verdieping 1
Atom: Verdieping 1[1] = 1
Atom: Verdieping 1[2] = 2
Atom: Verdieping 1[3] = 3
Atom: Verdieping 1[4] = 22
Atom: Verdieping 1[5] = 23
Atom: Verdieping 1[6] = 24
Atom: Verdieping 1[7] = 166058172
Atom: Verdieping 1[8] = 165557860
Atom: Verdieping 1[9] = 155960564
Atom: Verdieping 1[10] = 100
Atom: Verdieping 1[11] = 100
Atom: Verdieping 1[12] = 100
Atom: Verdieping 1[13] = 15
Atom: Verdieping 1[14] = 20
Atom: Verdieping 1[15] = 10
Atom: Verdieping 1[16] = 0
Atom: Verdieping 1[17] = 1
Atom: Verdieping 1[18] = 1
NOTICE: Using this method, the application will only collect the information between the first section it meets even if you have two sections with the same name.
Thanks,
I hope you find this helpful :)

Related

Unexpected result when generating a gray8 bitmap using C# on Win7

The following code generate a gray8 bitmap image file. It works fine on Win10.
var width = 4;
var height = 4;
var rect = new Int32Rect() {
Width = width,
Height = height,
};
var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, null);
var data = new byte[width * height];
for (var i = 0; i < data.Length; i++)
{
data[i] = (byte)(i * 2);
}
bitmap.WritePixels(rect, data, width, 0);
var encoder = new BmpEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap);
using (var s = new FileStream(filename, FileMode.Create))
{
encoder.Save(s);
}
But when I execute it on Win7, the pixel value is unexpected.
Expected:
0 2 4 6
8 10 12 14
16 18 20 22
24 26 28 30
Actual:
0 0 0 0
0 0 0 0
17 17 17 17
17 17 17 17
I had tested with other value, the generated pixel value is always input / 16 * 17.
The encoder final calls to WindowsCodecs.dll, but I cannot find much info about it.
Any idea about this issue?

C# loading data from a file and sorting into arrays

So I need to read from a file and display it all in a suitable format. So far I can read all the data in and it does show on the form however from the HRData it only reads in 1 full line and displays that on the form I need it to display every line from that specific part of the file. I've put down a snippet of my code where Iread from the file and display the HRData from that file, but I need every line to be in the table and not the repeat of the same line. Any Help would be appreciated. Sorry for the layout of my code I'm still trying the get used to stack over flow so apologies.
DateTime date = new DateTime(2014, 09, 08, 16, 0, 0);
string[] Data1 = Regex.Split(filetext, "HRData]");
Data1[1] = Data1[1].Trim();
string[] HRData = Regex.Split(Data1[1], "\r\n|\r\n");
dataGridView1.ColumnCount = 7;
dataGridView1.ColumnHeadersVisible = true;
DataGridViewCellStyle columnHeaderStyle = new DataGridViewCellStyle();
columnHeaderStyle.BackColor = Color.Beige;
columnHeaderStyle.Font = new Font("Verdana", 10, FontStyle.Bold);
dataGridView1.ColumnHeadersDefaultCellStyle = columnHeaderStyle;
dataGridView1.Columns[0].Name = "Time";
dataGridView1.Columns[1].Name = "Heart Rate (bpm)";
dataGridView1.Columns[2].Name = "Speed";
dataGridView1.Columns[3].Name = "Cadence (rpm)";
dataGridView1.Columns[4].Name = "Altitude (m/ft)";
dataGridView1.Columns[5].Name = "Power (watts)";
dataGridView1.Columns[6].Name = "Power Balance";
if (Versionval == 106)
{
String FindVersionMode = "VersionMode";
int indexofVersionMode = filetext.IndexOf(FindVersionMode);
VersionModeTypeA = filetext.Substring(indexofVersionMode + 5, 1);
VersionModeTypeB = filetext.Substring(indexofVersionMode + 6, 1);
VersionModeTypeC = filetext.Substring(indexofVersionMode + 7, 1);
VersionModeTypeD = filetext.Substring(indexofVersionMode + 8, 1);
VersionModeTypeE = filetext.Substring(indexofVersionMode + 9, 1);
VersionModeTypeF = filetext.Substring(indexofVersionMode + 10, 1);
VersionModeTypeG = filetext.Substring(indexofVersionMode + 11, 1);
VersionModeTypeH = filetext.Substring(indexofVersionMode + 12, 1);
//SpeedType = Int32.Parse(VModeTypeA);
//CadenceType = Int32.Parse(VModeTypeA);
//AltType = Int32.Parse(VModeTypeA);
//PowerType = Int32.Parse(VModeTypeA);
//PowerLRBType = Int32.Parse(VModeTypeA);
//PowerPIType = Int32.Parse(VModeTypeA);
// HRCCType = Int32.Parse(VModeTypeA);
// EuroUsType = int.Parse(VModeTypeA);
dataGridView1.Columns[3].Name = "Cadence (rpm)";
dataGridView1.Columns[4].Name = "Altitude (m/ft)";
dataGridView1.Columns[5].Name = "Power (watts)";
dataGridView1.Columns[6].Name = "Power Balance";
}
for (int i = 0; i < HRData.Length; i++)
{
date = date.AddSeconds(1);
string[] HRDataColumn = Regex.Split(HRData[1], "\t");
double speed1 = Int32.Parse(HRDataColumn[1]);
speed1 = speed1 / 10;
HRDataColumn[1] = speed1.ToString();
dataGridView1.Rows.Add(date.ToString("HH:mm:ss"), HRDataColumn[0], HRDataColumn[1], HRDataColumn[2], HRDataColumn[3], HRDataColumn[4], HRDataColumn[5]);
}
[HRData]
91 154 70 309 83 6451 91 154 70 309 83 6451
92 160 75 309 87 5687 94 173 80 309 87 5687 96 187 87 309 95 4662
100 190 93 309 123 4407 101 192 97 309 141 4915
103 191 98 309 145 5429 106 190 99 309 157 4662
So this is a snippet of the HRData part of the file theirs a lot more data then that, but I cant paste all of it and so far my code only reads in the first line successfully in each column of the table. Keeps repeating the same line.
so the first line that keeps repeating is: 91 154 70 309 83 6451
Thanks
You are always using the same string array HRDataColumn
string[] HRDataColumn = Regex.Split(HRData[1], "\t");
Try to change this line to
string[] HRDataColumn = Regex.Split(HRData[i], "\t");

distribute values of list 1 unequal lenth over list 2 in linear/evenly fashion

I have 2 List<float> of unequal length.
Scores and Grades.
Depending on the configuration one or the other list is greater than the other.
There are 2 scenarios:
Use case 1: 60 scores and 50 grades - 1 to 6 with tenth grades -
I have to distribute 60 scores over 50 grades in an even way.
Thus many grades will have multiple related consecutive scores (1:N relation concerning data structure)
Use case 2: 40 scores and 50 grades - 1 to 6 with tenth grades -
I have to distribute 40 scores over 50 grades in an even way.
Thus 10 grades must be removed (50 - 40) evenly to get a 1:1 relation data structure between scores and grades.
What algorithm`s are available to me that I can solve the problem with C#?
I have written down here the result data I would expect to look like when the implemented algorythm is run.
Use case 1 sample result data:
Scores Grade
0 6
1 5,9
2 - 3 5,8
4 5,7
5 5,6
6 5,5
7 5,4
8 - 9 5,3
10 5,2
11 5,1
12 5
13 4,9
14 - 15 4,8
16 4,7
17 4,6
18 4,5
19 4,4
20 - 21 4,3
22 4,2
23 4,1
24 4
25 3,9
26 - 27 3,8
28 3,7
29 3,6
30 3,5
31 3,4
32 - 33 3,3
34 3,2
35 3,1
36 3
37 2,9
38 - 39 2,8
40 2,7
41 2,6
42 2,5
43 2,4
44 - 45 2,3
46 2,2
47 2,1
48 2
49 1,9
50 - 51 1,8
52 1,7
53 1,6
54 1,5
55 1,4
56 - 57 1,3
58 1,2
59 1,1
60 1
Use case 2 sample result data:
Score Grade
0 6
1 5,9
2 5,8
3 5,6
4 5,5
5 5,4
6 5,3
7 5,1
8 5
9 4,9
10 4,8
11 4,6
12 4,5
13 4,4
14 4,3
15 4,1
16 4
17 3,9
18 3,8
19 3,6
20 3,5
21 3,4
22 3,3
23 3,1
24 3
25 2,9
26 2,8
27 2,6
28 2,5
29 2,4
30 2,3
31 2,1
32 2
33 1,9
34 1,8
35 1,6
36 1,5
37 1,4
38 1,3
39 1,1
40 1
C# converted code for y = f(x) ;-)
int x, y, accum;
int scores = 33;
int grades = 51;
float[] gradesArray = new float[grades];
float[] scoresArray = new float[scores];
for (y = 0; y < grades; y++)
gradesArray[y] = 6.0f - y / 10.0f;
accum = scores / 2;
y = 0;
for (x = 0; x < scores; x++)
{
scoresArray[x] = gradesArray[y];
accum += grades;
while (accum >= scores)
{
y++;
accum -= scores;
}
}
Assume gradesArray is an input array indexed by y, and scoresArray is the output array indexed by x. To generate the output array, for each index in the output array, you need to select the corresponding value from the input array. In pseudo-code:
scoresArray[x] = gradesArray[y] where y = f(x)
In words, each output value scoresArray[x] is taken from location y in the input array gradesArray[y], where y is some function of x. And what is that function? Well it's a line.
Here's some sample code that uses a line drawing algorithm to solve use case 1:
int x, y, accum;
int scores = 61;
int grades = 51;
float[] gradesArray = new float[grades];
float[] scoresArray = new float[scores];
for (y = 0; y < grades; y++)
gradesArray[y] = 6.0f - y / 10.0f;
accum = (scores-1) / 2;
y = 0;
for (x = 0; x < scores; x++)
{
scoresArray[x] = gradesArray[y];
accum += (grades-1);
y += accum / (scores-1);
accum %= (scores-1);
}
To adapt the code for use case 2, just change int scores = 61 to int scores = 41.

Extracting data packet out of byte buffer

I have a buffer of length 256 that receives byte sequences from bluetooth. The actual packet that I need to extract is starting and ending with byte 126. I want to extract the latest packet in the buffer using LINQ.
What I am doing now is checking for last index of 126 and then count backward until I reach another 126. There are some pitfalls as well, for example, two adjacent packet can result in two bytes of 126 next to eachother.
Here is a sample of buffer:
126 6 0 5 232 125 93 126 126 69 0
0 1 0 2 2 34 6 0 5 232 125
93 126 126 69 0 0 1 0 2 2 34
6 0 5 232 125 93 126 126 69 0 0
1 0 2 2 34 6 0 5 232 125 93
126 126 69 0 0
So the information I have is:
Packet starts and ends with byte value of 126
the next byte after the starting index does have value of 69
the 3 last byte right befor the ending byte of 126 is a CRC of the whole packet that I know how to calculate, so after extracting a packet I can check this CRC to see if I have the right packet
So at the end I want to have an array or list that contains the correct packet. for example:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
Can you give me a fast soloution of extracting this packet from buffer?
This is what I'v tried so far....it fails as it cant really return the correct packet I am looking for:
var data = ((byte[])msg.Obj).ToList(); //data is the buffer
byte del = 126; //delimeter or start/end byte
var lastIndex = data.LastIndexOf(del);
var startIndex = 0;
List<byte> tos = new List<byte>(); //a new list to store the result (packet)
//try to figure out start index
if(data[lastIndex - 1] != del)
{
for(int i = lastIndex; i > 0; i--)
{
if(data[i] == del)
{
startIndex = i;
}
}
//add the result in another list
for(int i = 0; i <= lastIndex - startIndex; i++)
{
tos.Add(data[i]);
}
string shit = string.Empty;
foreach (var b in tos)
shit += (int)b + ", ";
//print result in a textbox
AddTextToLogTextView(shit + "\r\n");
}
Solutions
I've prepared three possible solution of taking the last packet from input buffor:
Using LINQ
public static byte[] GetLastPacketUsingLINQ(byte[] input, byte delimiter)
{
var part = input.Reverse()
.SkipWhile(i => i != delimiter)
.SkipWhile(i => i == delimiter)
.TakeWhile(i => i != delimiter)
.Reverse();
return (new byte[] { delimiter }).Concat(part).Concat(new byte[] { delimiter }).ToArray();
}
Using string.Split
public static byte[] GetLastPacketUsingString(byte[] input, byte delimiter)
{
var encoding = System.Text.Encoding.GetEncoding("iso-8859-1");
string inputString = encoding.GetString(input);
var parts = inputString.Split(new[] { (char)delimiter }, StringSplitOptions.RemoveEmptyEntries);
return encoding.GetBytes((char)delimiter + parts[parts.Length - 2] + (char)delimiter);
}
Using while loop and indexers
public static byte[] GetLastPacketUsingIndexers(byte[] input, byte delimiter)
{
int end = input.Length - 1;
while (input[end--] != delimiter) ;
int start = end - 1;
while (input[start--] != delimiter) ;
var result = new byte[end - start];
Array.Copy(input, start + 1, result, 0, result.Length);
return result;
}
Performance
I've also performed some very simple performance tests. Here are the results:
LINQ version result:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
String version result:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
Indexers version result:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
LINQ version time: 64ms (106111 ticks)
String version time: 2ms (3422 ticks)
Indexers version time: 1ms (2359 ticks)
Conclusion
As you can see, the simplest one is also the best one here.
You may think that LINQ is an answer for every problem, but some time it's really better idea to write simpler solution manually instead of using LINQ methods.
Using LINQ this can be done in a single line of code if the following two rules can be applied to the buffer:
The buffer contains at least one complete package surrounded by the
given delimiter.
Each packet contains at least one byte of data.
Here is the code:
var data = (byte[])msg.Obj;
byte delimiter = 126;
var packet = data.Reverse()
.SkipWhile(b => b != delimiter)
.SkipWhile(b => b == delimiter)
.TakeWhile(b => b != delimiter)
.Reverse();
(Ok, this was more than a single line because I splitted it into multiple lines for better readability.)
EDIT: Removed the call to Take(1) because that would always return an empty sequence. The result however doesn't contain delimiter this way.
And here is how it works:
Since we want to find the last packet we can reverse the data:
var reversed = data.Reverse();
The buffer can end with a packet which is not complete yet. So let's skip that:
reversed = reversed.SkipWhile(b => b != delimiter);
reversed is now either empty or it starts with delimiter. Since we assumed that the buffer always contains at least one complete packet we can already take the next byte for our result because we know it is the delimiter:
var packet = reversed.Take(1);
In the sequence we can now skip one byte. If the delimiter we found was actually the start of a new packet the remaining sequence will start with another delimiter so we have to skip that also:
reversed = reversed.Skip(1);
if (reversed.First() == delimiter)
{
reversed.Skip(1);
}
Since we know that a packet can not be empty because it contains a 3 bytes CRC we could have written:
reversed = reversed.SkipWhile(b => b == delimiter);
Now the actual data follows:
packet = packet.Concat(reversed.TakeWhile(b => b != delimiter));
reversed = reversed.SkipWhile(b => b != delimiter);
The next byte is the delimiter which marks the start of the packet:
packet = packet.Concat(reversed.Take(1));
The last thing to do is to reverse the result again:
packet = packet.Reverse();
Maybe you want to put this into a method:
public IEnumerable<byte> GetPacket(byte[] data, byte delimiter)
{
yield return delimiter;
foreach (byte value in data.Reverse()
.SkipWhile(b => b != delimiter)
.SkipWhile(b => b == delimiter)
.TakeWhile(b => b != delimiter))
{
yield return value;
}
yield return delimiter;
}
You will have to call Reverse on the return value of this method.
If performance matters you can use the same algorithm on the underlying array. This way it will be about 20 times faster:
int end = data.Length - 1;
while (data[end] != delimiter)
end--;
while (data[end] == delimiter)
end--;
int start = end;
while (data[start] != delimiter)
start--;
byte[] result = new byte[end - start + 2]; // +2 to include delimiters
Array.Copy(data, start, result, 0, result.Length);
There actually are various ways to solve your question, the simplest idea is detect double 126(0x7e), and doesn't matter other things such like CRC.
The basic implemention of this concept would be like this
Code as simple
var list=new List<byte[]>();
int i=0, j=0;
for(; i<data.Length; ++i)
if(i>0&&0x7e==data[i]&&0x7e==data[i-1]) {
list.Add(data.Skip(j).Take(i-j).ToArray());
j=i;
}
list.Add(data.Skip(j).Take(i-j).ToArray());
Base on my old answer of Konami Code in C#, and it even used to solve this question: Double characters shown when typing special characters while logging keystrokes in c#.
Code with a sequence detector
public partial class TestClass {
public static void TestMethod() {
var data=(
new[] {
126, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0
}).Select(x => (byte)x).ToArray();
var list=new List<List<byte>>();
foreach(var x in data) {
if(list.Count<1||SequenceCapturer.Captured((int)x))
list.Add(new List<byte>());
list.Last().Add(x);
}
foreach(var byteList in list)
Debug.Print("{0}", byteList.Select(x => x.ToString("x2")).Aggregate((a, b) => a+"\x20"+b));
}
}
public class SequenceCapturer {
public int Count {
private set;
get;
}
public int[] Sequence {
set;
get;
}
public bool Captures(int value) {
for(var i=Sequence.Length; i-->0; ) {
if(Sequence[i]!=value) {
if(0==i)
Count=0;
continue;
}
if(Count!=i)
continue;
++Count;
break;
}
var x=Sequence.Length==Count;
Count=x?0:Count;
return x;
}
public SequenceCapturer(int[] newSequence) {
Sequence=newSequence;
}
public SequenceCapturer()
: this(new[] { 0x7e, 0x7e }) {
}
public static bool Captured(int value) {
return Instance.Captures(value);
}
public static SequenceCapturer Instance=new SequenceCapturer();
}
Or if you would like to write it full in Linq, you might want to try the following. You even don't need to use List, packetArray gives you an array of byte arrays directly.
The lets are intended to break the code into lines, otherwise it would be an extreme long statement in one line. If you consider one line is the best, then I will.
Code of packetArray
var packetArray=(
from sig in new[] { new byte[] { 0x7e, 0x7e } }
let find=new Func<byte[], int, IEnumerable<byte>>((x, i) => x.Skip(i).Take(sig.Length))
let isMatch=new Func<IEnumerable<byte>, bool>(sig.SequenceEqual)
let filtered=data.Select((x, i) => 0==i||isMatch(find(data, i-1))?i:~0)
let indices=filtered.Where(i => ~0!=i).Concat(new[] { data.Length }).ToArray()
from index in Enumerable.Range(1, indices.Length-1)
let skipped=indices[index-1]
select data.Skip(skipped).Take(indices[index]-skipped).ToArray()).ToArray();
Code for output
foreach(var byteArray in packetArray)
Debug.Print("{0}", byteArray.Select(x => x.ToString("x2")).Aggregate((a, b) => a+"\x20"+b));
However, even in the same concept of solution, there would be various ways as I mentioned before. I'd strongly recommend that don't involve additional conditions like something about CRC, which might make things more complicated.
Since you're looking for the last packet, it's much easier to reverse the byte[] and look for the first packet. Your two packet delimiters are not just 126. They are 126, 69 for the start and 126, 126 for the end unless the end of the packet is the last byte recieved, which makes the end delimiter 126.
I would suggest using a method simular to this:
public static byte[] GetMessage(byte[] msg)
{
//Set delimiters
byte delimit = 126;
byte startDelimit = 69;
//Reverse the msg so we can find the last packet
List<byte> buf = msg.Reverse().ToList();
//set indices to impossible values to check for failures
int startIndex = -1;
int endIndex = -1;
//loop through the message
for (int i = 0; i < buf.Count - 1; i++)
{
//find either a double 126, or 126 as the last byte (message just ended)
if (buf[i] == delimit && (buf[i + 1] == delimit || i == 0))
{
if (i == 0)
{
startIndex = i;
i++;
}
else
{
startIndex = i + 1;
i += 2;
}
continue;
}
//Only process if we've found the start index
if (startIndex != -1)
{
//check if the byte is 69 followed by 126
if (buf[i] == startDelimit && buf[i + 1] == delimit)
{
endIndex = i + 1;
break;
}
}
}
//make sure we've found a message
if (!(startIndex == -1 || endIndex==-1))
{
//get the message and reverse it to be the original packet
byte[] revRet = new byte[endIndex - startIndex];
Array.Copy(buf.ToArray(), startIndex, revRet, 0, endIndex - startIndex);
return revRet.Reverse().ToArray();
}
return new byte[1];
}
I'm not totally sure if the indices of the copy are completely correct, but this should be the jist of it.
since you may receive incomplete data, you must store the last incomplete buffer.
this is sample case, First Receive :
126, 6, 0, 5, 232, 125, 93, 126, 126, 69, 0,
0, 1, 0, 2, 2, 34, 6 , 0 , 5 , 232, 125,
93, 126, 126, 69, 0, 0, 1 , 0, 2, 2, 34,
6, 0, 5, 232, 125, 93, 126, 126, 69, 0, 0 ,
1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93,
126, 126, 69, 0, 0
the second stream :
69, 0, 0 , 1, 0, 2, 2, 34, 6, 0, 126
and the code:
List<byte> lastBuf = new List<byte>();
List<byte[]> Extract(byte[] data, byte delim)
{
List<byte[]> result = new List<byte[]>();
for (int i = 0; i < data.Length; i++)
{
if (lastBuf.Count > 0)
{
if(data[i] == delim)
{
result.Add(lastBuf.ToArray());
lastBuf.Clear();
}
else
{
lastBuf.Add(data[i]);
}
}
else
{
if(data[i] != 126)
{
lastBuf.Add(data[i]);
}
}
}
return result;
}
result :

Music major scale converter

i am trying to create music major scale converter.
Dose anyone have info how do to it
so far i have
rootNote is scale base note like cMajor or gMajor
note is note that i want to convert into major scale 0-126
if i insert rootNote 60 and note 60 the right return would be 0,
if i insert rootNote 60 and note 61 the right return would be 2,
if i insert rootNote 60 and note 62 the right return would be 4,
if i insert rootNote 60 and note 63 the right return would be 5,
if i insert rootNote 61 and note 60 the right return would be 0,
if i insert rootNote 61 and note 61 the right return would be 1,
if i insert rootNote 61 and note 62 the right return would be 3,
if i insert rootNote 61 and note 63 the right return would be 5,
ok i have this other one and it seems to work
i want to map my sequence out put in major scale
but is there some kind of formula what can i use?
.
public int getINMajorScale(int note, int rootNote)
{
List<int> majorScale = new List<int>();
//int bNote = (int)_bNote.CurrentValue;
int bNoteMpl = bNote / 12;
bNote = 12 + (bNote - (12 * bNoteMpl)) - 7;
majorScale.Add(bNote + (12 * bNoteMpl));
int tBnote = bNote;
int res = 0;
for (int i = bNote; i < bNote + 6; i++)
{
//algorytm
res = tBnote + 7;
int mod = 0;
if (res >= 12)
{
mod = res / 12;
res = res - 12 * mod;
}
tBnote = res;
majorScale.Add(res + (bNoteMpl * 12));
}
majorScale.Sort();
int modNuller = 0;
if (nmr >= 7)
{
modNuller = nmr / 7;
nmr = nmr - 7 * modNuller;
}
return (majorScale[nmr] + (modNuller *12));
}
but it's obviously faulty.
Problems with the code as it stands:
modScaling does nothing more than rootNote % 12 as you always pass in 0 and 11
You define mNote but never use it
i is never used in the for loop and so each of the 5 iterations prints the same thing.
OK, lets translate your examples into actual notes to make it easier to understand (numbers presumably correspond to MIDI notes):
rootNote = 60 (C), note = 60 (C) - output 0
rootNote = 60 (C), note = 61 (C#) - output 2
rootNote = 60 (C), note = 62 (D) - output 4
rootNote = 60 (C), note = 63 (D#) - output 5
rootNote = 61 (C#), note = 60 (C) - output 0
rootNote = 61 (C#), note = 61 (C#) - output 1
rootNote = 61 (C#), note = 62 (D) - output 3
rootNote = 61 (C#), note = 63 (D#) - output 5
I might be being really dense but I'm afraid I can't see the pattern there.
A Major scale is of course made up of the sequence Tone, Tone, Semi-tone, Tone, Tone, Tone, Semi-tone, but how does that map to your outputs?
Given your input-outputs, I think I know what you are looking for.
determine steps = note - rootNote
determine interval = number of semi-tones between rootNote and the note steps up the scale
determine phase = rootNote - 60
This algorithm provides accurate results:
static int getINMajorScale(int note, int rootNote)
{
if (note < rootNote) return 0;
var scale = new[] { 2, 2, 1, 2, 2, 2, 1 };
var phase = rootNote - 60;
var steps = note - rootNote;
var interval = steps == 0
? 0 : Enumerable.Range(0, steps).Sum(step => scale[step % scale.Length]);
var number = phase + interval;
return number;
}
yielding:
static void Main(string[] args)
{
//rootNote = 60(C), note = 60(C) - output 0
//rootNote = 60(C), note = 61(C#) - output 2
//rootNote = 60(C), note = 62(D) - output 4
//rootNote = 60(C), note = 63(D#) - output 5
//rootNote = 61(C#), note = 60 (C) - output 0
//rootNote = 61(C#), note = 61 (C#) - output 1
//rootNote = 61(C#), note = 62 (D) - output 3
//rootNote = 61(C#), note = 63 (D#) - output 5
Console.WriteLine(getINMajorScale(60, 60)); // 0
Console.WriteLine(getINMajorScale(61, 60)); // 2
Console.WriteLine(getINMajorScale(62, 60)); // 4
Console.WriteLine(getINMajorScale(63, 60)); // 5
Console.WriteLine(getINMajorScale(60, 61)); // 0
Console.WriteLine(getINMajorScale(61, 61)); // 1
Console.WriteLine(getINMajorScale(62, 61)); // 3
Console.WriteLine(getINMajorScale(63, 61)); // 5
Console.ReadKey();
}

Categories