Is there any way to append StringBuilder horizontally in C#? - c#

I am trying to append two StringBuilders so that they produce something like:
Device # 1 2 3
Pt.Name ABC DEF GHI
what I have tried is:
class Program
{
class Device
{
public int ID { get; set; }
public string PatName { get; set; }
}
static void Main(string[] args)
{
var datas = new List<Device>
{
new Device { ID = 1, PatName = "ABC" },
new Device { ID = 2, PatName = "DEF" },
new Device { ID = 3, PatName = "GHO" }
};
// there is a collection which has all this information
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0} {1}", "Device #", "Pt.Name").AppendLine();
foreach (var data in datas)
{
var deviceId = data.ID;
var patName = data.PatName;
sb.AppendFormat("{0} {1}", deviceId, patName).AppendLine();
}
Console.WriteLine(sb);
}
}
but it is printing it in vertical manner, like
Device # Pt.Name
1 ABC
2 DEF
3 GHI
and if I remove that last AppendLine(); it is appending it at the end in the same line.
I want to use only one stringbuilder followed by only one foreach loop.

1.You could do it like:
StringBuilder sb=new StringBuilder();
sb.Append("Device #");
foreach(var data in datas)
sb.Append($" {data.deviceId}");
sb.Append("PT.Name");
foreach(var data in datas)
sb.Append($" {data.PatName}");
2.if you want to loop only once then you can use 2 StringBuilders:
StringBuilder sb1=new StringBuilder();
StringBuilder sb2=new StringBuilder();
sb1.Append("Device #");
sb2.Append("Pt.Name");
foreach(var data in datas)
{
sb1.Append($" {data.deviceId}");
sb2.Append($" {data.patName}");
}
sb1.Append(sb2.ToString());
3.You could also use string.Join() which also relies on StringBuilder to write a one-liner but however this way you have extra select statements:
string result = $"Device # {string.Join(" ",datas.Select(x => x.deviceId))}\r\nPt.Name {string.Join(" ",datas.Select(x => x.patName))}";

I love your question because it is based on avoiding these two assumption, 1) that strings are always printed left to right and 2) newlines always result in advancing the point of printing downwards.[1]
Others have given answers that will probably meet your needs, but I wanted to write about why your way of thinking won’t work. The assumptions above are so engrained into people’s thinking about how strings and terminals work that I'm sure many people taught your question was odd or even naïve, I did at first.
StringBuilder doesn’t print strings to the screen. Somewhere I suspect you are calling Console.Write to print the string. StringBuilder allows you to convert non-string variables as strings and to concatenate strings together in a more efficient way than String.Format and the + operator, see Immutability and the StringBuilder class.
When you are done using StringBuilder what you have is a string of characters. It’s called a string because it is a 1D structure, one character after each other. There is nothing special about the new line characters in the string,[2] they are just characters in the list. There is nothing in the string that specifies the position the characters other that that each one comes after the previous one. When you do something like Console.Write the position of the character on the screen is defined by the implementation of that method, or the implements of the terminal, or both. They follow the conventions of our language, i.e. each character is to the right of the previous one. When Console.Write you encounters a newline it then prints the following character in the first position of the line below the current one.
If you are using String, StringBuilder and Console you can write code to create a single string with the pieces of test in the places you want so that when Console.Write follows the left to write, top to bottom conventions your text will appear correctly. This is what the other answers here do.
Alternately you could find a library which gives you more control over when text is printed on the screen. These were very popular before Graphical User Interfaces when people build interactive applications in text terminals. Two examples I remember are CRT for Pascal and Ncurses for C. If you want to investigate this approach I’d suggest doing some web searches or even another question here. Most terminal applications you see at banks, hospitals and airlines use such a library running on a VAX.
[1] This may be differently in systems setup for languages which are not like English, or not like Latin.
[2] The character or characters which reprsent a new line are different on different operating systems.

normally you cannot horizontally append to the right side of stringbuilder so maybe you roll your own extension method such as
static class SbExtensions
{
public static StringBuilder AppendRight(this StringBuilder sb, string key, string value)
{
string output = sb.ToString();
string[] splitted = output.Split("\n");
splitted[0] += key.PadRight(10, ' ');
splitted[1] += value.PadRight(10, ' ');
output = string.Join('\n', splitted);
return new StringBuilder(output);
}
}
simple solution:
StringBuilder sb2 = new StringBuilder(columns);
foreach(var data in datas)
{
sb2 = sb.AppendRight(data.ID.ToString(), data.PatName);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
complex one: dynamic
just another solution using MathNet.Numerics library at https://numerics.mathdotnet.com/
introduce an array property in your Entity class
class Device
{
public int ID { get; set; }
public string PatName { get; set; }
public string[] Array
{
get
{
return new string[] { ID.ToString(), PatName };
}
}
}
then in main method
static void Main(string[] args)
{
var datas = new List<Device>
{
new Device { ID = 1, PatName = "ABC" },
new Device { ID = 2, PatName = "DEF" },
new Device { ID = 3, PatName = "GHO" }
};
var MatrixValues = datas
.SelectMany(x => x.Array)
.Select((item, index) => new KeyValuePair<double, string>(Convert.ToDouble(index), item)).ToArray();
var matrixIndexes = MatrixValues.Select(x => x.Key);
var M = Matrix<double>.Build;
var C = M.Dense(datas.Count, datas.First().Array.Count(), matrixIndexes.ToArray());
var TR = C.Transpose();
string columns = "Device #".PadRight(10, ' ') + "\n" + "Pt.Name".PadRight(10, ' ');
StringBuilder sb = new StringBuilder(columns);
for (int i = 0; i < TR.Storage.Enumerate().Count(); i += 2)
{
sb = sb.AppendRight(MatrixValues[i].Value, MatrixValues[i + 1].Value);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
}
yea and those references
using MathNet.Numerics.LinearAlgebra;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Output
PS: this may not be your desired solution as it is creating multiple string builders when you append new data

This should get you going:
Code
StringBuilder deviceSB = new StringBuilder();
StringBuilder patNameSB = new StringBuilder();
deviceSB.Append("Device #".PadRight(9));
patNameSB.Append("Pt.Name".PadRight(9));
foreach (var data in datas)
{
deviceSB.Append($"{data.Device}".PadLeft(2).PadRight(4));
patNameSB.Append($"{data.PatName} ");
}
deviceSB.AppendLine();
deviceSB.Append(patNameSB);
Or optional without loop
StringBuilder result = new StringBuilder();
StringBuilder s1 = new StringBuilder("Device # ".PadRight(9));
StringBuilder s2 = new StringBuilder("Pt.Name ".PadRight(9));
s1 = s1.AppendJoin(String.Empty, datas.Select(x => x.Device.PadLeft(2).PadRight(4)));
s2 = s2.AppendJoin(' ', datas.Select(x => x.PatName));
result = result.Append(s1).AppendLine().Append(s2);
Note that i took the idea of the second option from #AshkanMobayenKhiabani, but instead of using strings i stick to StringBuilder since it is much more performant than using strings!
Output
Both of previous options offer the same output:

Related

How to convert greek characters to HTML characters

I would like to be able to do this kind of operation:
var specialCharactersString = "αβ";
var encodedString = WebUtility.HtmlEncode(specialCharactersString);
Console.WriteLine(encodedString); // result: αβ
We work with an external database that stores data using both notations αβ and αβ. We want to be able to query both terms when the end-user use αβ.
So far, I tried:
WebUtility.HtmlEncode
HttpUtility.HtmlEncode
Encoding.GetEncoding(1253)
Thanks to #claudiom248, the answer was in another Stack Overflow post.
How to convert currency symbol to corresponding HTML entity
https://github.com/degant/web-utility-wrapper/blob/master/WebUtilityWrapper.cs
unicode characters and html has been a problem all the time. Here is a helper I use. Hope this helps.
Update: The source is from https://www.codeproject.com/Articles/20255/Full-HTML-Character-Encoding-in-C with very minor modification.
specialCharactersString.HtmlEncode()
public static class TextHelpers {
public static string HtmlEncode(this string text)
{
var chars = System.Web.HttpUtility.HtmlEncode(text).ToCharArray();
System.Text.StringBuilder result = new System.Text.StringBuilder(text.Length + (int)(text.Length * 0.1));
foreach (char c in chars)
{
int ansiValue = Convert.ToInt32(c);
if (ansiValue > 127)
result.AppendFormat("&#{0};", ansiValue);
else
result.Append(c);
}
return result.ToString();
}
}
As mentioned by claudiom248, the .NET Framework libraries cannot properly map high ASCII html entity characters. You can certainly pull in a 3rd party library, but if you'd like to avoid the additional cost and/or if you only have a small subset of characters that you always want to handle, you can maintain a simple dictionary lookup.
void Main()
{
var specialCharactersString = "αβ";
var sb = new StringBuilder();
foreach (var specialChar in specialCharactersString)
{
var valueExists = _dict.TryGetValue(specialChar, out var mappedSpecialChar);
if (valueExists)
{
sb.Append(mappedSpecialChar);
}
}
Console.WriteLine(sb.ToString());
}
private Dictionary<char, string> _dict = new Dictionary<char, string>
{
{ 'α', "α" },
{ 'β', "β" }
};
This will output αβ as expected.

c# Working with Strings

I'm Learning c# and i am making some exercises. i was asked to make a program that make an array of strings and remove the vowels form it's words
i did this code to remove the vowel "S" but it didn't work. can someone help me with that ?
string[] musicinst = new string[4] { "cello", "guitar", "violin", "double bass" };
foreach (string s in musicinst)
{
if (s.Contains("s")) { s.Replace("s", ""); }
Console.WriteLine(s);
}
now this code outputs the words exactly as i typed them in the array with no changes. so what is the problem here ?
.Replace does not change the string but returns a new string with the change. You need to now assign it back to s:
if (s.Contains("s"))
{
s = s.Replace("s", "o");
}
This will now also not work:
Cannot assign to 's' because it is a 'foreach iteration variable'
So instead use a for loop and access by indexer or create a new list and add the result of s.Replace to it:
string[] musicinst = new string[4] { "cello", "guitar", "violin", "double bass" };
var newData = musicinst.Select(item => item.Replace("s", "o")).ToArray();
If you need to deal with replacement when insensitive then look at:
Is there an alternative to string.Replace that is case-insensitive?
You're running into a feature of C# strings called immutability - operations on strings do not change the string, it returns a new string. given this, you might think you need to do this:
s = s.Replace("s", "o");
But that won't work because 's' is a foreach iterator. Your best bet is to recast your loop:
for (int i = 0; i < musicinst.Length; ++i)
{
if (musicinst[i].Contains("s"))
{
musicinst[i] = musicinst.Replace("s", "o");
}
}
Which will change your array in-place. To preserve immutability of the array as well you might consider a LINQ-like option that builds a new array as others have demonstrated.
static void Main(string[] args)
{
string[] musicinst = new string[4] { "cello", "guitar", "violin", "double bass" };
char[] vowels = new char[5] { 'a', 'e', 'i' ,'o', 'u' };
List<string> output = new List<string>();
foreach (string s in musicinst)
{
string s1 = s;
foreach (var v in vowels)
{
if (s1.Contains(v))
{
s1=s1.Remove(s1.IndexOf(v),1);
}
}
output.Add(s1);
}
Console.ReadLine();
}
Whenever you perform any operation on a string data type, it creates a new string which you have to store in a new variable.

How to read different types of data from text file?

I need to read text data from file, where there are different types of data in every line.
So, I created a one big class named subject. My data looks something like this:
Subject name M1 M2 M3 M4
Subject1 5 7 8 3
Old Subject 1 2 5 9
The main question is, if it is possible to read all the data in line 1 for instance and assign it to proper fields, like SubjName = Subject1, M1 = 5, M2 = 7, M3 = 8 and so on, WITHOUT using substrings? (something like stream >> Subject.SubjName; stream >> Subject.M1 = 5 and so on in C++).
Here's the code that I have.
internal void Read()
{
TextReader tr = new StreamReader("Data.txt");
string line;
while ((line = tr.ReadLine()) != null) //read till end of line
{
tr.ReadLine(); //Skips the first line
}
Thanks in advance
EDIT: To clarify, I'd prefer that fields are delimited.
Something like the solution in this question might help, but obviously use a tab delimeter (\t)
CSV to object model mapping
from line in File.ReadAllLines(fileName).Skip(1)
let columns = line.Split(',')
select new
{
Plant = columns[0],
Material = int.Parse(columns[1]),
Density = float.Parse(columns[2]),
StorageLocation = int.Parse(columns[3])
}
It is not clear from your question how the records are stored in the file - whether fields are delimited or fixed length.
Regardless - you can use the TextFieldParser class, which:
Provides methods and properties for parsing structured text files.
It lives in the Microsoft.VisualBasic.FileIO namespace in the Microsoft.VisualBasic.dll assembly.
Split and Dictionary and your two methods of choice here. You read in your line, split it by empty spaces, then save it as a name/object pair in a dictionary.
Put the code below into a *.cs file, then build and run it as a demo:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
namespace stringsAsObjects
{
class stringObject
{
public static int Main(string[] args)
{
int counter = 0;
string line;
// Read the file and display it line by line.
System.IO.StreamReader file =
new System.IO.StreamReader("Data.txt");
string nameLine = file.ReadLine();
string valueLine = file.ReadLine();
file.Close();
string[] varNames = nameLine.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
string[] varValues = valueLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, object> map = new Dictionary<string, object>();
for(int i = 0; i<varNames.Length; i++)
{
try
{
map[varNames[i]] = varValues[i];
}
catch (Exception ex)
{
map[varNames[i]] = null;
}
}
foreach (object de in map)
{
System.Console.WriteLine(de);
}
Console.ReadKey();
return 0;
}
}
}

String manuplation in C#

Is there any method that I can use that returns a fixed length array after spliting a string with some delimiter and fill the rest with a default string.
Eg.
string fullName = "Jhon Doe";
string[] names = fullName.SpecialSplit(some parameters); //This should always return string array of length 3 with the second elememnt set to empty if there is no middle name.
Next time specify the language you're asking about. We're no guessers.
In Java:
fullName.split(" ");
And anyway, no method will "return string array of length 3 with the second elememnt set to empty if there is no middle name". For the method, there are just two elements. You have to write that method yourself wrapping the standard split() method.
You should read over Jon Skeet's Writing the perfect question. It will be beneficial to you in the future when posting questions of StackOverflow.
There is no method in C# to do what you are asking, but you can easily write an extension method to do what I think you are asking.
here is a quick example:
public static class AbreviatorExtention
{
public static string[] GetInitials(this String str, char splitChar)
{
string[] initialArray = new string[3];
var nameArray = str.Split(new char[] { splitChar },
StringSplitOptions.RemoveEmptyEntries);
if (nameArray.Length == 2)
{
var charArrayFirstName = nameArray[0].ToCharArray();
var charArrayLastName = nameArray[1].ToCharArray();
initialArray[0] = charArrayFirstName[0].ToString().ToUpper();
initialArray[1] = string.Empty;
initialArray[2] = charArrayLastName[0].ToString().ToUpper();
}
else
{
for (int i = 0; i < nameArray.Length; i++)
{
initialArray[i] = (nameArray[i].ToCharArray())[1]
.ToString().ToUpper();
}
}
return initialArray;
}
}
class Program
{
static void Main(string[] args)
{
string FullName = "john doe";
//Extension method in use
string[] names = FullName.GetInitials(' ');
foreach (var item in names)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
Output:
J
D
I would set it up to split the string separate from the fixed array. If you still want a fixed array, then you set up the array to a size of three an populate. This is not the best method, however, as it has no meaning. Better, set up a person or user class and then populate, via rules, from the split string.

Removing duplicate substrings within a string in C#

How can I remove duplicate substrings within a string? so for instance if I have a string like smith:rodgers:someone:smith:white then how can I get a new string that has the extra smith removed like smith:rodgers:someone:white. Also I'd like to keep the colons even though they are duplicated.
many thanks
string input = "smith:rodgers:someone:smith:white";
string output = string.Join(":", input.Split(':').Distinct().ToArray());
Of course this code assumes that you're only looking for duplicate "field" values. That won't remove "smithsmith" in the following string:
"smith:rodgers:someone:smithsmith:white"
It would be possible to write an algorithm to do that, but quite difficult to make it efficient...
Something like this:
string withoutDuplicates = String.Join(":", myString.Split(':').Distinct().ToArray());
Assuming the format of that string:
var theString = "smith:rodgers:someone:smith:white";
var subStrings = theString.Split(new char[] { ':' });
var uniqueEntries = new List<string>();
foreach(var item in subStrings)
{
if (!uniqueEntries.Contains(item))
{
uniqueEntries.Add(item);
}
}
var uniquifiedStringBuilder = new StringBuilder();
foreach(var item in uniqueEntries)
{
uniquifiedStringBuilder.AppendFormat("{0}:", item);
}
var uniqueString = uniquifiedStringBuilder.ToString().Substring(0, uniquifiedStringBuilder.Length - 1);
Is rather long-winded but shows the process to get from one to the other.
not sure why you want to keep the duplicate colons. if you are expecting the output to be "smith:rodgers:someone::white" try this code:
public static string RemoveDuplicates(string input)
{
string output = string.Empty;
System.Collections.Specialized.StringCollection unique = new System.Collections.Specialized.StringCollection();
string[] parts = input.Split(':');
foreach (string part in parts)
{
output += ":";
if (!unique.Contains(part))
{
unique.Add(part);
output += part;
}
}
output = output.Substring(1);
return output;
}
ofcourse i've not checked for null input, but i'm sure u'll do it ;)

Categories