I know I can test the condition and run the code like this:
if (scrapedElement.Contains(".html")
string [] Test = new string[] { scrapedElement, string.empty }
else
string [] Test = new string[] { scrapedElement }
However, I want to do it in a single line if possible. Something similar to this (this is the full line of code that I want it to work in):
File.AppendAllLines(#"C:\Users\DJB\Documents\Visual Studio 2017\Projects\TempFiles\WebScraperExport.csv", new[] { (scrapedElement.Contains(".html") ? scrapedElement, string.Empty : scrapedElement)});
What I am doing is a web scraper that then saves the files in an excel file. For every element it finds that is a link, add a blank line after it, if not just add the element.
This compiled for me and should do what You need
using System;
public class Test
{
public static void Main()
{
string scrapedElement = "test test .html test";
string [] Test = scrapedElement.Contains(".html")
? new string[] { scrapedElement, string.Empty }
: new string[] { scrapedElement };
}
}
Another alternative that will handle Your case without duplication (but not 1-liner!)
using System;
public class Test
{
public static void Main()
{
string scrapedElement = "test test .html test";
string [] Test =new string[scrapedElement.Contains(".html")?2:1];
Test[0] = scrapedElement;
}
}
Related
I'm new to C# and OOP I have two different files. File A where I've created the list.
//This file will contain predetermine list of responses.
using System.Linq;
using System.Collections.Generic;
public class Responses
{
//bot name
static string nomber = "Jarvis";
List<string> answer = new List<string>(){
$"Mi nomber ta {nomber}",
"Mi ta bon"
};
public void AddToList(string value){
this.answer.Add(value);
}
public string Answer(int id)
{
return answer.ElementAt(id);
}
}
And in file B I have these two lines of code to add the string 1 to the list, I've also included the Generics and Linq System in file B.
var response = new Responses();
response.answer.Add("1");
I've tried creating a method called AddToList to pass the value and add it to the list, but with no luck. When I try to display the list at index 2 I'll get an argument out of range instead of the value "1".
*Also both files are located in the same folder.
After read your source code, I understand your problem. First you Add new element to Response.answers of Interperter and it return id then you get Response.answer of Output by that id. Of course you never get that, because they were 2 different instances.
I provide 2 options for you:
Option 1: Make Reponses single instance (singleton)
Responses.cs
using System.Linq;
using System.Collections.Generic;
public class Responses
{
private static Responses _instance = new Responses();
public static GetInstance() {
return _instance;
}
//bot name
static string nomber = "Jarvis";
List<string> answer = new List<string>(){
$"Mi nomber ta {nomber}",
"Mi ta bon"
};
public void AddToList(string value){
this.answer.Add(value);
}
public string Answer(int id)
{
return answer.ElementAt(id);
}
}
Then change on other files
//from
var responses = new Responses();
//to
var responses = Responses.GetInstance();
//from
responses.answer.Add()
//to
reponses.AddToList()
Option 2: Make Responses static
Response.cs
using System.Linq;
using System.Collections.Generic;
public static class Responses
{
//bot name
static string nomber = "Jarvis";
static List<string> answer = new List<string>(){
$"Mi nomber ta {nomber}",
"Mi ta bon"
};
public static void AddToList(string value){
this.answer.Add(value);
}
public static string Answer(int id)
{
return answer.ElementAt(id);
}
}
Output.cs
using System;
public class Output
{
public void Return(int respondType, int respond)
{
switch(respondType)
{
case 0:
Console.WriteLine(Responses.Answer(respond));
break;
default:
Console.WriteLine("Mi no ta kompronde");
break;
}
}
}
Interperter.cs
using System.Collections.Generic;
using System.Linq;
public class Interpreter
{
public int UserInputType(string value)
{
// Turns the user input into an array of words
string[] words = value.Split(' ');
int returnValue = 2;
//int match = 0;
Responses.AddToList("1");
//This stores the correct response to the given question
//var element = new List<int>();
foreach(var word in words)
{
// if(!string.IsNullOrWhiteSpace(word))
// {
foreach(var listOfQuestions in userInputedQuestions)
{
//Convert words in the listOfQuestions to array string to match them with the userInputedQuestion
string[] listOfQWords = listOfQuestions.Split(" ");
//Check how many words matches the predefined list of questions
foreach(var qWord in listOfQWords){
if(word == qWord){
returnValue = 0;
}
}
}
}
// }
return returnValue;
}
private List<string> userInputedQuestions = new List<string>(){
"Ki ta bo nomber?",
"Konta ku bo?"
};
}
Hope it helps
I am writing a program in C# for extracting comments from code. I am using Roslyn compiler to do that. It's great, because I am just visiting the whole abstract syntax tree and fetching SingleLineComment trivia, MultiLineComment trivia and DocumentationComment trivia syntax from the file in solution. But there is a problem because programmers often write comments like that:
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
You can see that these are three single line comments, but I want them them to be fetched from code as one comment. Can I achieve that with Roslyn or maybe there is another way? Because that's frequent situation when programmers are writing multi line commments using single line comments syntax.
My code for extracting comments looks like this:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
namespace RoslynPlay
{
public class CommentStore
{
public List<Comment> Comments { get; } = new List<Comment>();
public void AddCommentTrivia(SyntaxTrivia trivia,
LocationStore commentLocationstore, string fileName)
{
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia)
{
Comments.Add(new SingleLineComment(trivia.ToString(),
trivia.GetLocation().GetLineSpan().EndLinePosition.Line + 1, commentLocationstore)
{
FileName = fileName,
});
}
else if (trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
{
Comments.Add(new MultiLineComment(trivia.ToString(),
trivia.GetLocation().GetLineSpan().StartLinePosition.Line + 1,
trivia.GetLocation().GetLineSpan().EndLinePosition.Line + 1, commentLocationstore)
{
FileName = fileName,
});
}
}
public void AddCommentNode(DocumentationCommentTriviaSyntax node,
LocationStore commentLocationstore, string fileName)
{
Comments.Add(new DocComment(node.ToString(),
node.GetLocation().GetLineSpan().StartLinePosition.Line + 1,
node.GetLocation().GetLineSpan().EndLinePosition.Line,
commentLocationstore)
{
FileName = fileName,
});
}
}
}
and in main main file (Program.cs) I am launching comment extraction from code like this:
string fileContent;
SyntaxTree tree;
SyntaxNode root;
CommentsWalker commentWalker;
MethodsAndClassesWalker methodWalker;
string[] files = Directory.GetFiles(projectPath, $"*.cs", SearchOption.AllDirectories);
var commentStore = new CommentStore();
Console.WriteLine("Reading files...");
ProgressBar progressBar = new ProgressBar(files.Length);
foreach (var file in files)
{
fileContent = File.ReadAllText(file);
string filePath = new Regex($#"{projectPath}\\(.*)$").Match(file).Groups[1].ToString();
tree = CSharpSyntaxTree.ParseText(fileContent);
root = tree.GetRoot();
commentWalker = new CommentsWalker(filePath, commentStore);
commentWalker.Visit(root);
progressBar.UpdateAndDisplay();
}
and here is also the comment walker:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace RoslynPlay
{
public class CommentsWalker : CSharpSyntaxWalker
{
private string _fileName;
private CommentStore _commentStore;
public CommentsWalker(string fileName,
CommentStore commentStore)
: base(SyntaxWalkerDepth.StructuredTrivia)
{
_fileName = fileName;
_commentStore = commentStore;
}
public override void VisitTrivia(SyntaxTrivia trivia)
{
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia
|| trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
{
_commentStore.AddCommentTrivia(trivia, _commentLocationStore, _fileName);
}
base.VisitTrivia(trivia);
}
public override void VisitDocumentationCommentTrivia(DocumentationCommentTriviaSyntax node)
{
_commentStore.AddCommentNode(node, _commentLocationStore, _fileName);
base.VisitDocumentationCommentTrivia(node);
}
}
}
And the problem is because trivia.Kind() == SyntaxKind.SingleLineCommentTrivia extracts only single line of comments, but I want to extract single line comments blocks as one comment.
I am trying to pass a json string to a C#-Program using Commandline.
The JSON-String looks like this:
{
"config": {
"script": {
"script_name": "test",
"dir": "D:\\test",
"destination": "M:\\neu\\test",
"params": "/b /s /r:3 /w:5"
}
}
}
In Commandline it looks like this:
{"config":{"script":{"script_name":"test","dir":"D:\\test","destination":"M:\\neu\\test","params":"/b /s /r:3 /w:5"}}}
But if I just pass the string then it gets chunked into several pieces. But I want my program to see it as just a single string.
Do I have to adapt my JSON-String?
Declare it as a string with "" and escape the other " with \ and it should work.
Command line:
"{\"config\":{\"script\":{\"script_name\":\"test\",\"dir\":\"D:\\test\",\"destination\":\"M:\\neu\\test\",\"params\":\"/b /s /r:3 /w:5\"}}}"
This should work:
var jsonString = Environment.CommandLine;
I tested it with the debugger like so:
var jsonString = Environment.CommandLine;
// (*) This correction makes it work, although it is pretty ugly:
jsonString = jsonString.Split(new string[] { ".exe\" " }, StringSplitOptions.None)[1];
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(jsonString);
Debugging with VS2015, and not modifying the json input (not even removing the line changes). I am using the same structure as your input:
public class Script
{
public string script_name { get; set; }
public string dir { get; set; }
public string destination { get; set; }
public string #params { get; set; }
}
public class Config
{
public Script script { get; set; }
}
public class RootObject
{
public Config config { get; set; }
}
About (*) => The problem with the deserialization is that the exe info is added in front of the command line with Environment.CommandLine and it "pollutes" the json like this: jsonString =
"path\to\assembly\name.vshost.exe" {
"config": {
"script": {
"script_name": "test",
"dir": "D:\\test",
"destination": "M:\\neu\\test",
"params": "/b /s /r:3 /w:5"
}
}
}
If anybody has a prettier fix to this problem please let me know.
Try to save the JSON object into a file, and pass the file as the argument to your application.
#Wildcard27 :
This is an actual use case in order to create Windows Tasks which was used for the faculty degree app. The JSON was just a simple serialization of a DTO that I was using.
When you serialize the JSON, just save it into a blank file, giving it a proper name so that is unique.
private string CreateTaskConfigurationFile(string taskName, EquipmentEventExtended eventData, string host)
{
List<Change> changes = new List<Change>
{
new Change(MailConstants.EventName,eventData.EventName),
new Change(MailConstants.Deadline, eventData.DateTo.Value.ToShortDateString()),
new Change(MailConstants.EventDetails, eventData.EventDetails),
new Change(MailConstants.Link,$"{host}/Inventory/Details/{eventData.InventoryId}")
};
MailTaskModel mtm = new MailTaskModel
{
Body = MailConstants.UpdateTemplate(MailConstants.TaskMailTemplate, changes),
Subject = "[Reminder] Upcoming Event needs your attention",
ToAddress = "abcdef#gmail.com",
IsHtml = true
};
var fileName = string.Format(#"E:\{0}.json", taskName);
using (StreamWriter file = File.CreateText(fileName))
{
JsonSerializer js = new JsonSerializer();
js.Serialize(file, mtm);
}
return fileName;
}
Then you provide the file path as an argument to the console application:
static void Main(string[] args)
{
var configFilePath = args[0];
var mailConfig = LoadConfigurationFile(configFilePath);
MailManager manager = new MailManager(mailConfig.ToAddress, mailConfig.FromAddress,mailConfig.Subject, mailConfig.Body,mailConfig.IsHtml);
manager.SendMail();
}
private static MailTaskModel LoadConfigurationFile(string configurationFilePath)
{
MailTaskModel mailConfig;
using(var sr = new StreamReader(configurationFilePath))
{
string json = sr.ReadToEnd();
mailConfig = JsonConvert.DeserializeObject<MailTaskModel>(json);
}
return mailConfig;
}
You can then use something like
ConsoleApplication.exe -yourFilePath
I've removed noisy check-ups for nulls and all that so that it's more clear.
Instead of looking at the "string[] args" you could use Environment.CommandLine.
From MSDN https://msdn.microsoft.com/en-us/library/system.environment.commandline.aspx
public static void Main()
{
Console.WriteLine();
// Invoke this sample with an arbitrary set of command line arguments.
Console.WriteLine("CommandLine: {0}", Environment.CommandLine);
}
// The example displays output like the following:
// C:>env0 ARBITRARY TEXT
//
// CommandLine: env0 ARBITRARY TEXT
Just send json value to commandline after catch value and replace it. It's work for me.
args[1].Replace("{","{\"").Replace(":","\":\"").Replace(",","\",\"").Replace("}","\"}");
Following on from #Selcuk Gurals post, here is a more complete answer:
args[1].Replace("{", "{\"").Replace(":", "\":\"").Replace(",", "\",\"").Replace("}", "\"}").Replace(":\"[", ":[").Replace(":\"{", ":{").Replace("https\":\"", "https:").Replace("http\":\"", "http:").Replace("\":\"9", ":9").Replace("}\",", "},").Replace("]\",", "],").Replace("}\"}", "}}");
This caters for things like embedded http/https and ports. My port number was in the 9000 region... So a regex solution would be better. But it improves on the former answer
The value part of a JSON key/value pair can also be:
another JSON object
a list
"key": {}, ....
"key":[], ....
I want to detect class from a string.
example;
string test = "class Test { string Name; string PassWord;}"
string[] classNames = GetClassNamesFromString(test);
//Now string[0] has to be Test
Or more complex
string test = "[Export(typeof(ITest))]public class Test : ITest { }"
string[] classNames = GetClassNamesFromString(test);
//Now string[0] has to be Test
And must be work with generics.
I would simple use a Regex.
class\s+(\w+\d*)+
In that maner:
string test = "[Export(typeof(ITest))]public class Test : ITest { }";
var match = Regex.Match(test, #"class\s+(\w+\d*)+");
string classname = match.Value.Split(new []{' '}, StringSplitOptions.RemoveEmptyEntries)[1];
It even works with lots of space between class and the name:
string test = "[Export(typeof(ITest))]public class Test : ITest { }";
Try this:
var input = "public class MyClass {//whatever}";
var regex = new Regex(#"class\s(\w+)\s*[{,:]", RegexOptions.Compiled);
var result = regex.Match(input);
var className = result.Groups[1].Value;
If running this multiple times, use the RegexOptions.Compiled and store the regex in an instance variable to get better performance, otherwise you can remove the flag.
I let you take care of the error handling and don't forget to write some test conditions etc I have not added support for generics I also leave that to you to explore the awesomeness of REGEX.
I solved this way;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
var t = CSharpSyntaxTree.ParseText(#"
using System;
namespace Muhterem
{
class Main
{
static void Main(string[] args)
{
Hello();
}
static void Hello()
{
Console.WriteLine();
}
}
class Generic<T>
{}
abstract class AbstractClass
{}
}
");
var roo = t.GetRoot();
var classes = roo.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var y in classes)
{
Console.WriteLine(y.Identifier);
}
and output ;
My aim is to "Sanitize a string".
The class should do:
trim an input
make the first letter upper case.
Could you please tell me:
Is there a way to better code it?
Would it make sense to use a PARAMETER for a method like: CapitalizeFirstLetterTrim(string x)
when I initiate an object I need write a lot of code like below, any other way to make it shorter?
UserInputSanitizer myInput = new UserInputSanitizer();
myInput.Input = " ciao world";
string ouput = myInput.CapitalizeFirstLetterTrim();
Useful resource http://msdn.microsoft.com/en-us/library/bb311042.aspx
----------- CLASS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebProject.Core.Utilities
{
public class UserInputSanitizer
{
// Backing variables
private string _input;
// Properties
public string Input
{
set { _input = value; }
}
private string _output;
// Backing variables
// Properties
public string Output
{
get { return _output; }
}
public string CapitalizeFirstLetterTrim()
{
// Trim
_input.Trim();
// Make First letter UpperCase and the rest levae lower case
_output = _input.Substring(0, 1).ToUpper() + _input.Substring(1);
return Output;
}
}
}
I think I would create an extension method on string instead:
public static class MyStringExtensions{
public static string Sanitize(this string input)
{
if(input == null) throw new ArgumentNullException("input");
var trimmed = input.Trim();
return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(trimmed);
}
}
You would call the method like this:
var s = " Unsanitized ";
var sanitized = s.Sanitize();
You can use Extension Method to support your requirement
With extension methods , you can use method as if they are part of System.String class.
See Here
I would use an extension method for the string class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WebProject.Core.Utilities
{
public static class StringExtensions
{
public static string Sanitize(this string s)
{
//your code to sanitize your string, for example
if(s == null) throw new ArgumentNullException("s");
var trimmed = input.Trim();
return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(trimmed);
}
}
}
Then you can use:
string s = " UnsanitizedString";
s = s.Sanitize();
I would make the class and methods static
namespace WebProject.Core.Utilities
{
public static class UserInputSanitizer
{
public static string CapitalizeFirstLetterTrim(string input)
{
// Trim
input.Trim();
// Make First letter UpperCase and the rest levae lower case
return input.Substring(0, 1).ToUpper() + input.Substring(1);
}
}
}
and then you would call it like this:
string ouput = UserInputSanitizer.CapitalizeFirstLetterTrim(" ciao world");
Rather than a utility class, it may make more intuitive sense to write this as an extension method for string so you can just call it directly from the literal. Much less overhead.