Scripting in Sound forge - c#

I am completely new to scripting in sound forge. I have a requirement of mixing 2 .wav files.
For example, 1.wav file mix with INVERTED 2.wav file.
I need to get the statistics of the resultant mixed output file.
Statistics like minimum sample value, maximum sample value for all the channels.
Below i have the code to mix 2 .wav files. But the 2nd is not inverted.
Can anyone please help me on the scripting for the above in C#?
Or if anyone can share a document with the standard API's available for sound forge scripting also will be helpful.
using System;
using System.IO;
using System.Windows.Forms;
using System.Collections.Generic;
using SoundForge;
public class EntryPoint
{
public void Begin(IScriptableApp app)
{
//choose the first file.
OpenFileDialog openFile = new OpenFileDialog();
openFile.Title = "Open the input file.";
String inputFilePath = String.Empty;
if (openFile.ShowDialog() == DialogResult.OK)
{
inputFilePath = openFile.FileName.ToString();
}
OpenFileDialog openOutputFile = new OpenFileDialog();
openOutputFile.Title = "Open the output file.";
String outputFilePath = String.Empty;
if (openOutputFile.ShowDialog() == DialogResult.OK)
{
outputFilePath = openOutputFile.FileName.ToString();
}
ISfFileHost backFile = app.OpenFile(outputFilePath, true, false);
ISfFileHost file = app.OpenFile(inputFilePath, false, false);
long fileLen = file.Length;
SfAudioSelection asel = new SfAudioSelection(file);
file.DoMixReplace(SfAudioSelection.All, 1, 1, backFile, new SfAudioSelection(0, file.Length), null, null, EffectOptions.EffectOnly | EffectOptions.WaitForDoneOrCancel);
backFile.Close(CloseOptions.DiscardChanges);
}
public void FromSoundForge(IScriptableApp app)
{
ForgeApp = app; //execution begins here
app.SetStatusText(String.Format("Script '{0}' is running.", Script.Name));
Begin(app);
app.SetStatusText(String.Format("Script '{0}' is done.", Script.Name));
}
public static IScriptableApp ForgeApp = null;
public static void DPF(string sz) { ForgeApp.OutputText(sz); }
public static void DPF(string fmt, params object[] args) {
ForgeApp.OutputText(String.Format(fmt, args)); }
} //EntryPoint

Using the below code the requirement can be achieved.
Requirement was:
1.wav file mix with INVERTED 2.wav file. Get the statistics of the resultant mixed output file.
Statistics like minimum sample value, maximum sample value for all the channels.
using System;
using System.IO;
using System.Windows.Forms;
using System.Collections.Generic;
using SoundForge;
public class EntryPoint
{
public void Begin(IScriptableApp app)
{
string strOutFile = GETARG("outfile", "path to save file at");
if ("" == strOutFile)
{
MessageBox.Show("invald output path");
return;
}
OpenFileDialog openFile = new OpenFileDialog();
openFile.Title = "Open the input file.";
////string to hold the path of input file.
String strFile1 = String.Empty;
if (openFile.ShowDialog() == DialogResult.OK)
{
strFile1 = openFile.FileName.ToString();
}
OpenFileDialog openOutputFile = new OpenFileDialog();
openOutputFile.Title = "Open the output file.";
////string to hold the path of output file.
String strFile2 = String.Empty;
if (openOutputFile.ShowDialog() == DialogResult.OK)
{
strFile2 = openOutputFile.FileName.ToString();
}
ISfFileHost fhOne = app.OpenFile(strFile1, true, true);
if (null == fhOne)
return;
ISfFileHost fhTwo = app.OpenFile(strFile2, true, true);
if (null == fhTwo)
{
fhOne.Close(CloseOptions.DiscardChanges);
return;
}
ISfFileHost fhOut = app.NewFile(fhOne.DataFormat, false);
fhOut.ReplaceAudio(new SfAudioSelection(0, 0), fhOne, new SfAudioSelection(fhOne));
fhOut.DoEffect("Invert/Flip", 0, new SfAudioSelection(fhOut), EffectOptions.EffectOnly);
fhOut.WaitForDoneOrCancel();
fhOut.MixAudio(new SfAudioSelection(fhOut), 1.0, 1.0, fhTwo, new SfAudioSelection(fhTwo));
fhOut.WaitForDoneOrCancel();
fhOut.SaveAs(strOutFile, fhOne.SaveFormat.Guid, "Default Template", RenderOptions.RenderOnly);
fhOut.WaitForDoneOrCancel();
fhOne.Close(CloseOptions.DiscardChanges);
fhTwo.Close(CloseOptions.DiscardChanges);
SfAudioStatistics[] stat = new SfAudioStatistics[fhOut.Channels];
fhOut.UpdateStatistics(SfAudioSelection.All);
stat[0] = fhOut.GetStatistics(0);
stat[1] = fhOut.GetStatistics(1);
stat[2] = fhOut.GetStatistics(2);
stat[3] = fhOut.GetStatistics(3);
stat[4] = fhOut.GetStatistics(4);
stat[5] = fhOut.GetStatistics(5);
stat[6] = fhOut.GetStatistics(6);
stat[7] = fhOut.GetStatistics(7);
MessageBox.Show(String.Format("Max Sample Value Channel 1 - {0},Channel 2 - {1},Channel 3 - {2},Channel 4 - {3},Channel 5 - {4},Channel 6 - {5},Channel 7 - {6},Channel 8 - {7}", stat[0].MaxValue, stat[1].MaxValue, stat[2].MaxValue, stat[3].MaxValue, stat[4].MaxValue, stat[5].MaxValue, stat[6].MaxValue, stat[7].MaxValue));
MessageBox.Show(String.Format("Min Sample Value Channel 1 - {0},Channel 2 - {1},Channel 3 - {2},Channel 4 - {3},Channel 5 - {4},Channel 6 - {5}, Channel 7 - {6}, Channel 8 - {7}", stat[0].MinValue, stat[1].MinValue, stat[2].MinValue, stat[3].MinValue, stat[4].MaxValue, stat[5].MinValue, stat[6].MinValue, stat[7].MinValue));
System.Diagnostics.Process curr = System.Diagnostics.Process.GetCurrentProcess();
}
public void FromSoundForge(IScriptableApp app)
{
ForgeApp = app; //execution begins here
app.SetStatusText(String.Format("Script '{0}' is running.", Script.Name));
Begin(app);
app.SetStatusText(String.Format("Script '{0}' is done.", Script.Name));
}
public static IScriptableApp ForgeApp = null;
public static void DPF(string sz) { ForgeApp.OutputText(sz); }
public static void DPF(string fmt, object o) { ForgeApp.OutputText(String.Format(fmt, o)); }
public static void DPF(string fmt, object o, object o2) { ForgeApp.OutputText(String.Format(fmt, o, o2)); }
public static void DPF(string fmt, object o, object o2, object o3) { ForgeApp.OutputText(String.Format(fmt, o, o2, o3)); }
public static string GETARG(string k, string d) { string val = Script.Args.ValueOf(k); if (val == null || val.Length == 0) val = d; return val; }
public static int GETARG(string k, int d) { string s = Script.Args.ValueOf(k); if (s == null || s.Length == 0) return d; else return Script.Args.AsInt(k); }
public static bool GETARG(string k, bool d) { string s = Script.Args.ValueOf(k); if (s == null || s.Length == 0) return d; else return Script.Args.AsBool(k); }
} //EntryPoint

Related

Compare text files in C# and remove duplicate lines

1.txt:
Origination,destination,datetime,price
YYZ,YTC,2016-04-01 12:30,$550
YYZ,YTC,2016-04-01 12:30,$550
LKC,LKP,2016-04-01 12:30,$550
2.txt:
Origination|destination|datetime|price
YYZ|YTC|2016-04-01 12:30|$550
AMV|YRk|2016-06-01 12:30|$630
LKC|LKP|2016-12-01 12:30|$990
I have two text files with ',' and '|' as separators, and I want to create a console app in C# which reads these two files when I pass an origination and destination location from command prompt.
While searching, I want to ignore duplicate lines, and I want to display the results in order by price.
The output should be { origination } -> { destination } -> datetime -> price
Need help how to perform.
Here's a simple solution that works for your example files. It doesn't have any error checking for if the file is in a bad format.
using System;
using System.Collections.Generic;
class Program
{
class entry
{
public string origin;
public string destination;
public DateTime time;
public double price;
}
static void Main(string[] args)
{
List<entry> data = new List<entry>();
//parse the input files and add the data to a list
ParseFile(data, args[0], ',');
ParseFile(data, args[1], '|');
//sort the list (by price first)
data.Sort((a, b) =>
{
if (a.price != b.price)
return a.price > b.price ? 1 : -1;
else if (a.origin != b.origin)
return string.Compare(a.origin, b.origin);
else if (a.destination != b.destination)
return string.Compare(a.destination, b.destination);
else
return DateTime.Compare(a.time, b.time);
});
//remove duplicates (list must be sorted for this to work)
int i = 1;
while (i < data.Count)
{
if (data[i].origin == data[i - 1].origin
&& data[i].destination == data[i - 1].destination
&& data[i].time == data[i - 1].time
&& data[i].price == data[i - 1].price)
data.RemoveAt(i);
else
i++;
}
//print the results
for (i = 0; i < data.Count; i++)
Console.WriteLine("{0}->{1}->{2:yyyy-MM-dd HH:mm}->${3}",
data[i].origin, data[i].destination, data[i].time, data[i].price);
Console.ReadLine();
}
private static void ParseFile(List<entry> data, string filename, char separator)
{
using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open))
using (System.IO.StreamReader reader = new System.IO.StreamReader(fs))
while (!reader.EndOfStream)
{
string[] line = reader.ReadLine().Split(separator);
if (line.Length == 4)
{
entry newitem = new entry();
newitem.origin = line[0];
newitem.destination = line[1];
newitem.time = DateTime.Parse(line[2]);
newitem.price = double.Parse(line[3].Substring(line[3].IndexOf('$') + 1));
data.Add(newitem);
}
}
}
}
I'm not 100% clear on what the output of your program is supposed to be, so I'll leave that part of the implementation up to you. My strategy was to use a constructor method that takes a string (that you will read from a file) and a delimiter (since it varies) and use that to create objects which you can manipulate (e.g. add to hash sets, etc).
PriceObject.cs
using System;
using System.Globalization;
namespace ConsoleApplication1
{
class PriceObject
{
public string origination { get; set; }
public string destination { get; set; }
public DateTime time { get; set; }
public decimal price { get; set; }
public PriceObject(string inputLine, char delimiter)
{
string[] parsed = inputLine.Split(new char[] { delimiter }, 4);
origination = parsed[0];
destination = parsed[1];
time = DateTime.ParseExact(parsed[2], "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
price = Decimal.Parse(parsed[3], NumberStyles.Currency, new CultureInfo("en-US"));
}
public override bool Equals(object obj)
{
var item = obj as PriceObject;
return origination.Equals(item.origination) &&
destination.Equals(item.destination) &&
time.Equals(item.time) &&
price.Equals(item.price);
}
public override int GetHashCode()
{
unchecked
{
var result = 17;
result = (result * 23) + origination.GetHashCode();
result = (result * 23) + destination.GetHashCode();
result = (result * 23) + time.GetHashCode();
result = (result * 23) + price.GetHashCode();
return result;
}
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
HashSet<PriceObject> list1 = new HashSet<PriceObject>();
HashSet<PriceObject> list2 = new HashSet<PriceObject>();
using (StreamReader reader = File.OpenText(args[0]))
{
string line = reader.ReadLine(); // this will remove the header row
while (!reader.EndOfStream)
{
line = reader.ReadLine();
if (String.IsNullOrEmpty(line))
continue;
// add each line to our list
list1.Add(new PriceObject(line, ','));
}
}
using (StreamReader reader = File.OpenText(args[1]))
{
string line = reader.ReadLine(); // this will remove the header row
while (!reader.EndOfStream)
{
line = reader.ReadLine();
if (String.IsNullOrEmpty(line))
continue;
// add each line to our list
list2.Add(new PriceObject(line, '|'));
}
}
// merge the two hash sets, order by price
list1.UnionWith(list2);
List<PriceObject> output = list1.ToList();
output.OrderByDescending(x => x.price).ToList();
// display output here, e.g. define your own ToString method, etc
foreach (var item in output)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
}
}

How to update a TextView text (decode text effect)?

I need help as mentioned in the title.
I have a timer routine that will update the text of a label. It's a decode text effect, but it doesn't seem to update when the timer routine is executed. I am using C#.
MainActivity.cs
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Text;
using System.Timers;
using System.Linq;
namespace ValidateCreditCardNumber_Android
{
[Activity (Label = "ValidateCreditCardNumber_Android", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
EditText editText;
DecodeTextView resultLabel;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
editText = FindViewById<EditText> (Resource.Id.editText);
Button validateButton = FindViewById<Button> (Resource.Id.validateButton);
resultLabel = FindViewById<DecodeTextView> (Resource.Id.resultLabel);
editText.KeyListener = Android.Text.Method.DigitsKeyListener.GetInstance("0123456789" + System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalDigits);
validateButton.Click += OnNumberEntryCompleted;
}
void OnNumberEntryCompleted(object sender, EventArgs args)
{
var entry = editText;
var resultText = "";
if (Mod10Check (entry.Text)) {
// resultLabel.SetTextColor(Android.Graphics.Color.White);
resultText = "__VALID NUMBER";
} else {
resultText = "INVALID NUMBER";
}
// entry.Enabled = false;
// resultLabel.AnimateText (true, resultText, 10);
RunOnUiThread(() => resultLabel.AnimateText (true, resultText, 10));
}
public static bool Mod10Check(string creditCardNumber)
{
// Check whether input string is null or empty.
if (string.IsNullOrEmpty(creditCardNumber)) {
return false;
}
char[] charArray = creditCardNumber.ToCharArray();
// 1. Starting with the check digit double the value of every other digit
// 2. If doubling of a number results in a two digits number, add up.
// the digits to get a single digit number. This will results in eight single digit numbers.
// 3. Get the sum of the digits.
int sumOfDigits = charArray.Where((e) => e >= '0' && e <= '9')
.Reverse()
.Select((e, i) => ((int)e - 48) * (i % 2 == 0 ? 1 : 2))
.Sum((e) => e / 10 + e % 10);
// If the final sum is divisible by 10, then the credit card number.
// is valid. If it is not divisible by 10, the number is invalid.
return sumOfDigits % 10 == 0;
}
}
}
DecodeTextView.cs
using System;
using System.Text;
using System.Timers;
//using Android.Runtime;
using Android.Content;
using Android.Util;
namespace ValidateCreditCardNumber_Android
{
public class DecodeTextView : Android.Widget.TextView
{
private readonly Timer _timerAnimate = new Timer();
private TextDecodeEffect _decodeEffect;
private bool _showing;
private int _initGenCount;
public int Interval
{
get { return (int)_timerAnimate.Interval; }
set { _timerAnimate.Interval = value; }
}
// protected DecodeTextView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
public DecodeTextView(Context c, IAttributeSet args) : base(c, args)
{
_timerAnimate.Interval = 100;
_timerAnimate.Elapsed += _timerAnimate_Tick;
}
public void AnimateText(bool show, string text, int initGenCount)
{
_initGenCount = initGenCount;
_decodeEffect = new TextDecodeEffect(text) { TextVisible = !show };
Text = _decodeEffect.Peek (DecodeMode.None);
_showing = show;
_timerAnimate.Start ();
}
private void _timerAnimate_Tick(object sender, EventArgs e)
{
if (_initGenCount != 0) {
Text = _decodeEffect.GenerateNumberRange (Text.Length);
_initGenCount--;
return;
}
var decodeMode = _showing ? DecodeMode.Show : DecodeMode.Hide;
var text = _decodeEffect.Peek (decodeMode);
if (text == null) {
_timerAnimate.Stop ();
} else {
Text = text;
}
}
}
public enum DecodeMode
{
None,
Show,
Numbers,
Hide
}
class TextDecodeEffect
{
private int _visibleCount;
private readonly Random _random = new Random ();
public bool TextVisible
{
get { return _visibleCount == OriginalText.Length; }
set { _visibleCount = value ? OriginalText.Length : 0; }
}
public string OriginalText { get; private set; }
public TextDecodeEffect(string text)
{
OriginalText = text;
}
public string Peek(DecodeMode mode)
{
switch (mode) {
case DecodeMode.Numbers:
return GenerateNumberRange (OriginalText.Length);
case DecodeMode.Hide:
if (_visibleCount == 0)
return null;
_visibleCount--;
break;
case DecodeMode.Show:
if (_visibleCount == OriginalText.Length)
return null;
_visibleCount++;
break;
}
var text = GenerateNumberRange (OriginalText.Length - _visibleCount);
text += OriginalText.Substring (OriginalText.Length - _visibleCount, _visibleCount);
return text;
}
public string GenerateNumberRange(int count)
{
var SB = new StringBuilder ();
for (int i = 0; i < count; i++)
SB.Append(_random.Next(0, 10));
return SB.ToString();
}
}
}
Here you can found the project
Please help me to fix this problem :( thank you.
This is a threading issue, you can only change UI elements on the UI thread and the timer callback _timerAnimate_Tick will execute on a background thread.
You can see this by logging the thread ID for DecodeTextViews constructor and its _timerAnimate_Tick method:
public DecodeTextView(Context c, IAttributeSet args) : base(c, args)
{
// ...
Console.WriteLine ("DecodeTextView executing on thread: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
}
private void _timerAnimate_Tick(object sender, EventArgs e)
{
Console.WriteLine ("_timerAnimate_Tick executing on thread: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
// ...
}
Which will render the following in the log output:
DecodeTextView executing on thread: 1
_timerAnimate_Tick executing on thread: 6
This is simply fixed by changing the Text property of DecodeTextView on the UI thread using the Post method:
private void _timerAnimate_Tick(object sender, EventArgs e)
{
if (_initGenCount != 0) {
Post (() => {
Text = _decodeEffect.GenerateNumberRange (Text.Length);
});
_initGenCount--;
return;
}
var decodeMode = _showing ? DecodeMode.Show : DecodeMode.Hide;
var text = _decodeEffect.Peek (decodeMode);
if (text == null) {
_timerAnimate.Stop ();
} else {
Post (() => {
Text = text;
});
}
}

How to choose an XML Node? (Using LINQ, XPath, anything is fine)

I have an XML like below :
<Decide Name="MemoryCheck" CommonUnit="MB">
<Decision CellColor="Red" Status="Critical" Exp="<=100" />
<Decision CellColor="Yellow" Status="Warning" Exp="<=200 & >100"/>
<Decision CellColor="Green" Status="OK" Exp=">200" />
</Decide>
For Input 50 MB, Output returned should be "Critical-Red"
For Input 142 MB, Output returned should be "Warning-Yellow"
For Input 212 MB, Output returned should be"OK-Green"
How to go about this using C# ??
Xml Name is "Decide.xml" and Code I have now :
XmlDocument xmldecide = new XmlDocument();
xmldecide.Load("C:\\Decide.xml");
XmlNodeList decidelist = xmldecide.GetElementsbyTagName("Decide");
XmlNode xdecide = decidelist[0];
string input = "50"; // Unit in MB
// Now I have to display the desired O/P "Critical-Red"
string input = "142"; // Unit in MB
// Now I have to display the desired O/P "Warning-Yellow"
string input = "212"; // Unit in MB
// Now I have to display the desired O/P "OK-Green"
Just a suggestion - If you have control of that xml you should consider creating a min and max attribute. Having to parse out conditional and integer information from a single attribute is ugly. That said, assuming you can't change the xml, here's a solution. It assumes the conditionals in the attribute are always in a similar format.
public static string AlertLevel(this XDocument decisionDocument, int size)
{
var queryResult = decisionDocument.Descendants("Decision");
foreach (var item in queryResult)
{
var expAttribute = item.Attribute("Exp");
if (expAttribute == null) continue;
var returnString = CreateResultString(item);
int minValue;
int maxValue;
if (expAttribute.Value.Contains(">") && expAttribute.Value.Contains("<="))
{
//evaluate minValue < size > maxValue
var stringValue = expAttribute.Value.Replace("<=", string.Empty).Replace(">", string.Empty).Trim();
var stringValueArray = stringValue.Split('&');
if (int.TryParse(stringValueArray[1], out minValue) &&
int.TryParse(stringValueArray[0], out maxValue))
{
if (minValue < size &&
size < maxValue)
return returnString;
}
}
else if (expAttribute.Value.Contains(">"))
{
//evaluate size > value
var stringValue = expAttribute.Value.Replace(">", string.Empty).Trim();
if (int.TryParse(stringValue, out maxValue))
{
if (size > maxValue)
return returnString;
}
}
else if (expAttribute.Value.Contains("<="))
{
//else evaluate size < value
var stringValue = expAttribute.Value.Replace("<=", string.Empty).Trim();
if (int.TryParse(stringValue, out minValue))
{
if (size < minValue)
return returnString;
}
}
}
return "No condition was met!";
}
private static string CreateResultString(XElement item)
{
var statusAttribute = item.Attribute("Status");
var returnString = statusAttribute == null ? "Status" : statusAttribute.Value;
var colorAttribute = item.Attribute("CellColor");
returnString += colorAttribute == null ? "-Color" : "-" + colorAttribute.Value;
return returnString;
}
usage
var xmlDecide = XDocument.Load("Decide.xml");
Console.WriteLine("50MB: " + xmlDecide.AlertLevel(50));
Console.WriteLine("142MB: " + xmlDecide.AlertLevel(142));
Console.WriteLine("212MB: " + xmlDecide.AlertLevel(212));
EDIT: You can use the same code for use with XmlDocument instead of XDocument. Just change "Attribute" to "Attributes.GetNamedItem" and "Descendants" to "GetElementsByTagName"
This is complicated.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string XML =
"<Decide Name=\"MemoryCheck\" CommonUnit=\"MB\">" +
"<Decision CellColor=\"Red\" Status=\"Critical\" Exp=\"<=100\" />" +
"<Decision CellColor=\"Yellow\" Status=\"Warning\" Exp=\"<=200 & >100\"/>" +
"<Decision CellColor=\"Green\" Status=\"OK\" Exp=\">200\" />" +
"</Decide>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(XML);
XmlNodeList memoryCheck = doc.GetElementsByTagName("Decision");
foreach(XmlNode decision in memoryCheck)
{
Decision newDecision = new Decision();
Decision.decisions.Add(newDecision);
newDecision.Cellcolor = decision.Attributes.GetNamedItem("CellColor").Value;
newDecision.status = decision.Attributes.GetNamedItem("Status").Value;
newDecision.low = 0;
newDecision.high = null;
string exps = decision.Attributes.GetNamedItem("Exp").Value;
string[] expsArray = exps.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string exp in expsArray)
{
if(exp.StartsWith("<="))
{
newDecision.high = int.Parse(exp.Substring(exp.IndexOf("=") + 1));
}
if(exp.StartsWith(">"))
{
newDecision.low = int.Parse(exp.Substring(exp.IndexOf(">") + 1));
}
}
}
Decision result = Decision.GetBySize(212);
}
}
public class Decision
{
public static List<Decision> decisions = new List<Decision>();
public string Cellcolor { get; set; }
public string status { get; set; }
public int? low { get; set; }
public int? high {get; set;}
public static Decision GetBySize(int memory)
{
Decision newDecision = null;
foreach(Decision decision in decisions)
{
if (memory >= decision.low)
{
if (decision.high == null)
{
newDecision = decision;
break;
}
else
{
if (memory <= decision.high)
{
newDecision = decision;
break;
}
}
}
}
return newDecision;
}
}
}

How to find all the hardcoded values in a C# project(solution)?

This question is not asking about hard coded strings only, but magic numbers etc. as well.
Is there a way to find all the hard coded values i.e. string , magic numbers and what not in C# project/solution in VS?
What prompted this question is a project that I am looking at, I just found 174 times a string value was hardcodely repeated!
What you could do is program Roslyn, the (not so) new cool kid in town. It allows you to parse C# (or VB.NET) projects quite easily. Then you can visit the detected nodes and check what you really want to check. Detecting magic literals for a machine is not always as easy as it seems for a human. For example, is 1 really a magic number? I personally consider it's not, but 2 is more suspect...
Anyway, here is a small sample that does a good part of the job I believe, but it could/should be improved, maybe to tailor your exact business needs or rules (which is very interesting).
Note Roslyn can also be used directly in the context of Visual Studio, so you could turn this sample into what's called a diagnostic (an extension to Visual Studio) that can help you directly live from within the IDE. There are samples for this: Samples and Walkthroughs
class Program
{
static void Main(string[] args)
{
var text = #"
public class MyClass
{
public void MyMethod()
{
const int i = 0; // this is ok
decimal d = 11; // this is not ok
string s = ""magic"";
if (i == 29) // another magic
{
}
else if (s != ""again another magic"")
{
}
}
}";
ScanHardcodedFromText("test.cs", text, (n, s) =>
{
Console.WriteLine(" " + n.SyntaxTree.GetLineSpan(n.FullSpan) + ": " + s);
}).Wait();
}
public static async Task ScanHardcodedFromText(string documentName, string text, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (text == null)
throw new ArgumentNullException("text");
AdhocWorkspace ws = new AdhocWorkspace();
var project = ws.AddProject(documentName + "Project", LanguageNames.CSharp);
ws.AddDocument(project.Id, documentName, SourceText.From(text));
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcodedFromSolution(string solutionFilePath, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (solutionFilePath == null)
throw new ArgumentNullException("solutionFilePath");
var ws = MSBuildWorkspace.Create();
await ws.OpenSolutionAsync(solutionFilePath);
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcodedFromProject(string solutionFilePath, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (solutionFilePath == null)
throw new ArgumentNullException("solutionFilePath");
var ws = MSBuildWorkspace.Create();
await ws.OpenProjectAsync(solutionFilePath);
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcoded(Workspace workspace, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (workspace == null)
throw new ArgumentNullException("workspace");
if (scannedFunction == null)
throw new ArgumentNullException("scannedFunction");
foreach (var project in workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
var tree = await document.GetSyntaxTreeAsync();
var root = await tree.GetRootAsync();
foreach (var n in root.DescendantNodesAndTokens())
{
if (!CanBeMagic(n.Kind()))
continue;
if (IsWellKnownConstant(n))
continue;
string suggestion;
if (IsMagic(n, out suggestion))
{
scannedFunction(n, suggestion);
}
}
}
}
}
public static bool IsMagic(SyntaxNodeOrToken kind, out string suggestion)
{
var vdec = kind.Parent.Ancestors().OfType<VariableDeclarationSyntax>().FirstOrDefault();
if (vdec != null)
{
var dec = vdec.Parent as MemberDeclarationSyntax;
if (dec != null)
{
if (!HasConstOrEquivalent(dec))
{
suggestion = "member declaration could be const: " + dec.ToFullString();
return true;
}
}
else
{
var ldec = vdec.Parent as LocalDeclarationStatementSyntax;
if (ldec != null)
{
if (!HasConstOrEquivalent(ldec))
{
suggestion = "local declaration contains at least one non const value: " + ldec.ToFullString();
return true;
}
}
}
}
else
{
var expr = kind.Parent.Ancestors().OfType<ExpressionSyntax>().FirstOrDefault();
if (expr != null)
{
suggestion = "expression uses a non const value: " + expr.ToFullString();
return true;
}
}
// TODO: add other cases?
suggestion = null;
return false;
}
private static bool IsWellKnownConstant(SyntaxNodeOrToken node)
{
if (!node.IsToken)
return false;
string text = node.AsToken().Text;
if (text == null)
return false;
// note: this is naïve. we also should add 0d, 0f, 0m, etc.
if (text == "1" || text == "-1" || text == "0")
return true;
// ok for '\0' or '\r', etc.
if (text.Length == 4 && text.StartsWith("'\\") && text.EndsWith("'"))
return true;
if (text == "' '")
return true;
// TODO add more of these? or make it configurable...
return false;
}
private static bool HasConstOrEquivalent(SyntaxNode node)
{
bool hasStatic = false;
bool hasReadOnly = false;
foreach (var tok in node.ChildTokens())
{
switch (tok.Kind())
{
case SyntaxKind.ReadOnlyKeyword:
hasReadOnly = true;
if (hasStatic)
return true;
break;
case SyntaxKind.StaticKeyword:
hasStatic = true;
if (hasReadOnly)
return true;
break;
case SyntaxKind.ConstKeyword:
return true;
}
}
return false;
}
private static bool CanBeMagic(SyntaxKind kind)
{
return kind == SyntaxKind.CharacterLiteralToken ||
kind == SyntaxKind.NumericLiteralToken ||
kind == SyntaxKind.StringLiteralToken;
}
}
If you run this little program (I've also provided helper methods to use it on solution or projects), it will output this:
test.cs: (6,20)-(6,22): local declaration contains at least one non const value: decimal d = 11; // this is not ok
test.cs: (7,19)-(7,26): local declaration contains at least one non const value: string s = "magic";
test.cs: (8,17)-(8,19): expression uses a non const value: i == 29
test.cs: (11,22)-(11,43): expression uses a non const value: s != "again another magic"
I have some code which can find magic numbers and hard coded non-constant strings. May be that can help someone -
/// <summary>
/// Scans all cs files in the solutions for magic strings and numbers using the Roslyn
/// compiler and analyzer tools.
/// Based upon a Roslyn code sample.
/// </summary>
class MagicStringAnalyzer
{
protected static Filter filter;
static void Main(string[] args)
{
string outputPath = #"E:\output.txt";
string solutionPath = #"E:\Solution.sln";
filter = new Filter(#"E:\IgnorePatterns.txt");
if (File.Exists(outputPath))
{
OverWriteFile(outputPath);
}
analyzeSolution(outputPath, solutionPath);
}
protected static void loadFilters()
{
}
private static void OverWriteFile(string path)
{
Console.WriteLine("Do you want to overwrite existing output file? (y/n)");
if (Console.ReadKey().Key == ConsoleKey.Y)
{
File.Delete(path);
Console.WriteLine("");
}
else
{
Environment.Exit(-1);
}
}
public static void analyzeSolution(string outputPath, string solutionPath)
{
Console.WriteLine("Analyzing file...");
System.IO.StreamWriter writer = new System.IO.StreamWriter(outputPath);
ScanHardcodedFromSolution(solutionPath, (n, s) =>
{
string syntaxLineSpan = n.SyntaxTree.GetLineSpan(n.FullSpan).ToString();
if (!filter.IsMatch(syntaxLineSpan))
{
writer.WriteLine(" " + syntaxLineSpan + ": \r\n" + s + "\r\n\r\n");
}
}).Wait();
writer.Close();
}
public static async Task ScanHardcodedFromText(string documentName, string text, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (text == null)
throw new ArgumentNullException("text");
AdhocWorkspace ws = new AdhocWorkspace();
var project = ws.AddProject(documentName + "Project", LanguageNames.CSharp);
ws.AddDocument(project.Id, documentName, SourceText.From(text));
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcodedFromSolution(string solutionFilePath, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (solutionFilePath == null)
throw new ArgumentNullException("solutionFilePath");
var ws = MSBuildWorkspace.Create();
await ws.OpenSolutionAsync(solutionFilePath);
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcodedFromProject(string solutionFilePath, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (solutionFilePath == null)
throw new ArgumentNullException("solutionFilePath");
var ws = MSBuildWorkspace.Create();
await ws.OpenProjectAsync(solutionFilePath);
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcoded(Workspace workspace, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (workspace == null)
throw new ArgumentNullException("workspace");
if (scannedFunction == null)
throw new ArgumentNullException("scannedFunction");
foreach (var project in workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
var tree = await document.GetSyntaxTreeAsync();
var root = await tree.GetRootAsync();
foreach (var n in root.DescendantNodesAndTokens())
{
if (!CanBeMagic(n.Kind()))
continue;
if (IsWellKnownConstant(n))
continue;
string suggestion;
if (IsMagic(n, out suggestion))
{
scannedFunction(n, suggestion);
}
}
}
}
}
public static bool IsMagic(SyntaxNodeOrToken kind, out string suggestion)
{
var vdec = kind.Parent.Ancestors().OfType<VariableDeclarationSyntax>().FirstOrDefault();
if (vdec != null)
{
var dec = vdec.Parent as MemberDeclarationSyntax;
if (dec != null)
{
if (!HasConstOrEquivalent(dec))
{
suggestion = "member declaration could be const: " + dec.ToFullString();
return true;
}
}
else
{
var ldec = vdec.Parent as LocalDeclarationStatementSyntax;
if (ldec != null)
{
if (!HasConstOrEquivalent(ldec))
{
suggestion = "local declaration contains at least one non const value: " + ldec.ToFullString();
return true;
}
}
}
}
else
{
var expr = kind.Parent.Ancestors().OfType<ExpressionSyntax>().FirstOrDefault();
if (expr != null)
{
suggestion = "expression uses a non const value: " + expr.ToFullString();
return true;
}
}
// TODO: add other cases?
suggestion = null;
return false;
}
private static bool IsWellKnownConstant(SyntaxNodeOrToken node)
{
if (!node.IsToken)
return false;
string text = node.AsToken().Text;
if (text == null)
return false;
// note: this is naïve. we also should add 0d, 0f, 0m, etc.
if (text == "1" || text == "-1" || text == "0")
return true;
// ok for '\0' or '\r', etc.
if (text.Length == 4 && text.StartsWith("'\\") && text.EndsWith("'"))
return true;
if (text == "' '")
return true;
if (text == "")
return true;
return false;
}
private static bool HasConstOrEquivalent(SyntaxNode node)
{
bool hasStatic = false;
bool hasReadOnly = false;
foreach (var tok in node.ChildTokens())
{
switch (tok.Kind())
{
case SyntaxKind.ReadOnlyKeyword:
hasReadOnly = true;
if (hasStatic)
return true;
break;
case SyntaxKind.StaticKeyword:
hasStatic = true;
if (hasReadOnly)
return true;
break;
case SyntaxKind.ConstKeyword:
return true;
}
}
return false;
}
private static bool CanBeMagic(SyntaxKind kind)
{
return kind == SyntaxKind.CharacterLiteralToken ||
kind == SyntaxKind.NumericLiteralToken ||
kind == SyntaxKind.StringLiteralToken;
}
}
public class Filter
{
protected string[] patterns;
public Filter(string path)
{
loadFilters(path);
}
protected void loadFilters(string path)
{
patterns = File.ReadAllLines(path);
}
public bool IsMatch(string input)
{
foreach (string pattern in patterns)
{
if(Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase))
{
return true;
}
}
return false;
}
}
Your txt file that contains file names to ignore would contain values like -
Constant.cs
Resoures.Designer.cs
Configuration.cs
Reference.cs
Test
Give name of your solution in solution path and run this. This will generate txt file for you with all hard coded strings and magic numbers.
Edit:
To compile the project, you'll need to install Microsoft.CodeAnalysis NuGet package into your console app project:
Install-Package Microsoft.CodeAnalysis -Pre
Here is a complete list of references you should have in your Program.cs:
using System;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;
namespace MagicStringAnalyzer
{
// the rest of the code goes here...
}

how to save xls file as xlsx file using NPOI c#?

I'm using NPOI to open XLS file, then add some modifications to the XLS file.
at the end i want to save it as XLSX file.
i'm using this code to save it as XLS file:
using (var fs = new FileStream(Name, FileMode.Create, FileAccess.Write))
{
wb.Write(fs);
}
Is it possible to save this XLS file as XLSX file using NPOI in C#?
Thanks in advance for your response
It's an old question, but I've encountered with the similar problem. This code is using NPOI 2.2.1 from https://npoi.codeplex.com/releases. This code is based on code from Convert xlsx file to xls using NPOI in c# question, but it converts xls->xlsx, not xlsx->xls. Plus it converts styles and fixes bug with ranges. This code works for simple workbooks without charts and other complex stuff.
using NPOI.XSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.HSSF.UserModel;
using System.Collections.Generic;
using System.Linq;
using System.IO;
namespace XlsToXlsxConverter
{
public static class XLSToXLSXConverter
{
public static byte[] Convert(Stream sourceStream)
{
var source = new HSSFWorkbook(sourceStream);
var destination = new XSSFWorkbook();
for (int i = 0; i < source.NumberOfSheets; i++)
{
var xssfSheet = (XSSFSheet)destination.CreateSheet(source.GetSheetAt(i).SheetName);
var hssfSheet = (HSSFSheet)source.GetSheetAt(i);
CopyStyles(hssfSheet, xssfSheet);
CopySheets(hssfSheet, xssfSheet);
}
using (var ms = new MemoryStream())
{
destination.Write(ms);
return ms.ToArray();
}
}
private static void CopyStyles(HSSFSheet from, XSSFSheet to)
{
for (short i = 0; i <= from.Workbook.NumberOfFonts; i++)
{
CopyFont(to.Workbook.CreateFont(), from.Workbook.GetFontAt(i));
}
for (short i = 0; i < from.Workbook.NumCellStyles; i++)
{
CopyStyle(to.Workbook.CreateCellStyle(), from.Workbook.GetCellStyleAt(i), to.Workbook, from.Workbook);
}
}
private static void CopyFont(IFont toFront, IFont fontFrom)
{
toFront.Boldweight = fontFrom.Boldweight;
toFront.Charset = fontFrom.Charset;
toFront.Color = fontFrom.Color;
toFront.FontHeightInPoints = fontFrom.FontHeightInPoints;
toFront.FontName = fontFrom.FontName;
toFront.IsBold = fontFrom.IsBold;
toFront.IsItalic = fontFrom.IsItalic;
toFront.IsStrikeout = fontFrom.IsStrikeout;
//toFront.Underline = fontFrom.Underline; <- bug in npoi setter
}
private static void CopyStyle(ICellStyle toCellStyle, ICellStyle fromCellStyle, IWorkbook toWorkbook, IWorkbook fromWorkbook)
{
toCellStyle.Alignment = fromCellStyle.Alignment;
toCellStyle.BorderBottom = fromCellStyle.BorderBottom;
toCellStyle.BorderDiagonal = fromCellStyle.BorderDiagonal;
toCellStyle.BorderDiagonalColor = fromCellStyle.BorderDiagonalColor;
toCellStyle.BorderDiagonalLineStyle = fromCellStyle.BorderDiagonalLineStyle;
toCellStyle.BorderLeft = fromCellStyle.BorderLeft;
toCellStyle.BorderRight = fromCellStyle.BorderRight;
toCellStyle.BorderTop = fromCellStyle.BorderTop;
toCellStyle.BottomBorderColor = fromCellStyle.BottomBorderColor;
toCellStyle.DataFormat = fromCellStyle.DataFormat;
toCellStyle.FillBackgroundColor = fromCellStyle.FillBackgroundColor;
toCellStyle.FillForegroundColor = fromCellStyle.FillForegroundColor;
toCellStyle.FillPattern = fromCellStyle.FillPattern;
toCellStyle.Indention = fromCellStyle.Indention;
toCellStyle.IsHidden = fromCellStyle.IsHidden;
toCellStyle.IsLocked = fromCellStyle.IsLocked;
toCellStyle.LeftBorderColor = fromCellStyle.LeftBorderColor;
toCellStyle.RightBorderColor = fromCellStyle.RightBorderColor;
toCellStyle.Rotation = fromCellStyle.Rotation;
toCellStyle.ShrinkToFit = fromCellStyle.ShrinkToFit;
toCellStyle.TopBorderColor = fromCellStyle.TopBorderColor;
toCellStyle.VerticalAlignment = fromCellStyle.VerticalAlignment;
toCellStyle.WrapText = fromCellStyle.WrapText;
toCellStyle.SetFont(toWorkbook.GetFontAt((short)(fromCellStyle.GetFont(fromWorkbook).Index + 1 )));
}
private static void CopySheets(HSSFSheet source, XSSFSheet destination)
{
var maxColumnNum = 0;
var mergedRegions = new List<CellRangeAddress>();
var styleMap = new Dictionary<int, HSSFCellStyle>();
for (int i = source.FirstRowNum; i <= source.LastRowNum; i++)
{
var srcRow = (HSSFRow)source.GetRow(i);
var destRow = (XSSFRow)destination.CreateRow(i);
if (srcRow != null)
{
CopyRow(source, destination, srcRow, destRow, mergedRegions);
if (srcRow.LastCellNum > maxColumnNum)
{
maxColumnNum = srcRow.LastCellNum;
}
}
}
for (int i = 0; i <= maxColumnNum; i++)
{
destination.SetColumnWidth(i, source.GetColumnWidth(i));
}
}
private static void CopyRow(HSSFSheet srcSheet, XSSFSheet destSheet, HSSFRow srcRow, XSSFRow destRow, List<CellRangeAddress> mergedRegions)
{
destRow.Height = srcRow.Height;
for (int j = srcRow.FirstCellNum; srcRow.LastCellNum >= 0 && j <= srcRow.LastCellNum; j++)
{
var oldCell = (HSSFCell)srcRow.GetCell(j);
var newCell = (XSSFCell)destRow.GetCell(j);
if (oldCell != null)
{
if (newCell == null)
{
newCell = (XSSFCell)destRow.CreateCell(j);
}
CopyCell(oldCell, newCell);
var mergedRegion = GetMergedRegion(srcSheet, srcRow.RowNum,
(short)oldCell.ColumnIndex);
if (mergedRegion != null)
{
var newMergedRegion = new CellRangeAddress(mergedRegion.FirstRow,
mergedRegion.LastRow, mergedRegion.FirstColumn, mergedRegion.LastColumn);
if (IsNewMergedRegion(newMergedRegion, mergedRegions))
{
mergedRegions.Add(newMergedRegion);
destSheet.AddMergedRegion(newMergedRegion);
}
}
}
}
}
private static void CopyCell(HSSFCell oldCell, XSSFCell newCell)
{
CopyCellStyle(oldCell, newCell);
CopyCellValue(oldCell, newCell);
}
private static void CopyCellValue(HSSFCell oldCell, XSSFCell newCell)
{
switch (oldCell.CellType)
{
case CellType.String:
newCell.SetCellValue(oldCell.StringCellValue);
break;
case CellType.Numeric:
newCell.SetCellValue(oldCell.NumericCellValue);
break;
case CellType.Blank:
newCell.SetCellType(CellType.Blank);
break;
case CellType.Boolean:
newCell.SetCellValue(oldCell.BooleanCellValue);
break;
case CellType.Error:
newCell.SetCellErrorValue(oldCell.ErrorCellValue);
break;
case CellType.Formula:
newCell.SetCellFormula(oldCell.CellFormula);
break;
default:
break;
}
}
private static void CopyCellStyle(HSSFCell oldCell, XSSFCell newCell)
{
if (oldCell.CellStyle == null) return;
newCell.CellStyle = newCell.Sheet.Workbook.GetCellStyleAt((short)(oldCell.CellStyle.Index + 1));
}
private static CellRangeAddress GetMergedRegion(HSSFSheet sheet, int rowNum, short cellNum)
{
for (var i = 0; i < sheet.NumMergedRegions; i++)
{
var merged = sheet.GetMergedRegion(i);
if (merged.IsInRange(rowNum, cellNum))
{
return merged;
}
}
return null;
}
private static bool IsNewMergedRegion(CellRangeAddress newMergedRegion,
List<CellRangeAddress> mergedRegions)
{
return !mergedRegions.Any(r =>
r.FirstColumn == newMergedRegion.FirstColumn &&
r.LastColumn == newMergedRegion.LastColumn &&
r.FirstRow == newMergedRegion.FirstRow &&
r.LastRow == newMergedRegion.LastRow);
}
}
}
It is possible in general, but not quite easy, rather it is significantly complicated task.
XLS file format is handled by HSSFWorkbook class (and according HSSFSheet and so on).
XLSX file format is handled by XSSFWorkbook class (and XSSFSheet and so on).
So in order to save your file as XLSX after you opened and modified it using NPOI, you need to create new XSSFWorkbook, then for each worksheet of your source file you need to create appropriate XSSFSheet, copy data to it from your original worksheet and so on until you will get full copy of your data, and then save workbook to file.

Categories