A Loop unnecessarily requires MessageBox, freezes if removed - c#

I have a C# code that when it runs, it feeds approximately 200 values into separate lines in a multi-line enabled textbox but a messagebox in the middle, which I put just to be sure it's feeding the correct data, now freezes the whole code if removed. I couldn't understand the significance of it, previous textboxes handled heavier loads with little trouble but apparently this one require a short break or at least some sort of human interaction(luckily only pressing the OK button on a messagebox) so I was wondering if there is a solution or a way around it like a similar stop but not requiring my guidance. It's not convenient to press a key 200+ times just to get one group of data out of many.
Below is the code, just in case:
if (listBox3.SelectedIndex < 0 || listBox4.SelectedIndex < 0) {
MessageBox.Show("Select the Chart Parameters(E.g.: BTC-USD)");
}
if (listBox4.GetItemText(listBox4.SelectedItem).Contains(listBox3.GetItemText(listBox3.SelectedItem))) {
MessageBox.Show("Please select seperate pairs.(E.g.: NOT BTC-BTC but e.g.:BTC-USD)");
}
else
{
string url = textBox8.Text + listBox3.GetItemText(listBox3.SelectedItem) + listBox4.GetItemText(listBox4.SelectedItem);
MessageBox.Show(url);
System.Net.WebClient client8 = new System.Net.WebClient();
var html = client8.DownloadString(url);
string s = html.Replace("[[", "[");
string ss = s.Replace("]]", "]");
textBox9.Text = ss;
dynamic obj1 = JsonConvert.DeserializeObject(ss); //ss is the data
var timezz = (Int32)obj1.SelectToken("time");
textBox11.Text = textBox11.Text + timezz;
var data1m = (string)obj1.SelectToken("data1m");
ICollection<string> matches =
Regex.Matches(data1m, #"\[([^]]*)\]", RegexOptions.Multiline)
.Cast<Match>()
.Select(x => x.Groups[1].Value)
.ToList();
foreach (string match in matches) { textBox10.Text += Environment.NewLine + match; }
for (int i = 1; i < textBox10.Lines.Length; i++)
{
string linebyline = textBox10.Lines[i];
var resultString = Regex.Match(linebyline, #"\d+").Value;
//uni_time = resultString;
var yen = linebyline.Replace(resultString, string.Empty);
MessageBox.Show(yen);
/////WHY DOES IT NEEDS TO BE STOPPED BY THIS MSGBOX INORDER2 EXECUTE THE WHOLE CODE???
//////IF I REMOVE IT, IT FREEZES
var resultz = yen.Substring(yen.LastIndexOf(',') + 1);
//uni_vol = resultz;
var ztring = yen.Replace(resultz, string.Empty);
//MessageBox.Show(ztring);
//Regex re = new Regex(#"\d+(\.\d{1,4})?");
ICollection<string> mc = Regex.Matches(ztring, #"\d+(\.\d{1,4})?").Cast<Match>()
.Select(x => x.Groups[0].Value)
.ToList();
foreach (string m in mc)
{
//MessageBox.Show(m);
if (textBox13.Text == string.Empty) { textBox13.Text += m; }
else
{
textBox13.Text += Environment.NewLine + m;
}
}
}
}

Related

Search anywhere in DataGridView

I'm trying to write a program which will made a datagridview based on a text file. See code below.
private void Button1_Click(object sender, EventArgs e)
{
ls_datenTabelle.Clear();
ls_datenTabelle.Columns.Clear();
string kdstr = (comboBox1.SelectedItem.ToString());
string fileName = "ls.txt";
string fileName2 = kdstr + ".txt";
string sourcePath = #"H:\import_directoy\customer\" + kdstr;
string tempPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string dmitempPath = #"program_name\";
string targetPath = Path.Combine(tempPath, dmitempPath);
File.Delete(targetPath + "output");
string sourceFileName = Path.Combine(sourcePath, fileName);
string destFile = Path.Combine(targetPath, fileName2);
Directory.CreateDirectory(targetPath);
File.Copy(sourceFileName, destFile, overwrite: true);
using (var output = File.Create(targetPath + "output"))
{
foreach (var file in new[] { "H:\\directoy1\\directoy2\\index.config", destFile })
{
using (var input = File.OpenRead(file))
{
input.CopyTo(output);
}
}
}
string[] raw_text = File.ReadAllLines(targetPath + "output", Encoding.Default);
string[] data_col = null;
int x = 0;
foreach (string text_line in raw_text)
{
data_col = text_line.Split(';');
if (x == 0)
{
for (int i = 0; i <= data_col.Count() - 1; i++)
{
ls_datenTabelle.Columns.Add(data_col[i]);
}
x++;
}
else
{
ls_datenTabelle.Rows.Add(data_col);
}
}
ls_dataGridView.DataSource = ls_datenTabelle;
this.Controls.Add(ls_dataGridView);
ls_dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
ls_dataGridView.AllowUserToAddRows = false;
}
Now I want to search all over this datatable/datagridview and I will show the rows if the search function found the searchvalue in any column.
But I dodn't know how. The header of the table could change every day. So this code doesn't work for me:
public void findingValue(object sender, EventArgs e)
{
string searchValue = textBox1.Text;
DataView data = ls_datenTabelle.DefaultView;
ls_dataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
data.RowFilter = string.Format("Name like '" + searchValue + "%'");
}
The program will find only rows where the searchValue is in the "Name" column and not one of the other 18 (umknown) columns.
Here is an example which uses Linq to loop over the columns in the DataTable:
if (searchValue == "") data.RowFilter = "";
else
{
var cols = ls_datenTabelle.Columns.Cast<DataColumn>().Select(x => x.ColumnName);
var filter = cols.Select(x => x + " like '" + searchValue + "%'")
.Aggregate((a, b) => a + " OR " + b);
data.RowFilter = filter;
}
First we collect the column names and then we build a filter from them, each part connectd with a OR clause.
You could also use Join or good old loops..
Note that this assumes that all columns allow searching for strings, i.e. no numbers etc..
If this is not true you will want to either pick only the string columns or change the filter for non-string columns, if searching them makes any sense.
To restrict the search to string columns simply insert
.Where(x => x.DataType == typeof(string))
between Cast and Select, so that cols contains only searchable columns..
If instead you want to search numeric columns you could write the filter like this:
var filter = cols.Select(x => "Convert(" + x + ", 'System.String') like '"
+ searchValue + "%'").Aggregate((a, b) => a + " OR " + b);
This uses the Convert function of the expression syntax . But why would one search for numbers starting with some digits..?
Here is a test with 2 string, 1 numeric and one datetime columns. The filter works for all; note that for the test I have added an extra '%' to extend the search to the whole values, not just the start..

Get Set for a phone number

In my database, I store phone numbers like this "7279884545". One solid string with no spaces.
In my class that stores the phone number info, I have a function that will add the correct punctuation.
public static String beautifyPhoneNumber(String number, String extension)
{
String beautifulNumber = "";
if (!String.IsNullOrEmpty(number))
{
beautifulNumber = "(" + number.Substring(0, 3) + ") " +
number.Substring(3, 3) + "-" +
number.Substring(6, 4);
}
if (!String.IsNullOrEmpty(extension))
{
beautifulNumber += " x" + extension;
}
return beautifulNumber;
}
And here is how I have the variable in the class itself.
private string _PhonePrimary;
[DisplayName("Phone Primary")]
public string PhonePrimary
{
get
{
if(this._PhonePrimary != null)
{
this._PhonePrimary = beautifyPhoneNumber(this._PhonePrimary, this.Extension);
}
return this._PhonePrimary;
}
set
{
this._PhonePrimary = value;
}
}
This works fine most of the time. The numbers are outputted to the screen in a "(727) 988-4545" or "(727) 988-4545 x12" if there is an extension for that record in the database.
The problem comes when I do a HttpPost request. The model information that is inside of the post request looks like this.
_PhonePrimary = "(727) 988-4545"
PhonePrimary = "(((7) 2) -7) -"
As noted, it looks like you're calling beautifyPhoneNumber on a number you've already beautified.
Here's an implementation using regular expressions that should get you started:
public static String BeautifyPhoneNumber(string numberToBeautify)
{
//The below gives us capture groups for each
//individual piece of the number.
var regularExpression = new Regex(#"(\d{3})(\d{3})(\d{4})(x\d*)?");
//This matches a number that's already been beautified,
//so we can guard against beautifying twice.
var alreadyBeautifulExpression = new Regex(#"(\(\d{3}\)) (\d{3})-(\d{4}) ?(x\d*)?");
var beautifulNumber = string.Empty;
var separator = "-";
var space = " ";
//This prevents us from accidentally beautifying
//something more than once
//You could also guard against this in your getter using a
//IsBeautified extension, using the alreadyBeautifulExpression above
if (alreadyBeautifulExpression.IsMatch(numberToBeautify))
{
return numberToBeautify;
}
//Trying to protect against invalid input... May be insufficient,
//Or unnecessary
if (string.IsNullOrEmpty(numberToBeautify)
|| regularExpression.Matches(numberToBeautify).Count <= 0)
{
return beautifulNumber;
}
GroupCollection groups = regularExpression.Matches(
numberToBeautify)[0].Groups;
//More protection against invalid input
if (groups.Count < 3)
{
return beautifulNumber;
}
//Given "7689131234",
beautifulNumber += "(" + groups[1] + ")" + space; //gives us "(768) "
beautifulNumber += groups[2] + separator; //gives us "(768) 913-"
beautifulNumber += groups[3]; //gives us "(768) 913-1234"
//If we have an extension, we add it.
if (groups[4] != null)
{
beautifulNumber += space + groups[4];
}
return beautifulNumber;
}
Given inputs of:
7279884545
7279884545x12
(727) 988-4545
This returns:
(727) 988-4545
(727) 988-4545 x12
(727) 988-4545

Make string checker more efficient

I am using the following code to check if a string is contained within another string -
foreach (string testrecord in testlist)
{
foreach (string realrecord in reallist)
{
if ((Regex.Replace(testrecord , "[^0-9a-zA-Z]+", "")
.Contains((
Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "")))
&&
((Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "") != "")
&&
((Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "").Length >= 4)))))
{
matchTextBox.AppendText("Match: " + testrecord + " & " + realrecord + Environment.NewLine);
}
}
}
However the runtime for this to finish is taking quite a while. Since I added the special character regex removal the runtime is taking a lot longer however the regex is definitely required.
Is there a more efficient way of applying this regex? I tried to add it to the foreach string variables however you cannot alter them as they are in a foreach loop.
Optimized version:
// Do not put text into matchTextBox direct:
// it makes the control re-painting each time you change the text
// Instead, collect all the text into StringBuffer
StringBuilder Sb = new StringBuilder();
// Pull out as much as you can from the inner loop,
// that's why I've changed the loops' order:
// first loop on reallist, then on testlist
foreach (string realrecord in reallist) {
// Cache Regex.Replace result
String realCleaned = Regex.Replace(realrecord, "[^0-9a-zA-Z]+", "");
// Test as early as possible
if (realCleaned.Length < 4)
continue;
// You don't need to test realCleaned != "";: realCleaned.Length < 4 is enough
foreach (string testrecord in testlist) {
// Cache Regex.Replace result: it's a little bit overshoot here, but if some
// more tests are added it'll be helpful
String testCleaned = Regex.Replace(testrecord, "[^0-9a-zA-Z]+", "");
if (testCleaned.Contains(realCleaned))
Sb.AppendLine("Match: " + testrecord + " & " + realrecord);
}
}
// At last matchTextBox.Text change
matchTextBox.AppendText(Sb.ToString());
This should be a bit quicker (one regex operation per testrecord):
var strippedRealList = reallist.Select(s => Regex.Replace(s, "[^0-9a-zA-Z]+", ""))
.Where(s => s.Length >= 4)
.ToArray();
foreach (string realrecord in reallist)
{
strippedRealList.Where(s => realrecord.Contains(s))
.ToList()
.ForEach(s =>
matchTextBox.AppendText("Match: "
+ s
+ " & "
+ realrecord
+ Environment.NewLine));
}
I wonder that you are using Regex to achieve your purpose ignoring the fact that you can also achieve this by only using .Contains() method such that your code should be simple and faster then before
foreach (string testrecord in testlist)
{
foreach (string realrecord in reallist)
{
if(testrecord.Contains(realrecord))
{
matchTextBox.AppendText("Match: " + testrecord + " & " + realrecord + Environment.NewLine);
}
}
}

How to present(not open) a very large file into an UI component(TextBox) quickly

Before talking about my question, I'd like to clarify that this is not a question asking about how to OPEN a large text file.
I have done it. It's a 150MB .txt file and I dump it into a dictionary object around 1 second.
After this, I'd like to display it in an UI component.
I have tried to use TextBox, but until now the application windows hasn't shown up (it's been already 5 mins after I clicked the F5).....
So the question is what is the better UI component to display a large number of characters(I have 393300 elements in the dictionary object)
Thanks
Update:
private void LoadTermCodes(TextBox tb)
{
Stopwatch sw = new Stopwatch();
sw.Start();
StreamReader sr = new StreamReader(#"xxx.txt");
string line;
while ((line = sr.ReadLine()) != null)
{
string[] colums = line.Split('\t');
var id = colums[4];
var diagnosisName = colums[7];
if (dic.Keys.Contains(id))
{
var temp = dic[id];
temp += "," + diagnosisName;
dic[id] = temp;
}
else
{
dic.Add(id, diagnosisName);
}
//tb.Text += line + Environment.NewLine;
}
sw.Stop();
long spentTime = sw.ElapsedMilliseconds;
foreach (var element in dic)
{
tb.Text += element.Key + "\t" + element.Value + Environment.NewLine;
}
//tb.Text = "Eplased time (ms) = " + spentTime;
MessageBox.Show("Jie shu le haha~~~ " + spentTime);
}
The long running issue you're seeing is possibly due to how String are handled by the c# runtime. Since Strings are immutable what's happening every time you're calling + on them it's copying the String so far and the next small part into a new memory location and then returning that.
There's a good couple of articles by Eric Lippert here: Part 1 and Part 2 that explain it under the hood.
Instead, to stop all of this copying, you should use a StringBuilder. What this will do to your code is:
private void LoadTermCodes(TextBox tb)
{
Stopwatch sw = new Stopwatch();
sw.Start();
StreamReader sr = new StreamReader(#"xxx.txt");
string line;
// initialise the StringBuilder
System.Text.StringBuilder outputBuilder = new System.Text.StringBuilder(String.Empty);
while ((line = sr.ReadLine()) != null)
{
string[] colums = line.Split('\t');
var id = colums[4];
var diagnosisName = colums[7];
if (dic.Keys.Contains(id))
{
var temp = dic[id];
temp += "," + diagnosisName;
dic[id] = temp;
}
else
{
dic.Add(id, diagnosisName);
}
}
sw.Stop();
long spentTime = sw.ElapsedMilliseconds;
foreach (var element in dic)
{
// append a line to it, this will stop a lot of the copying
outputBuilder.AppendLine(String.Format("{0}\t{1}", element.Key, element.Value));
}
// emit the text
tb.Text += outputBuilder.ToString();
MessageBox.Show("Jie shu le haha~~~ " + spentTime);
}

Changing a Variable A-Z if file exists(C#)

Hey everyone I am currently getting the next variable based on if a file exists, but know there must be an easier way. This thread Iterating through the Alphabet - C# a-caz gave me some good insight, but I am having a bit of a problem Implementing it with what I have. Any suggestions would be really appreciated. Thanks
//Generate Motor Spacer Part Number
textBox3.Text = "MLB028A-MTRSPR-" + "z" + "-" + "y";
if (comboBox3.Text == "28mm (NEMA 11)") textBox3.Text = textBox3.Text.Replace("z", "B");
if (comboBox3.Text == "28mm (NEMA 11)") textBox3.Text = textBox3.Text.Replace("y", "A");
//Generate Motor Spacer Part Descriptions
textBox5.Text = "SPACER, " + comboBox3.Text + ", CFG-" + "y" + " MLB028";
if (comboBox3.Text == "28mm (NEMA 11)") textBox5.Text = textBox5.Text.Replace("y", "A");
string B = #"C:\Engineering\Engineering\SW Automation\Linear Actuator Technology\MLC Series\Models\MLB028Z-MTRSPR-B-A.SLDPRT";
if (File.Exists(B))
{
testBox3.Text = textBox3.Text.Replace("y", "B");
textBox5.Text = textBox5.Text.Replace("y", "B");
}
string C = #"C:\Engineering\Engineering\SW Automation\Linear Actuator Technology\MLC Series\Models\MLB028Z-MTRSPR-B-B.SLDPRT";
if (File.Exists(C))
{
testBox3.Text = textBox3.Text.Replace("y", "C");
textBox5.Text = textBox5.Text.Replace("y", "C");
}
Look at this code:
textBox3.Text.Replace("y", "B");
That doesn't do what you think it does.
string.Replace doesn't change the contents of the existing string (it can't, strings are immutable). It returns a new string with the replacement made. So you may want:
textBox3.Text = textBox3.Text.Replace("y", "B");
It could be there are other problems - it's hard to know as the code is fairly convoluted - but that (and the other similar lines) are certainly problematic.
Okay, I am not clear on what exactly you are trying to accomplish. If you are trying to populate a couple of textboxes with a value based on the first file you find in a directory with a specific file name, then try the following:
void PopulateTextBoxes()
{
string format = "MLB028A-MTRSPR-B-{1}";
string format2 = "SPACER, {0}, CFG-{1} MLB028";
string fileName = #"C:\Engineering\Engineering\SW Automation\Linear Actuator Technology\MLC Series\Models\MLB028Z-MTRSPR-B-{1}.SLDPRT";
for(string start = "A"; start != "Z"; start = GetNextBase26(start))
{
if(File.Exists(String.Format(fileName,start)))
{
textBox3.Text = String.Format(format,start);
textBox5.Text = String.Format(format2,textBox3.Text,start);
break;
}
}
}
// CODE FROM http://stackoverflow.com/questions/1011732/iterating-through-the-alphabet-c-sharp-a-caz
private static string GetNextBase26(string a)
{
return Base26Sequence().SkipWhile(x => x != a).Skip(1).First();
}
private static IEnumerable<string> Base26Sequence()
{
long i = 0L;
while (true)
yield return Base26Encode(i++);
}
private static char[] base26Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
private static string Base26Encode(Int64 value)
{
string returnValue = null;
do
{
returnValue = base26Chars[value % 26] + returnValue;
value /= 26;
} while (value-- != 0);
return returnValue;
}

Categories

Resources