I'm doing an assignment about working with structured / semi-structured / unstructured data and I'm doing a word count of Shakespeare plays (to see how language changes over time) by importing txt files of each play and an xml index file which stores key information about each play like the year it was written, character list etc.. Then I will remove character names, settings, punctuation and common words (and, but, or, if etc...) from the txt file ready for the word count - all in a console script run in C#. I'm writing a class for which each play's data will be stored and it currently looks like this:
class PlayImport
{
public string Title;
public DateTime Year;
public string location;
public string[] Cast;
public Counter[] WordCount;
public PlayImport(string location, int Num)
{
XmlDocument Reader = new XmlDocument();
Reader.Load(location);
this.Title = Convert.ToString(Reader.DocumentElement.ChildNodes[Num].Attributes["Title"].Value);
this.Year = Convert.ToDateTime(Reader.DocumentElement.ChildNodes[Num].Attributes["Year"].Value);
this.location = Convert.ToString(Reader.DocumentElement.ChildNodes[Num].Attributes["Location"].Value);
foreach (XmlNode xmlNode in Reader.DocumentElement.ChildNodes[Num].ChildNodes[0].ChildNodes)
this.Cast += Convert.ToString(xmlNode.Attributes["Name"].Value);
}
}
However, the final line (Cast +=) gives off an error cannot convert string to string[]. How can I get around this so that the character list gets bundled together into the Cast string array?
public string[] Cast;
The line above is a declaration of an array and this array hasn't been initialized anywhere. So you cannot add anything here until you inform the compiler that you want to initialize it with the space to store a certain number of strings.
....
this.Cast += Convert.ToString(xmlNode.Attributes["Name"].Value);
This line instead tries to execute a += operation on the previous array.
This is not possible because there is no operator defined for an array that is capable to do that operation, thus you get the error
A very simple and better approach is to declare your Cast field as a List<string>
public List<string> Cast = new List<string>();
then inside the foreach you just Add a new string to the existing string collection
foreach (XmlNode xmlNode in Reader.DocumentElement.ChildNodes[Num].ChildNodes[0].ChildNodes)
this.Cast.Add(Convert.ToString(xmlNode.Attributes["Name"].Value));
The advantage of using a List instead of an array is basically in the fact that you don't need to know in advance how many strings you want to store in the array, instead the list dinamically expand its internal storage to accomodate for new entries.
Related
I've been having a little problem with sharing variables to different objects, I was hoping to understand what I'm doing wrong and find a solution to the problem, I'm sort of new to programming using the object oriented programming language (C#) and I don't know what I'm doing wrong when it comes to that detail.
What I'm trying to do basically is to create a method which receives a user's input of some words as a string, it removes excess spaces from the string and creates a string array which holds every word within the string when it is seperated by a space.
The problem here is that the "return words;" at the end of the "RemoveSpace()" method isn't returning the value to "Test()" at all.
(I made sure to check that it was actually working using the debugger, but the RemoveSpace function is definitely working, just only within the method, and the returned value is seemingly ignored by the Test() method.)
(Also, to use Regex, use the "System.Text.RegularExpressions;" library)
What should I do? I've been racking my brain on it for a while and I'm out of ideas,
I'd much appreciate some help
public static class Reverso
{
static void Main(string[] args)
{
//User inputs the words as a string
string words = Console.ReadLine();
//Activates the Test object
Test(words);
}
public static void Test(string words)
{
//Activates the RemoveSpace object.
//It should receive the returned
//value here, but not working
RemoveSpace(words);
//Takes words into a string array
//seperated by spaces
string[] parts = words.Split(' ');
//Shows the result
Console.WriteLine(words);
}
public static string RemoveSpace(string words)
{
//Using regex in order to remove more
//than 1 space between words and characters
Regex regex = new Regex(#"[ ]{2,}", RegexOptions.None);
words = regex.Replace(words, #" ");
//Should return the value of the word
//to the Test object, *not working*
return words;
}
}
If you want to reassign to words inside RemoveSpace, you need to pass the variable by reference:
public static string RemoveSpace(ref string words) // Use ref to pass by reference
Then inside Test, you would call the method like this:
RemoveSpace(ref words);
Without passing by reference, the words argument of RemoveSpace is a separate variable to the words variable in the calling method, so reassigning only affects the scope of RemoveSpace.
The usual approach would be to reassign the string that is returned from RemoveSpace inside Test:
public static void Test(string words)
{
words = RemoveSpace(words);
I am currently trying to use Codecademy to learn how to use C# yet the last 'test' for the very second lesson asks us to convert a string to a list. I looked at a forum and it said you had to use loops which was not taught in the course yet, yet I wanted to use a loop anyway, how could I create the list with the for loop which possibly needs fixing? (And maybe help to check whether or not the other code is correct as it asks to convert bool to string and a random data type to another which I chose byte for.) Thanks.
bool pick = true;
byte number = 5;
string myTest = "Ping Pong";
Console.WriteLine(Convert.ToString(pick));
Console.WriteLine(Convert.ToInt32(number));
for ((char(myTest));)
{
Console.WriteLine(i);
}
You use string.ToCharArray to convert the string into an array of characters.
However you don't need to do that just to iterate over it, string implements IEnumerable<char>, so you can iterate over it directly.
Also for ((char(myTest));) makes absolutely no sense.
An string is already a character array and because System.String implements IEnumerable<char> you can just loop it:
/*
References you need:
using System;
using System.Collections.Generic;
using System.Linq;
*/
string myTest = "Ping Pong";
//looping a string
foreach (char character in myTest)
{
Console.WriteLine(character);
}
//Explicit converting a string to list of chars
var listChars = new List<char>();
listChars = myTest.ToList();
foreach (char character in listChars)
{
Console.WriteLine(character);
}
Similarly to passing a list through a function and adding elements to it, I want to pass a string and add characters to it. However, I do not want to change the reference.
Func(List myList) {
myList.Append("hello");
}
Func(List myList) {
myList = new List();
}
It's like the distinction between the two functions above. In one case you're adding an element to an existing reference to an object, in the other case you are changing the object that is referenced to.
With strings, I have noticed you always(?) change the object that is referenced to. Every solution i've found takes two or more strings, adds them together and returns a new string.
Is there a way to use the same string instance and add one or more characters to this specific instance?
With strings, I have noticed you always(?) change the object that is referenced to.
Because strings are immutable. You can't change a string in .NET. That opens the door to many optimizations (such as string interning), but also has performance issues when you want to build a long string by parts - many allocations and copyings of memory (to concatenate two strings, you have to allocate a third in the length of the two together, then copy them).
So Microsoft created System.Text.StringBuilder. The idea is to create mutable string. The basic methods are Append() (which appends some data, often primitive types) and AppendFormat() (similar to string.Format()). Then you get a normal string by calling to ToString():
void Func(StringBuilder s)
{
s.Append("Hi everyone!");
}
var s = new StringBuilder();
s.Append("a StringBuilder.");
Func(s);
s.ToString(); // "a StringBuilder.Hi everyone!"
Is there a way to use the same string instance and add one or more
characters to this specific instance?
No, but you can do it by using StringBuilder. Pass instance of StringBuilder to a function and append any string to it, it will add string but will refer to same instance of StringBuilder class
public void AppendString(StringBuilder sb) {
sb.Append("hello");
}
This is because string type is mutable, whenever you assign new value to string it creates new string object in memory, but StringBuilder is immutable, it is reference type. StringBuilder modifies without creating new object.
You can try below code,
public static void Main()
{
StringBuilder sb = new StringBuilder("Default Text");
Console.WriteLine($"Before function call: {sb.ToString()}");
AppendString(sb); //Function call
Console.WriteLine($"After function call: {sb.ToString()}");
}
public static void AppendString(StringBuilder sb)
{
sb.Append(" Hello world");
Console.WriteLine($"Inside function: {sb.ToString()}");
}
Output:
Before function call: Default Text
Inside function: Default Text Hello world
After function call: Default Text Hello world
.Net fiddle
I would suggest you to read string vs StringBuilder
You can achieve this by passing string parameter as reference. Please refer to the following code snippet.
static void Main(string[] args)
{
string input = "input";
AddString(ref input);
System.Console.WriteLine(input);
}
private static void AddString(ref string input)
{
input += "_edited";
}
You will need to use ref keyword in both the cases while defining and passing method parameter. Hope it helps.
Hi I am new to programming. I would like to read a text file and take the values ( strings ) and store each character of the string in an array individually. I have used a list to take in the vales from the text file. I am finding it difficult to move them into an array and then use those values in my program. Please find me a solution if possible. Thanking you in advance.
public class file_IO
{
string[] letters = new string[] //I would like to store it in this variable
public void File_Reader()
{
string filepath = #"env.txt"; //Variable to hold string
List<string> file_lines = File.ReadAllLines(filepath).ToList();//returns array of strings into List
foreach (string line in file_lines)
{
}
}
}
Hope this will work for you!.
public char[] File_Reader()
{
string filepath = #"env.txt"; //Variable to hold string
StreamReader sr = new StreamReader(filepath);
string fileContentInString = sr.ReadToEnd();
sr.Close();
return fileContentInString.ToCharArray();
}
List<List<char>> linesAsChars = File.ReadAllLines(filepath)
.Select(l => l.ToList())
.ToList();
This will get a List of List of chars.
string implements IEnumerable<char>, so with ToList each line in the file is translated to List<char>.
Solution to "store each character of the string in an array individually" is fairly easy because string is in fact an array of char. You can do this using something like this :
char[] letters;
public void File_Reader()
{
string filepath = #"env.txt";
letters = File.ReadAllText(filePath).ToArray();
}
I'm not really sure if I have understood your question properly, but from what I have read, I will assume that you want an array of lines (which are strings).
In this case, you don't need to do much as the File.ReadAllLines() method naturally outputs an array of string variables.
Remove the for loop and replace
List<string> file_lines = File.ReadAllLines(filepath).ToList();//returns array of strings into List
with:
letters = File.ReadAllLines(filepath)
In case what you want is actually an array of every char value in your file, I would refer to #m.rogalski's answer and declare an array of char[], for example, declare:
char[] fileChars;
and then replace the line I mentioned earlier with:
fileChars = readAllText(filePath).toCharArray()
You will notice that you do not need a loop in either of the above situations. Hope I helped.
Im still learning in C#, and there is one thing i cant really seem to find the answer to.
If i have a string that looks like this "abcdefg012345", and i want to make it look like "ab-cde-fg-012345"
i tought of something like this:
string S1 = "abcdefg012345";
string S2 = S1.Insert(2, "-");
string S3 = S2.Insert(6, "-");
string S4 = S3.Insert.....
...
..
Now i was looking if it would be possible to get this al into 1 line somehow, without having to make all those strings.
I assume this would be possible somehow ?
Whether or not you can make this a one-liner (you can), it will always cause multiple strings to be created, due to the immutability of the String in .NET
If you want to do this somewhat efficiently, without creating multiple strings, you could use a StringBuilder. An extension method could also be useful to make it easier to use.
public static class StringExtensions
{
public static string MultiInsert(this string str, string insertChar, params int[] positions)
{
StringBuilder sb = new StringBuilder(str.Length + (positions.Length*insertChar.Length));
var posLookup = new HashSet<int>(positions);
for(int i=0;i<str.Length;i++)
{
sb.Append(str[i]);
if(posLookup.Contains(i))
sb.Append(insertChar);
}
return sb.ToString();
}
}
Note that this example initialises StringBuilder to the correct length up-front, therefore avoiding the need to grow the StringBuilder.
Usage: "abcdefg012345".MultiInsert("-",2,5); // yields "abc-def-g012345"
Live example: http://rextester.com/EZPQ89741
string S1 = "abcdefg012345".Insert(2, "-").Insert(6, "-")..... ;
If the positions for the inserted strings are constant you could consider using string.Format() method. For example:
string strTarget = String.Format("abc{0}def{0}g012345","-");
string s = "abcdefg012345";
foreach (var index in [2, 6, ...]
{
s = s.Insert(index, "-");
}
I like this
StringBuilder sb = new StringBuilder("abcdefg012345");
sb.Insert(6, '-').Insert(2, '-').ToString();
String s1 = "abcdefg012345";
String seperator = "-";
s1 = s1.Insert(2, seperator).Insert(6, seperator).Insert(9, seperator);
Chaining them like that keeps your line count down. This works because the Insert method returns the string value of s1 with the parameters supplied, then the Insert function is being called on that returned string and so on.
Also it's worth noting that String is a special immutable class so each time you set a value to it, it is being recreated. Also worth noting that String is a special type that allows you to set it to a new instance with calling the constructor on it, the first line above will be under the hood calling the constructor with the text in the speech marks.
Just for the sake of completion and to show the use of the lesser known Aggregate function, here's another one-liner:
string result = new[] { 2, 5, 8, 15 }.Aggregate("abcdefg012345", (s, i) => s.Insert(i, "-"));
result is ab-cd-ef-g01234-5. I wouldn't recommend this variant, though. It's way too hard to grasp on first sight.
Edit: this solution is not valid, anyway, as the "-" will be inserted at the index of the already modified string, not at the positions wrt to the original string. But then again, most of the answers here suffer from the same problem.
You should use a StringBuilder in this case as Strings objects are immutable and your code would essentially create a completely new string for each one of those operations.
http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.71).aspx
Some more information available here:
http://www.dotnetperls.com/stringbuilder
Example:
namespace ConsoleApplication10
{
class Program
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder("abcdefg012345");
sb.Insert(2, '-');
sb.Insert(6, '-');
Console.WriteLine(sb);
Console.Read();
}
}
}
If you really want it on a single line you could simply do something like this:
StringBuilder sb = new StringBuilder("abcdefg012345").Insert(2, '-').Insert(6, '-');