Merge / optimize substring statements - c#

How can I optimize this code snippet?
string page = wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB");
int pos;
while ((pos = page.IndexOf("/watch?v=")) > 0) {
page = page.Substring(pos);
page = page.Substring(page.IndexOf("video-time"));
page = page.Substring(page.IndexOf("aria-label"));
page = page.Substring(page.IndexOf(#"\u003e") + 6);
string vt = page.Substring(0, page.IndexOf(#"\u003c"));
page = page.Substring(page.IndexOf("title=") + 1);
page = page.Substring(page.IndexOf("title=") + 1);
page = page.Substring(page.IndexOf("title=") + 1);
page = page.Substring(page.IndexOf("\\\"") + 2);
string tt = page.Substring(0, page.IndexOf("\\\" aria-describedby="));
}
Sadly, I can't just skip some of the Substring lines as this seems to be the only way to find the right occurance of vt and tt.
Since Substring always returns a new string I tried a solution with StringBuilder:
System.Text.StringBuilder sb=new System.Text.StringBuilder(wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB"));
int pos;
while ((pos = sb.ToString().IndexOf("/watch?v=")) > 0) {
sb.Remove(0,pos);
sb.Remove(0,sb.ToString().IndexOf("video-time"));
sb.Remove(0,sb.ToString().IndexOf("aria-label"));
sb.Remove(0,sb.ToString().IndexOf(#"\u003e") + 6);
string vt =sb.ToString(0,sb.ToString().IndexOf(#"\u003c"));
sb.Remove(0,sb.ToString().IndexOf("title=") + 1);
sb.Remove(0,sb.ToString().IndexOf("title=") + 1);
sb.Remove(0,sb.ToString().IndexOf("title=") + 1);
sb.Remove(0,sb.ToString().IndexOf("\\\"") + 2);
string tt =sb.ToString(0,sb.ToString().IndexOf("\\\" aria-describedby="));
}
I was suprised to find out that this solution, although it doesn't look like due to all the ToString(), is indeed slightly faster.
Now, is there a way to optimize this even further? Maybe even make it look better?

As suggested by #harold
string page = wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB");
int pos;
while ((pos = page.IndexOf("/watch?v=")) > 0) {
int subPos=pos;
subPos=page.IndexOf("video-time",subPos);
subPos=page.IndexOf("aria-label",subPos);
subPos=page.IndexOf(#"\u003e",subPos);
subPos+=6;
string vt=page.Substring(subPos,(subPos=page.IndexOf(#"\u003c",subPos)));
subPos=page.IndexOf("title=",subPos);
subPos++;
subPos=page.IndexOf("title=",subPos);
subPos++;
subPos=page.IndexOf("title=",subPos);
subPos=page.IndexOf("\\\"",subPos);
subPos+=2;
string tt=page.Substring(subPos,(subPos=page.IndexOf("\\\" aria-describedby=", subPos)));
page=page.Substring(subPos);
}
Seems to be quite a lot faster than using StringBuilder with ToString()

As suggested by #dbc, using these extension methods
System.Text.StringBuilder sb=new System.Text.StringBuilder(wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB"));
int pos;
while ((pos = sb.ToString().IndexOf("/watch?v=")) > 0) {
sb.Remove(0,pos);
sb.Remove(0,sb.IndexOf("video-time"));
sb.Remove(0,sb.IndexOf("aria-label"));
sb.Remove(0,sb.IndexOf(#"\u003e") + 6);
string vt =sb.ToString(0,sb.IndexOf(#"\u003c"));
sb.Remove(0,sb.IndexOf("title=") + 1);
sb.Remove(0,sb.IndexOf("title=") + 1);
sb.Remove(0,sb.IndexOf("title=") + 1);
sb.Remove(0,sb.IndexOf("\\\"") + 2);
string tt =sb.ToString(0,sb.IndexOf("\\\" aria-describedby="));
}
Should be faster than using StringBuilder with ToString(), my test results are a bit jerky here.

Related

How get odd chars from String in C#

I am new to C#. My problem is to take odd chars from a string and get a new string from those odds.
string name = "Filip"; // expected output ="Flp"
I don't want to take, for example,
string result = name.Substring(0, 1) + name.Substring(2, 1) + ... etc.
I need a function for this operation.
Try Linq (you actually want even characters since string is zero-based):
string name = "Filip";
string result = string.Concat(name.Where((c, i) => i % 2 == 0));
In case of good old loop implementation, I suggest building the string with a help of StringBuilder:
StringBuilder sb = new StringBuilder(name.Length / 2 + 1);
for (int i = 0; i < name.Length; i += 2)
sb.Append(name[i]);
string result = sb.ToString();

split text in a text file

How can I split a text file where I have various length of sentences inside and I want to read the text file when I click to button1 on my form and take, extract words from that text file that are between start and the end of ' character and which contains # symbol or # symbol inside the start and end of ' character and I want to know which line is it in and output the words into the text file.
Example, lets say I have a text like
abc'123'#def'456''#ghi'
abc'123'#def'#456''#ghi'123456'
output:
1st sentence #ghi
2nd sentence #456 #ghi
PS: #def is not in start and end of ' character so not in the output
I tied with split function but couldn't make it and turned into mass: ( How can I make this. I will be pleased if someone who knows helps.
Thanks.
here ur input string is s & the string contains # or # at first index is str
int start = s.indexOf("'");
int end = s.indexOf("'", start + 1);
string str = s.SubString(start, end);
if(str.ToCharArray()[0] == "#" || str.ToCharArray()[0] == "#")
// proceed
As far as this example is concerned here is a sample code that works
string sen1="abc'123'#def'456''#ghi'";
string sen2 = "abc'123'#def'#456''#ghi'123456'";
string[] NewSen = Regex.Split(sen1, "''");
string YourFirstOP=NewSen[1].ToString(); //gets #ghi
NewSen = Regex.Split(sen2, "''");
string[] A1 = Regex.Split(NewSen[0], "'");
string[] A2 = Regex.Split(NewSen[1], "'");
string YourSecondOP= A1[A1.Length - 1] + "" + A2[A2.Length - 3].ToString();// gets #456 #ghi
But thats just this example
Hope this helps
Try this,
string testString = #"abc'123'#def'456''#ghi'abc'123'#def'#456''#ghi'123456'";
List<string> output = new List<string>();
int startIndex = 0;
int endIndex = 0;
while (startIndex >= 0 && endIndex >= 0)
{
startIndex = testString.IndexOf("'", endIndex + 1);
endIndex = testString.IndexOf("'", startIndex + 1);
if (startIndex >= 0 && endIndex >= 0)
{
string str = testString.Substring(startIndex + 1, (endIndex - startIndex) - 1);
int indexOfSpecialChar = str.IndexOf("#");
if (indexOfSpecialChar < 0)
{
indexOfSpecialChar = str.IndexOf("#");
}
if (indexOfSpecialChar >= 0)
{
output.Add(str.Substring(indexOfSpecialChar));
}
}
}
string [] Mass = s.Split('\'');
if (Mass.Length > 1)
for (int i = 1; i < (Mass.Length - 1); i += 2)
{
if (Mass[i].Contains("#") || Mass[i].Contains("#"))
// proceed
}

Replacing character after single quote

I am using the following C# code to modify a lowercase letter to uppercase after a single quote:
public virtual string FirstName
{
get { return _firstName; }
set
{
if (value != null)
{
int pos = value.IndexOf("'", 0);
int strlength = value.Length - 1;
if (pos >= 0 && pos != strlength)
{
string temp = value[pos + 1].ToString();
temp = temp.ToUpper();
value = value.Remove(pos + 1, 1);
value = value.Insert(pos + 1, temp);
}
}
}
}
To me this looks like overkill. Is there an easier way to achieve the desired result:
Value: Mc'donald
Expected: Mc'Donald
here is without regex
int pos = data.IndexOf("'");
if (pos >= 0 && pos < data.Length - 1)
{
StringBuilder sbl = new StringBuilder(data);
sbl[pos + 1] = char.ToUpper(sbl[pos + 1]);
data = sbl.ToString();
}
Since you're open to Regex, would this overload of the Regex.Replace do what you need?
Regex.Replace Method (String, MatchEvaluator)
Here's a modified version of the example given at the link above. I've changed it to use the '\w pattern and to return the match in upper case.
using System;
using System.Text.RegularExpressions;
class RegExSample
{
static string CapText(Match m)
{
// Return the match in upper case
return m.ToString().ToUpperInvariant();
}
static void Main()
{
string text = "Mc'donald";
System.Console.WriteLine("text=[" + text + "]");
Regex rx = new Regex(#"'\w");
string result = rx.Replace(text, new MatchEvaluator(RegExSample.CapText));
System.Console.WriteLine("result=[" + result + "]");
}
}
Perhaps regular expressions?
string value = "Mc'donald";
string found = Regex.Match(value, "'[\\w]").Value;
string result = value.Replace(found, found.ToUpper());
Console.WriteLine(result); // Mc'Donald

Automatically increment filename

Right now I have this code:
int number = 0;
DirectoryInfo di = new DirectoryInfo(scpath + #"Screenshots\");
if (di.Exists) {
} else {
di.Create();
}
int screenWidth = Screen.GetBounds(new Point(0, 0)).Width;
int screenHeight = Screen.GetBounds(new Point(0, 0)).Height;
Bitmap bmpScreenShot = new Bitmap(screenWidth, screenHeight);
Graphics gfx = Graphics.FromImage((Image)bmpScreenShot);
gfx.CopyFromScreen(0, 0, 0, 0, new Size(screenWidth, screenHeight));
bmpScreenShot.Save(di + "Screenshot_" + number, ImageFormat.Jpeg);
Program takes a screenshot (which works) and saves it. What I want to do is to have the program check and see if a screenshot exists ("Screenshot_*") and to create it if it doesn't. If it does, increment file name till it hits a number that hasn't been used at the end of "Screenshot_"
Not sure how to go about this given that it's more with files and incrementing. I'm thinking about a for loop but I'm playing with it now.
Getting the name of a file that does not exist sounds like a job for a method.
string IndexedFilename(string stub, string extension)
{
int ix = 0;
string filename = null;
do {
ix++;
filename = String.Format("{0}{1}.{2}", stub, ix, extension);
} while (File.Exists(filename));
return filename;
}
There is a race condition if you call this from multiple threads.
Assuming you have just one app and one thread in the app asking for filenames, then this ought to work.
The code to use the method looks like this:
string di = Path.Combine(scpath, "Screenshots");
if (!Directory.Exists(di) {
Directory.Create(di);
}
int screenWidth = Screen.GetBounds(new Point(0, 0)).Width;
int screenHeight = Screen.GetBounds(new Point(0, 0)).Height;
Bitmap bmpScreenShot = new Bitmap(screenWidth, screenHeight);
Graphics gfx = Graphics.FromImage((Image)bmpScreenShot);
gfx.CopyFromScreen(0, 0, 0, 0, new Size(screenWidth, screenHeight));
string filename = IndexedFilename(Path.Combine(di,"Shot_"),"jpg");
bmpScreenShot.Save(filename, ImageFormat.Jpeg);
Like #Quintin said, use datetime for filename:
string filename = Path.Combine(
di.FullName,
String.Format("{0}.jpg", DateTime.Now.ToString("yyyy-MM-dd HH.mm.ss")));
bmpScreenShot.Save(filename, ImageFormat.Jpeg);
This is a possibility
string[] files = System.IO.Directory.GetFiles(scpath, "Screenshot_*.jpg");
string baseName = Path.Combine(scpath, "Screenshot_");
string filename;
int i = 0;
do {
filename = baseName + ++i + ".jpg";
} while (files.Contains(filename));
The advantage of this approach is that the file system is queried only once. If the file number gets large, consider adding the file names to a hash set to speed up the checks even more:
var files = new HashSet<string>(Directory.GetFiles(scpath, "Screenshot_*.jpg"));
Instead of using a number as a way to differentiate between screenshots use a timestamp:
string currentDT = string.Format("{0:D4}.{1:D2}.{2:D2}-{3:D2}.{4:D2}.{5:D2}",
DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day,
DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second)
bmpScreenShot.Save(di + "Screenshot_" + currentDT, ImageFormat.Jpeg);
I'd use GUID...
try{
bmpScreenShot.Save(di + "Screenshot_" + Guid.NewGuid().ToString(), ImageFormat.Jpeg);
}catch(Exception e)
{
//handle the problems here, for example if file already exists, try again
}
This should work well until you run out of unique GUIDs...
public static string MakeUniqueFileName(string file)
{
string dir = Path.GetDirectoryName(file);
string fn;
for (int i = 0; ; ++i)
{
fn = Path.Combine(dir, string.Format(file, i));
if (!File.Exists(fn))
return fn;
}
}
Use it like this:
string file = scpath + #"Screenshots\" + "Screenshot_{0}.png";
bmpScreenShot.Save(MakeUniqueFileName(file), ImageFormat.Jpeg);
This will create output_0.jpg output_1.jpg ... output_n.jpg:
int filecount = 0;
string path = Environment.CurrentDirectory;
for (int i = 0; File.Exists(path + #"\output_" + i + ".jpg"); i++)
{
filecount = i + 1;
}
File.Create(path + #"\output_" + filecount + ".jpg");
private static string GetUniqueFile(string path, string file, string ext)
{
int filecount = 0;
int i = 0;
for (i = 0; File.Exists(path + "\\" + file + "_" + i + "." + ext); i++)
{
filecount = i + 1;
}
return path + "\\" + file + "_" + i.ToString() + "." + ext;
}
In case the directory with the screenshots contains many images, it might be beneficial to find the next available filename using binary search. This way the File.Exists method will be called far fewer times than doing an incremental search.
/// <summary>
/// Performs a binary search in the Int32 range, and returns the first element
/// that satisfies a condition.
/// </summary>
public static TElement BinarySearchFirst<TElement>(
Func<int, TElement> selector,
Predicate<TElement> predicate,
int start = 1)
{
ArgumentNullException.ThrowIfNull(selector);
ArgumentNullException.ThrowIfNull(predicate);
if (start < 0) throw new ArgumentOutOfRangeException(nameof(start));
long lo = start;
long hi = 1;
(TElement Value, bool HasValue) maxFound = default;
// First stage, find an approximate upper limit of the search space.
while (hi < Int32.MaxValue)
{
hi = Math.Min(hi * 10, Int32.MaxValue);
if (hi < start) continue;
TElement item = selector((int)hi);
bool accepted = predicate(item);
if (accepted)
{
maxFound = (item, true);
hi--;
break;
}
lo = hi + 1;
}
// Second stage, perform binary search between lo and hi.
while (lo <= hi)
{
long mid = lo + ((hi - lo) >> 1);
TElement item = selector((int)mid);
bool accepted = predicate(item);
if (accepted)
{
maxFound = (item, true);
hi = mid - 1;
}
else
lo = mid + 1;
}
if (maxFound.HasValue) return maxFound.Value;
throw new InvalidOperationException("Element not found in the Int32 range.");
}
Usage example:
string pathFound = BinarySearchFirst(
i => Path.Combine(#"C:\Screenshots", $"Screenshot-{i}.png"),
path => !File.Exists(path));
In a folder with 200 screenshots, the above code will check for the existence of the files below:
Screenshot-10.png
Screenshot-100.png
Screenshot-1000.png
Screenshot-550.png
Screenshot-325.png
Screenshot-212.png
Screenshot-156.png
Screenshot-184.png
Screenshot-198.png
Screenshot-205.png
Screenshot-201.png
Screenshot-199.png
Screenshot-200.png
...before returning the value "C:\Screenshots\Screenshot-201.png" as the result.
Online demo.

String does not contain a definition for add method

I am doing a program in .NET. I am doing some changes in the program. I am getting a error
String does not contain a definition for add method.
I don know how to rectify this error.
private string process(string fname)
{
//string errs = "";
string Strings = "";
string[] lines = File.ReadAllLines(fname);
StringBuilder b = new StringBuilder();
for (int i = 1; i < lines.Length; i++)
{
string[] sa = lines[i].Split(new string[] { "," }, StringSplitOptions.None);
bool ok = false;
if (sa[1].CompareTo("EQ") == 0)
ok = true;
if (!ok && sa[1].CompareTo("BE") != 0)
continue;
string name = sa[0];
int token = NSECM.Lookup(name);
if (token == 0)
{
//errs += "Symbol " + name + " not found\r\n";
continue;
}
//int open = (int)(double.Parse(sa[2]) * 100 + 0.5);
//int high = (int)(double.Parse(sa[3]) * 100 + 0.5);
//int low = (int)(double.Parse(sa[4]) * 100 + 0.5);
//int close = (int)(double.Parse(sa[5]) * 100 + 0.5);
//uint vol = uint.Parse(sa[8]);
//int date = cdate(sa[10]);
//uint time = cvt(date);
uint open = (uint)(double.Parse(sa[2]) * 100 + 0.5);
uint high = (uint)(double.Parse(sa[3]) * 100 + 0.5);
uint low = (uint)(double.Parse(sa[4]) * 100 + 0.5);
uint close = (uint)(double.Parse(sa[5]) * 100 + 0.5);
uint vol = uint.Parse(sa[8]);
int date = cdate(sa[10]);
//b.Append("D");
b.Append("S" + (1000000 + token).ToString().Substring(1));
b.Append("-" + date);
b.Append("|D");
b.Append(Encode.encode6(cvt(date)));
//b.Append(Encode.encode6(time));
b.Append(Encode.encode4(open));
b.Append(Encode.encode4(high));
b.Append(Encode.encode4(low));
b.Append(Encode.encode4(close));
b.Append(Encode.encode6(vol));
//b.Append("\n");
Strings.Add(b.ToString());
}
}
The string class does not define a method called Add therefore the line Strings.Add(b.ToString()) does not compile. Depending on what you like to do there are 2 possible solutions i can imagine of
You want to combine the current value of Strings with the value of b: Strings += b.ToString(). But keep in mind that you are always appending stuff to the same StringBuilder so in the end you add to much. But on the other hand you can just write Strings = b.ToString() after the for-loop because then you have added all your text to the StringBuilder.
You want to add the current value of b as a new string to a collection of strings. In this case Strings should be a collection. The Add method suggests that you should have a look in the List class. List<string> Strings = new List<string>();. Now you can use Strings.Add(b.ToString()). But also here keep in mind that you are always appending to the same StringBuilder without flushing it!
There is no Add() method on the string class:
Strings.Add(b.ToString());
You can concat the strings with the += operator instead:
Strings += b.ToString();
Use StringBuilder instead of string. It supports append method to concatenate strings. Moreover StringBuilder is mutable. When we make use of the "StringBuilder" object, the Append method is used. This means, an insertion is done on the existing string. Operation on StringBuilder object is faster than String operations, as the copy is done to the same location. Usage of StringBuilder is more efficient in case large amounts of string manipulations have to be performed

Categories

Resources