Why won't this c# string-wrapping algorithm work? [closed] - c#

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Alright, so, in my C# Cosmos operating system, I'm working on a system that lets me input a string, and a desired width, and wrap the string into lines based on the width.
It works sorta like this: Input string is "Hello beautiful world". Width is 6. The algorithm will iterate through the string, and if the char index is that of the width, and the current char is a Space, it'll take everything from the beginning of the string up until that point, add it to a List, and remove it from the string itself, and reset the char index to 0, and start over. It does this until the string is either empty or smaller than the width. If it's smaller than the width, it's added to the List, and the for loop is terminated. In laymen, our output string should come out like this:
Hello
beautiful
world.
This is my code.
public static List<string> split_string(int width, string text)
{
List<string> text_lines = new List<string>();
//Separate lines of text.
for (int i = 0; i < text.Length; i++)
{
if (text.Length <= width)
{
text_lines.Add(text);
i = text.Length + 5;
}
else
{
if (i >= width)
{
if (text[i] == ' ')
{
text_lines.Add(text.Substring(0, i + 1));
text = text.Remove(0, i + 1);
i = 0;
}
}
}
}
return text_lines;
}
The thing is, sometimes, if I end up having to deal with the string being smaller than the width, we get issues. It seems to skip that part of the string. Yikes!
For example, here's a piece of my OS that uses this. It's supposed to take a title and a message, and display it in a messagebox with an OK button.
public static void ShowMessagebox(string title, string text)
{
int splitWidth = 25;
if(text.Length < splitWidth)
{
splitWidth = text.Length;
}
if(title.Length > splitWidth)
{
splitWidth = title.Length;
}
var lines = new List<string>();
if(splitWidth > text.Length)
{
lines.Add(text);
}
else
{
lines = TUI.Utils.split_string(splitWidth, text);
}
foreach(var line in lines)
{
if(text.Contains(line))
{
text = text.Replace(line, "");
}
}
if(text.Length > 0)
{
lines.Add(text);
}
int h = lines.Count + 4;
int w = 0;
foreach(var line in lines)
{
if(line.Length + 4 > w)
{
w = line.Length + 4;
}
}
int x = (Console.WindowWidth - w) / 2;
int y = (Console.WindowHeight - h) / 2;
TUI.Utils.ClearArea(x, y, w, h, ConsoleColor.Green);
TUI.Utils.ClearArea(x, y, w, 1, ConsoleColor.White);
TUI.Utils.Write(x + 1, y, title, ConsoleColor.White, ConsoleColor.Black);
for(int i = 0; i < lines.Count - 1; i++)
{
TUI.Utils.Write(x + 2, (y + 2) + i, lines[i], ConsoleColor.Green, ConsoleColor.White);
}
int xw = x + w;
int yh = y + h;
TUI.Utils.Write(xw - 6, yh - 2, "<OK>", TUI.Utils.COL_BUTTON_SELECTED, TUI.Utils.COL_BUTTON_TEXT);
bool stuck = true;
while (stuck)
{
var kinf = Console.ReadKey();
if (kinf.Key == ConsoleKey.Enter)
{
stuck = false;
Console.Clear();
}
else
{
}
}
}
Pretty simple. Starts with a default width of 25 chars, and if the title is bigger, it sets it to title length. If text length is smaller than the width it sets the width to compensate. Then it makes the call to the splitter algorithm from above, found in 'TUI.Utils', and then does some stuff to print to the screen.
Here's a piece of my OS's "ConfigurationManager", an application that takes in user input and uses it to generate a config file. Very work-in-progress right now.
Curse.ShowMessagebox("Memphis can't run properly this system.", "Memphis needs at least one FAT partition on a Master Boot Record to be able to store it's configuration and other files on. Please use a partition utility like GParted to partition your hard drive properly.");
But have a look at what comes out onto my screen...
The messagebox coming out of the above method call
As you can see, not really the thing I want. It's missing some of the string!

You don't need to change text, as we can just store the offset of our original substring. The fewer string manipulations we do, the better.
public static List<string> split_string(int width, string text)
{
width = width - 1; //So we're not constantly comparing to width - 1
var returnSet = new List<string>();
var currLength = 0;
var oldOffset = 0;
for (var i = 0; i < text.Length; i++)
{
if (currLength >= width && text[i] == ' ')
{
returnSet.Add(text.Substring(oldOffset, i - oldOffset));
oldOffset = i + 1;
currLength = 0;
continue;
}
currLength++;
}
if (oldOffset < text.Length)
returnSet.Add(text.Substring(oldOffset));
return returnSet;
}
Testing:
split_string(25, "Memphis needs at least one FAT partition on a Master Boot Record to be able to store it's configuration and other files on. Please use a partition utility like GParted to partition your hard drive properly.");
Gives:
Memphis needs at least one
FAT partition on a Master
Boot Record to be able to
store it's configuration
and other files on. Please
use a partition utility like
GParted to partition your
hard drive properly.
split_string(6, "Hello beautiful world.")
Gives
Hello
beautiful
world.

Related

I need to get a string Array with single lines, which should be extraced from a big text with paragraphs

I got an idea that i wanted to make today but ran into the problem that i have a string variable with paragraph, but i need an Array of single lines. So I tried to do this with the String.Substring and the String.IndexOf functions but this only worked kinda because i dont exactly know how VisualStudio handels the Index of Paragraphs and how strings work with paragraphs because i just learned C# this year.
I tried it in Windows-Forms btw.
Can anyone tell me how index's with paragraphs work or especialy how to use them correctly.
This is the code i tried which only works for the 1st line and works kinda with the 2nd but not with any further
string input_raw;
string[] input = new string[100];
int index_zeile = 0;
int x = 0, y = 0;
input_raw = input_text_box.Text;
for (int i = 0; i < 100; i++)
{
if (i == 0)
{
y = input_raw.IndexOf(";");
input[i] = input_raw.Substring(index_zeile, y);
x = y + 1;
}
else
{
index_zeile = input_raw.IndexOf(";", x);
input[i] = input_raw.Substring(x, index_zeile-3);
x = x + index_zeile;
}
}
I wish you entered your text input, but you can split the string into an array. The following code assigns a multi-line string to an array.
string[] lines = input_text_box.Text.Split('\n');

Am I using boolean values in the wrong place? Console output does not appear

I have a 4-line poem. I read this poem line by line from the text, flip the sentence over and assign it to the array. The purpose of my inversion is to find the rhymes at the end of the poem. In my code I try to find the word ryhme. For example - Lifting her arms to soap hair
"She wrings the water from her hair. Here is the word rhyme hair. I put the hair in the array by letter by letter. But when I want to show it on the console, the output does not appear. Where do I go wrong?" //
string[] ss2 = new string[reversed.Length-1];
bool flagword = false;
int sayac = 0;
for (i = 0; i < linenum - 1; i++) //word
{
int k = 0;
while (reversed[i].Substring(k, 1) == reversed[i + 1].Substring(k, 1))
{
ss2[i] += reversed[i].Substring(k, 1);
k++;
flagword = true;
}
if (reversed[i].Substring(k, 1) == " ") sayac++;
}
if (flagword && sayac != 0)
{
Console.WriteLine(ss2[1]);
}

Minesweeper(simple) in c# [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
so I'm working on some small thing and I'm kind of lost. This is what I have so far.
static void izris()
{
Random bombe = new Random();
int[,] polje = new int[7, 7];
string stolpci = " ABCDEFG";
int vrstice=1;
foreach (char c in stolpci)
{
Console.Write(c);
Console.Write(" ");
}
Console.WriteLine();
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7; j++)
{
polje[i, j] = bombe.Next(0, 2);
}
}
//izris polja
for(int i=0;i<7;i++)
{
Console.Write(vrstice);
vrstice++;
Console.Write(" ");
for (int j = 0; j < 7; j++)
{
Console.Write(polje[i,j]);
Console.Write(" ");
}
Console.WriteLine();
}
}
So what this does is, it prints a field of 7x7 that contains random numbers from 0 to 1. Now 1 means it is a mine, and 0 means it's not a mine. Now if you look, there are characters marking the columns(ABCDEFG) and numbers from 1-7 marking the rows. Now what I want to do is, for example the user would enter a string and the string would be B4. Then the program would check that and see if the field that the user selected is a mine(0) or a bomb(1). If it is a bomb it would also say how many bombs(1's) are nearby. I have no idea how to do this.
I was thinking of transforming the A-F characters into 0,6 numbers so I could index easier, but I don't know.
So I made this to check if the input is correct.
static void vnospolja()
{
string lokacija;
string nekaj = "A";
int nekaj2 = nekaj[0] - 'A';
lokacija = Convert.ToString(Console.ReadLine());
while (lokacija.Length < 2)
{
string pravilnalokacija=lokacija.Substring(0,1);
int pravilnalokacijaint = pravilnalokacija[0];
if(pravilnalokacijaint>65 && pravilnalokacijaint<72)
{
Console.Write("input ok");
}
string drugiznak = lokacija.Substring(1, 1);
int drugiznakint = drugiznak[0];
if(drugiznakint>0 && drugiznakint<8)
{
Console.Write("input ok");
}
}
}
Now I need some ideas on how I would do this, for instance user enters A4, so the input is correct. So how do I actually move accros my 7,7 field and check for A4? I don't understand this, my friend told me something about a second indexer in my multidimensional field but I have no clue.
to just convert the users input into 0 based coordinates you could use this.
static int[] GetInput()
{
string input = Console.ReadLine();
//validate input here..
int x = input[0] - 'A'; //converts char to int
int y = int.Parse(input.Substring(1)) - 1;
return new int[]{ x, y };
}
You could use this method by doing...
var selected = GetInput();
if(polje[selected[0]][selected[1]] == 1)
{
//its a bomb
}
What about using a Dictionary instead of using an Array?
Dictionary<string, bool> p = new Dictionary<string, bool>();
if (p.ContainsKey("a1"))
{
//do something
}
else { } // not a bomb/mine
So you can easily check wether the field is a bomb.
If you want to translate a user input, such as B4 into row and column, here is a simple way you could do it. Since you already have a collection of column names stolpci, you can use string.IndexOf, which will find the position of a character in a string. For example, B is found in place 2 (using 0-based indexing)
01234567
" ABCDEFG"
Since you would want B to map to column number 1, rather than 2 in the minesweeper array, you would need to subtract one.
As far as the number part goes, you can use int.Parse to read the number. Once again, the number is going to be off by one, because row number 4 actually corresponds to row number 3 in the 0-based indexing for the minesweeper array. So your final result might look something like this:
var input = "B4"; // this would come from the user, hard-coding for example
var column = stolpci.IndexOf(entry.Substring(0, 1)); // 2
var row = int.Parse(entry.Substring(1, 1)); // 4
var isBomb = polje[row - 1, col - 1] == 1; // offset by 1 for 0-based indexing
// do something with this

Taking certain string characters and returning the string

I asked this question yesterday but it wasn't well received mainly due to how I asked it so ill try do better this time.
I have a string variable called message. lets say message equals "ABCDABCDABCDABCD"
now I need to do some processing on the characters in the string but not all at the same time, I want to access characters [0][4][8][12] on the first pass of the function, put each of these characters in a string and return it which is easy done if I pass an integer to my function lets say 4 and with in a for loop do
if(i % int == 0)
{
string += message[i];
}
this should return "AAAA"
the next time I call the function ill need elements [0][1], [4][5], [8][9], [12][13] and the time after that ill need [0][1][2], [4][5][6], [8][9][10], [12][13][14].
I need the characters returned in a string in the order they were taken, I could do this by changing my int I pass the function but then id need to call the function several times and do work on the returned strings to get them into the order they were taken, which I have already tried and it slowed my program down when dealing with large messages > 10k characters.
Please don't delete or put my question on hold, im quite happy to give more information on my problem if its not clear, ill seldom post to this site and usually try and find a solution myself, there are too many acceptance junkies on here for my liking. but I would appreciate some help from some of them regarding this.Thanks
Edit
I understand its not easy to figure it out and I have to say im not the best at describing it, its a vigenere cracker in WPF, I have done the kasiski examination on a piece of text and graphed out all the data, it finds the key length 90% of the time or gives me the best clue to what the key might be, now im calculating the frequency of bi,tri and quad grams of the message based on the data from the kasiski exam, lets say the key is 5 and the message is "ABCDABCDABCDABCD" im calculating probability on only the characters of the key Im changing so when I try key AAAAA im only wanting to calculate monograms on elements [0][4][9][14] of the message, ill run through 26 characters up to ZAAAA and take the most probable then I move onto element [1] of the key, lets say FAAAA gave the best score on the first element of the key. now I need elements [0][1],[5][6],[9][10][13][14] as im calculating probability on 2 pieces on the key FCAAA, so the length of the key and what key character im working on will determine what elements of the message ill be taking.
One-liner with LINQ (I use Batch extension from MoreLINQ, but you can use your own) which selects all required chars from input string:
string message = "ABCDABCDABCDABCD";
int size = 4;
int charsToTake = 2;
var characters = message.Batch(size).SelectMany(b => b.Take(charsToTake));
If you need result as string, you can easily create one:
var result = new String(characters.ToArray());
// ABABABAB
More efficient way - create your own method which will split string by substrings of required length:
public static IEnumerable<string> ToSubstrings(this string s, int length)
{
int index = 0;
while (index + length < s.Length)
{
yield return s.Substring(index, length);
index += length;
}
if (index < s.Length)
yield return s.Substring(index);
}
I would also create method for safe getting substring from start of string (to avoid annoying string length check and passing zero as start index):
public static string SubstringFromStart(this string s, int length)
{
return s.Substring(0, Math.Min(s.Length, length));
}
Now its very clear what you are doing:
var substrings = message.ToSubstrings(size)
.Select(s => s.SubstringFromStart(charsToTake));
var result = String.Concat(substrings);
Here is a simple program which does what you want, if I understand correctly:
static void Main(string[] args)
{
string data = "ABCDABCDABCDABCD";
Console.WriteLine(StrangeSubstring(data,4, 1));
// "AAAA"
Console.WriteLine(StrangeSubstring(data,4, 2));
// "ABABABAB"
Console.WriteLine(StrangeSubstring(data,4, 3));
// "ABCABCABCABC"
}
static string StrangeSubstring(string input, int modulo, int length)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.Length; ++i)
{
if (i % modulo == 0)
{
for (int j = 0; j<length; ++j)
{
if (i+j < input.Length)
sb.Append(input[i+j]);
}
}
}
return sb.ToString();
}
My solution will be like this
static string MethodName(int range){
StringBuilder sb = new StringBuilder();
for(int i = 0 ; i < str.Length ; i++){
if(i % 4 == 0){
sb.Append(str[i]);
for(int j = i + 1 ; j <= i + range ; j ++){
if(j >= str.Length)
break;
sb.Append(str[j]);
}
}
}
return sb.ToString();
}
you can parse your string to a char array :
string message="ABCDABCDABCDABCD";
char[] myCharArray = message.ToCharArray();
string result="";
for(int i=0, i<myCharArray.Length -1 ; i++)
{
if(i%4 ==0)
result+=myCharArray[i];
}
EDIT 1 :
public string[] myfunction(char[] charArray)
{
List<string> result = new List<string>();
for(int i=0, i<charArray.length -1; i=i+4)
{
result.add(charArray[i]+charArray[i+1])
}
return result.toArray();
}
This is a recursive solution. In YourFunction, PatternLength is the length of the character pattern which is repeated (so, 4 for "ABCD"), Offset is where you start in the pattern (e.g. 0 if you start with "A") and SubstringLength is the number of characters.
The function call in Main will give you all "A". If you change SubstringLength to 2, it gives you all "AB". There is no error handling, make sure then PatternLength<=Offest+SubstringLength
namespace Foo
{
class Bar
{
static void Main(string[] args)
{
Console.WriteLine(YourFunction("ABCABCABCABCABCABCABC", 3, 0,1));
Console.ReadKey();
}
static string YourFunction(string SubString, int PatternLength, int Offset, int SubstringLength)
{
string result;
if (SubString.Length <= PatternLength)
{
result = SubString.Substring(Offset, SubstringLength);
}
else
{
result = YourFunction(SubString.Substring(PatternLength, (SubString.Length - PatternLength)), PatternLength, Offset, SubstringLength) + SubString.Substring(Offset, SubstringLength);
}
return result;
}
}
}

Break string into separate lines: a showdown

Accommodating legacy database tables that were designed specifically for the IBM mainframe screens they represent has caused me much aggravation. Often, I find the need to break a string into multiple lines to fit the table column width as well as the users who are still viewing the data with terminal emulators. Here are two functions I've written to perform that task, accepting a string and a line width as parameters and returning some string enumerable. Which do you think is the better function and why? And by all means share the super-easy-fast-efficient way that I totally overlooked.
public string[] BreakStringIntoArray(string s, int lineWidth)
{
int lineCount = ((s.Length + lineWidth) - 1) / lineWidth;
string[] strArray = new string[lineCount];
for (int i = 0; i <= lineCount - 1; i++)
{
if (((i * lineWidth) + lineWidth) >= s.Length)
strArray[i] = s.Substring(i * lineWidth);
else
strArray[i] = s.Substring(i * lineWidth, lineWidth);
}
return strArray;
}
vs.
public List<string> BreakStringIntoList(string s, int lineWidth)
{
List<string> lines = new List<string>();
if (s.Length > lineWidth)
{
lines.Add(s.Substring(0, lineWidth));
lines.AddRange(this.BreakStringIntoList(s.Substring(lineWidth), lineWidth));
}
else
{
lines.Add(s);
}
return lines;
}
For example, passing in ("Hello world", 5) would return 3 strings:
"Hello"
" worl"
"d"
The first one is way better.
The second one will produce tons of temporary objects which is completely unnecessary if you can pre-determine the amount of target lines.
And for having something overlooked: Depending on the speed requirements you could likely use pointers to get a considerable speedup (depends on what you actually want to do with the result). But this will be WAY MORE complex than it is now.
Just to quantify the "tons of temporary objects". If you have a 1MB String (and worst case line-length of 1) the first approach needs 1MB of String-contents memory allocations. The second will need 500GB of String-contents memory allocations.
public List<string> BreakStringIntoLines(string s, int lineWidth)
{
string working = s;
List<string> result = new List<string>(Math.Ceil((double)s.Length / lineWidth));
while (working.Length > lineWidth)
{
result.add(working.Substring(0, lineWidth);
working = working.Substring(5);
}
result.Add(working);
return result;
}
That's probably how I would do it. A List is more flexible than a string array IMHO.
Also, I would avoid recursion like the plague. It is far easier to create and debug something that uses a simple loop.
Regex.Replace(input, ".{5}", x => x.Value + "\n").Split(new char [] {'\n'})
EDIT: sorry, i thought you want one string devided into lines ^__^"
depends on the length of the string.. but for long ones:
string BreakStringIntoLines(string s, int lineWidth)
{
StringBuilder sb = new StringBuilder(s);
for (int i = lineWidth; i < sb.Length; i += lineWidth)
{
sb.Insert(i, Environment.NewLine);
}
return sb.ToString();
}
I like the solution from Itay, but it does not produce the right output, so here's my edited version.
protected void Page_Load(object sender, EventArgs e)
{
string a = "12345678901234567890123456789012345";
TextBox1.Text = a;
TextBox2.Text = BreakStringIntoLinesVer2(a, 10);
}
string BreakStringIntoLinesVer2(string s, int lineWidth)
{
StringBuilder sb = new StringBuilder(s);
int last = (sb.Length % lineWidth == 0) ? sb.Length - lineWidth : sb.Length - (sb.Length % lineWidth);
for (int i = last; i > 0; i -= lineWidth)
{
sb.Insert(i, Environment.NewLine);
}
return sb.ToString();
}

Categories

Resources