I'm trying to make a Reddit Formatter tool for whenever you have a text with just one line break to add another and make a new paragraph. Here in StackOverflow it's the same, you have to press the enter key twice to start a new paragraph. It'd go from:
Roses are red
Violets are Blue
to
Roses are red
Violets are Blue
The code below works: it detects enter characters by checking every character from the text you've input in the textbox, starting from the end, and replaces them with a double one after clicking a button
private void button1_Click(object sender, EventArgs e)
{
for (int i = textBox1.Text.Length - 1; i >= 0; i--)
{
if (textBox1.Text[i] == '\u000A')
{
textBox1.Text = textBox1.Text.Insert(i, "\r\n\r\n");
}
}
}
It's great, but I don't want to add more than one enter character if it's already a double. I don't want to go from
Roses are red
Violets are Blue
to
Roses are red
Violets are Blue
because it's already working as the first example. It just adds more lines infinitely if you keep pressing the button.
I've tried with this:
private void button1_Click(object sender, EventArgs e)
{
for (int i = textBox1.Text.Length - 1; i >= 0; i--)
{
if (textBox1.Text[i] == '\u000A' && textBox1.Text[i - 1] != '\u000A')//if finds a SINGLE new line
{
textBox1.Text = textBox1.Text.Insert(i, "\r\n\r\n");
}
}
}
But it doesn't work? It's basically the same but also checks if the previous one is an enter character too
What am I doing wrong? I'm really confused because it should work... The output is exactly the same as the first code
Thank you in advance
Let's break the question down into 2 parts
Part #1: What am I doing wrong
Your code checks for 2 consecutive \n characters
if (textBox1.Text[i] == '\u000A' && textBox1.Text[i - 1] != '\u000A')
But you always end up finding the \r character at [i-1] when you find \n at [i]. In short your check only works to detect a single \n but never more than 1 consecutive EOLNs
Part #2: Best way to do this
RegularExpressions are the best way to handle such things. It not only makes the parsing part easy to read/write (if you know regex), but also retains flexibility when the pattern changes (again if you know regex)
The below line should do what you need
textBox1.Text = Regex.Replace(textBox1.Text, "(?:\r\n)+", "\r\n\r\n");
Let me explain the regex to you
(?:xxx) This is just a regular bracket (ignore the xxx, as that is just a placeholder) to group together things without capturing them
+ The plus sign after the bracket tells the engine to capture one or more instances of the item preceding it which in this case is `(?:\r\n)`
So as you would have realized, we are looking for one or more instances of \r\n and replacing it with just one instance of \r\n
Related
I'm making a spelling game for kids in Unity. I've got it so when the right letter is clicked, the letter is highlighted. I couldn't figure out how to highlight a specific char on the fly in a Text field, so I just put a highlighted version of the text behind the regular, and have the regular char disappear when clicked instead. Now, I need this text centered in the beginning, but the problem is it auto centers whenever a char disappears, misaligning the text.
Any ideas on how to stop getting it to auto center?
newWord = new StringBuilder (startWord.text);
i = 0;
if (Letter.gameObject.GetComponent<MoveLetter> ().checkIfClicked (startWord.text[i]))
{
newWord [i] = ' ';
startWord.text = newWord.ToString();
Destroy (Letter);
letters.Remove (Letter);
i++;
}
My answer will try to help you fix your first problem : highlighting characters.
I would advise you to not duplicate text to show the highlighted characters. Instead, use rich text
You can change the color of individual characters by surrounding them with the color tag, as follow :
<color=#ffff00ff>H</color><color=#ffff00ff>E</color>LP
This string in the text attribute of your Text component will make the H and E letters yellow, while the other letters will stay black (or whatever color you have chosen for the Text component)
Here is a function you can use to highlight specific letters of a given string :
using System;
// ....
public void Foo()
{
// Give the indices of the letters of the string, starting at 0
GetComponent<UnityEngine.UI.Text>().text = Highlight("HELP", "#ffff00ff", 3, 1, 2);
// Will output :
// H<color=#ffff00ff>E</color><color=#ffff00ff>L</color><color=#ffff00ff>P</color>
}
private string Highlight(string text, string color, params int[] indices)
{
Array.Sort(indices);
for (int i = indices.Length - 1 ; i >= 0; i--)
{
if( indices[i] == text.Length - 1 )
text = String.Format( "{0}<color={1}>{2}</color>", text.Substring(0, indices[i]), color, text[indices[i]] ) ;
else if( indices[i] == 0 )
text = String.Format( "<color={0}>{1}</color>{2}", color, text[indices[i]], text.Substring(1));
else if( indices[i] < text.Length )
text = String.Format( "{0}<color={1}>{2}</color>{3}", text.Substring(0, indices[i]), color, text[indices[i]], text.Substring(indices[i] + 1));
}
return text;
}
I'm not sure exactly how Shadow works (I'm a newbie), but maybe you could try to put a Shadow component on the right letter when clicked and that way it may be easier to make it work.
EDIT:
Here, you could do it like this. Dunno how efficient it is, but I'm sure you can control it from code somehow. Add 4 components and cast a shadow in each direction
http://imgur.com/gD4zs9N
#Hellium answer is the right one: using rich text is probably your best solution.
However there are two other possibilities I can think of right now:
keep your 2 text elements (one being the background and the other being the overlay) and use a monospaced font: this way the background could be "HELP" while the overlay would be " LP" (simply make sure both are the same size/alignment)
use TextMesh Pro which is now part of Unity :)
Hope this helps,
I would like to preface this by saying it is for an assessment, so I don't want you to directly give me the answer, I would like you to point me in the right direction, slightly tweak what I have done or just tell me what I should look into doing.
I am trying to create a Caesar Cipher to decipher a text document we have been given. It needs to print all the possible shifts in the console and output the final one to a text document. I'm thinking of trying to add frequency analysis to this later to find the correct one, but I have some problems now that I need to sort out before I can do that.
This is what I have made so far:
using System;
using System.IO;
class cipher
{
public static void Main(string[] args)
{
string output = "";
int shift;
bool userright = false;
string cipher = File.ReadAllText("decryptme.txt");
char[] decr = cipher.ToCharArray();
do {
Console.WriteLine("How many times would you like to shift? (Between 0 and 26)");
shift = Convert.ToInt32(Console.ReadLine());
if (shift > 26) {
Console.WriteLine("Over the limit");
userright = false;
}
if (shift < 0) {
Console.WriteLine("Under the limit");
userright = false;
}
if (shift <= 26 && shift >= 0)
{
userright = true;
}
} while (userright == false);
for (int i = 0; i < decr.Length; i++)
{
{
char character = decr[i];
character = (char)(character + shift);
if (character == '\'' || character == ' ')
continue;
if (character > 'Z')
character = (char)(character - 26);
else if (character < 'A')
character = (char)(character + 26);
output = output + character;
}
Console.WriteLine("\nShift {0} \n {1}", i + 1, output);
}
StreamWriter file = new StreamWriter("decryptedtext.txt");
file.WriteLine(output);
file.Close();
}
}
Right now it compiles and read the document but when it runs in the console it prints shift one as 1 letter from the encoded text, shift 2 as 2 letters from it, etc.
I have no idea what I have done wrong and any help would be greatly appreciated. I have also started thinking about ASCII values for letters but have no idea how to implement this.
Once again, please don't just give me the answer or I will not have learned anything from this - and I have been trying to crack this myself but had no luck.
Thanks.
Break the problem down into smaller bite-sized chunks. Start by printing a single shifted line, say with a shift of 1.
When you have that part working correctly (and only then) extend your code to print 26 lines with shifts of 0, 1, 2, 3, ... 26. I am not sure if your instructor wants either or both of shift 0 at the start and shift 26 at the end. You will need to ask.
Again, get that working correctly, and write new code to analyse one line only, and give it some sort of score. Get that working properly.
Now calculate the scores for all the lines and pick out the line with the best score. That should be the right answer. If it isn't then you will need to check your scoring method.
Writing small incremental changes to a very simple starting program is usually a lot easier than trying to go straight from a blank screen to the full, complex, program. Add the complexity gradually, one piece at a time, testing as you go.
I created an IF Statement for my XNA game which makes my characters leave their classroom(screen). There are 40 characters, in lines of 10. 'Dest' means destination of the character. This code is in my update method and I have a list of characters in my initialize method:
if (activeCharacter.DestX < 680)
{
activeCharacter.DestX = activeCharacter.DestX + 6;
}
else if (activeCharacter.DestY < 600) //600
{
activeCharacter.DestY = activeCharacter.DestY + 6;
}
else if (activeCharacter.DestY == 600)
{
activeCharacter = classroom[(Random.Next(classroom.Count))];
Console.WriteLine(activeCharacter.DestY);
The aim is to have random characters leave the classroom. When a character from the 1st row leaves, another random character will leave straight after and another depending on if it's from the first row. However if a character from any other row except the 1st leaves, only one character will go.
Can someone please tell me stop this from happening?
Thanks!
DestY is incremented by 6, until it is bigger then or equal to 600. However the final else if only checks for equal to 600.
Changing the final check to:
else if (activeCharacter.DestY >= 600)
Or simply:
else
Will most likely solve your problem.
I'm trying to create an "snippet" from a paragraph. I have a long paragraph of text with a word hilighted in the middle. I want to get the line containing the word before that line and the line after that line.
I have the following piece of information:
The text (in a string)
The lines are deliminated by a NEWLINE character \n
I have the index into the string of the text I want to hilight
A couple other criteria:
If my word falls on first line of the paragraph, it should show the 1st 3 lines
If my word falls on the last line of the paragraph, it should show the last 3 lines
Should show the entire paragraph in the degenative cases (the paragraph only has 1 or 2 lines)
Here's an example:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph
Example, if my index points to BIRD, it should show lines 1, 2, & 3 as one complete string like this:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
If my index points to DOG, it should show lines 3, 4, & 5 as one complete string like this:
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph
etc.
Anybody want to help tackle this?
In my opinion this is an excellent opportunity to use the StringReader class:
Read your text line by line.
Keep your lines in some kind of buffer (e.g., a Queue<string>), dropping lines you don't need after a given number of lines have been read.
Once your "needle" is found, read one more line (if possible) and then just return what's in your buffer.
In my opinion, this has some advantages over the other approaches suggested:
Since it doesn't utilize String.Split, it doesn't do more work than you need -- i.e., reading the entire string looking for the characters to split on, and creating an array of the substrings.
In fact, it doesn't necessarily read the entire string at all, since once it finds the text it's looking for it only goes as far as necessary to get the desired number of padding lines.
It could even be refactored (very easily) to be able to deal with any textual input via a TextReader -- e.g., a StreamReader -- so it could even work with huge files, without having to load the entire contents of a given file into memory.
Imagine this scenario: you want to find an excerpt of text from a text file that contains the entire text from a novel. (Not that this is your scenario -- I'm just speaking hypothetically.) Using String.Split would require that the entire text of the novel be split according to the delimiter you specified, whereas using a StringReader (well, in this case, a StreamReader) would only require reading until the desired text was found, at which point the excerpt would be returned.
Again, I realize this isn't necessarily your scenario -- just suggesting that this approach provides scalability as one of its strengths.
Here's a quick implementation:
// rearranged code to avoid horizontal scrolling
public static string FindSurroundingLines
(string haystack, string needle, int paddingLines) {
if (string.IsNullOrEmpty(haystack))
throw new ArgumentException("haystack");
else if (string.IsNullOrEmpty(needle))
throw new ArgumentException("needle");
else if (paddingLines < 0)
throw new ArgumentOutOfRangeException("paddingLines");
// buffer needs to accomodate paddingLines on each side
// plus line containing the needle itself, so:
// (paddingLines * 2) + 1
int bufferSize = (paddingLines * 2) + 1;
var buffer = new Queue<string>(/*capacity*/ bufferSize);
using (var reader = new StringReader(haystack)) {
bool needleFound = false;
while (!needleFound && reader.Peek() != -1) {
string line = reader.ReadLine();
if (buffer.Count == bufferSize)
buffer.Dequeue();
buffer.Enqueue(line);
needleFound = line.Contains(needle);
}
// at this point either the needle has been found,
// or we've reached the end of the text (haystack);
// all that's left to do is make sure the string returned
// includes the specified number of padding lines
// on either side
int endingLinesRead = 0;
while (
(reader.Peek() != -1 && endingLinesRead++ < paddingLines) ||
(buffer.Count < bufferSize)
) {
if (buffer.Count == bufferSize)
buffer.Dequeue();
buffer.Enqueue(reader.ReadLine());
}
var resultBuilder = new StringBuilder();
while (buffer.Count > 0)
resultBuilder.AppendLine(buffer.Dequeue());
return resultBuilder.ToString();
}
}
Some example input/output (with text containing your example input):
Code:
Console.WriteLine(FindSurroundingLines(text, "MOUSE", 1);
Output:
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
Code:
Console.WriteLine(FindSurroundingLines(text, "BIRD", 1);
Output:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
Code:
Console.WriteLine(FindSurroundingLines(text, "DOG", 0);
Output:
This is the 4th line of DOG text in the paragraph
Code:
Console.WriteLine(FindSurroundingLines(text, "This", 2);
Output:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph
Using the LINQ extension methods to get the right strings:
string[] lines = text.Split('\n');
// Find the right line to work with
int position = 0;
for (int i = 0; i < lines.Count(); i++)
if (lines[i].Contains(args[0]))
position = i - 1;
// Get in range if we had a match in the first line
if (position == -1)
position = 0;
// Adjust the line index so we have 3 lines to work with
if (position > lines.Count() - 3)
position = lines.Count() - 3;
string result = String.Join("\n", lines.Skip(position).Take(3).ToArray());
This can of course be optimized a bit by quitting the for loop as soon as the index has been found, and probably a number of other things. You can probably even LINQify so you never need to actually store that extra array, but I can't think of a good way to do that right now.
An alternative for the checks on position could be something like position = Math.Max(0,Math.Min(position, lines.Count() - 3)); - which would handle both of them at once.
There are a few ways one can handle this:
First Method:
Use String.IndexOf() and String.LastIndexOf().
You can find where the current selected word is by using TextBox.SelectionStart(). Then simply look for LastIndexOf from the selection location looking for the '\n' to find the previous line (don't grab the first lastindexof from the selection, once you find one...do it again from that location so you get the beginning of that line). Then do the same from the selection point only using IndexOf to find the '\n' to get the end of the line. Once again, don't use the first one you find, repeat it starting from the first found location to get the second line's end. Then simply substring the text with the area you found.
Second Method: Use String.Split() by the '\n' character (creates an array of strings, each one containing a different line from the text in order of array index). Find the index of the line the text is in, and then simply grab from the String[index] for the line before, including, and after. Hopefully this two methods are clear enough for you to figure out your coding. If you are still stuck, let me know.
Alright. Lemme have a crack,
I think the first thing I would do is split everything into arrays. Simply because then we have a simple way to "count" the lines.
string[] lines = fullstring.Split('\n');
Once we have that, Unfortunately I don't know of any indexof that goes through each point in an array. There probably is one, but without trawling through the internet, I would simply go
int i = -1;
string animal = 'bird';
foreach(string line in lines)
{
i++;
if(line.indexof(animal) > -1) break;
}
// we will need a if(i == -1) then we didn't find the animal etc
Ok so then, We now have the line. All we need to do, is...
if(i == 0)
{
writeln(lines[0);
writeln(lines[1]);
etc
}
else
if(i == lines.count - 1)
{
//this means last array index
}
else
{
//else we are in the middle. So just write out the i -1, i, i+1
}
I know that is messy as hell. But that's how I would solve the issue.
I'm trying to work my way through Ron Jeffries's Extreme Programming Adventures in C#. I am stuck, however, in Chapter 3 because the code does not, and cannot, do what the author says it does.
Basically, the text says that I should be able to write some text in a word-wrap enabled text box. If I then move the cursor to an intermediate line and hit enter, the code should re-display the lines before the cursor, add a couple of lines and a set of HTML paragraph tags, then append the rest of the lines. The code doesn't match the text because it uses the textbox.lines property. Well, no matter how many word-wrapped lines there are in a text box, there's only ONE line in the Lines property until you hit a carriage return. So, the statement that the code should, "Copy the rest of the lines into the buffer" appears wrong to me.
I'd appreciate anybody having experience with the book telling me what I'm reading, or doing, wrong!
Thanks.
EoRaptor
Try emailing Ron Jeffries directly. I have the book - somewhere, but I don't remember it not working. His email address is ronjeffries at acm dot org and put [Ron] in the subject line.
(And for those wondering - his email info was right from his website Welcome page)
I've also just started this book and had exactly the same problem although the code you have included looks further along than where I am.
The 'subscript out of range' occurred for 2 reasons, first as Ron explains he was just testing and so returned a hard-coded value of 3 before he wrote the CursorLine() function, which means you I think at least 4? lines of text which as you say need to be pasted in, or maybe set the text to this value before running, also as you say they need to have carriage returns to make txtbox.Lines return an array of strings.
The second reason occurs even after CursorLine() has been implemented but only happens if the text box is empty as txtbox.Lines returns string[0] but I think Ron is implementing a 'User Story' which says that when text has been entered and user presses enter, so not sure if he fixes this later, but will probably find out!
The author's do state that they are learning C# and will show the development wart's and all, which is one of the reasons I have chosen to study this book as I think it is encouraging me to develop projects. I also try to do the code first before looking at his solutions to see if I'm thinking the same way, but maybe I know C# a little better than I give myself credit for, or I'm completly crap, but I've noticed a few things, first he says that overriding OnKeyDown() doesn't work, but I think he must have got confused and tried to do in Form, instead of deriving from TextBox and overriding there.
This was my code when reading the 'User Story':
int curPos = txtbox.SelectionStart;
string Wrd = Environment.NewLine + "<P></P>" + Environment.NewLine;
txtbox.SelectedText = Wrd;
int pl = Environment.NewLine.Length + 3; // "<P>" length is 3
// Put text cursor inbetween <P> tags
txtbox.SelectionStart = curPos + pl;
It works differently to Ron's code, but was just my interpretation of the 'User Story' and not sure how should act if text is selected or wether to split line if text cursor in the middle etc.
Also in 'My Adventures' in Extreme Programming Adventures in C#
txtbox.GetLineFromCharIndex(txtbox.SelectionStart)
gets the cursor line position and doesn't matter if no carriage returns or resized,
as far as I can tell, I done little test with:
txtbox.GetLineFromCharIndex(txtbox.TextLength)
which returns the total amount of lines, which will vary if you resize the text box.
Using C# I always look for solutions which already exsist and people may slate me for this but I think MS have created a great language with great components which do what you expect them to do, so don't have to re-create the wheel each time.
Although like I say it's early days in this book and perhaps these simple solutions aren't extensible enough and maybe Ron's taking that into account, although he did mention just get it working then worry about that later is more the XP way.
Warren.
print("using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace NotepadOne {
public class TextModel {
private String[] lines;
private int selectionStart;
private int cursorPosition;
public TextModel() {
}
public String[] Lines {
get {
return lines;
}
set {
lines = value;
}
}
public int SelectionStart {
get {
return selectionStart;
}
set {
selectionStart = value;
}
}
public int CursorPosition {
get {
return cursorPosition;
}
set {
cursorPosition = value;
}
}
public void InsertControlPText() {
lines[lines.Length - 1] += "ControlP";
}
public void InsertParagraphTags() {
int cursorLine = CursorLine();
String[] newlines = new String[lines.Length + 2];
for (int i = 0; i <= cursorLine; i++) {
newlines[i] = lines[i];
}
newlines[cursorLine + 1] = "";
newlines[cursorLine + 2] = "<P></P>";
for (int i = cursorLine + 1; i < lines.Length; i++) {
newlines[i + 2] = lines[i];
}
lines = newlines;
selectionStart = NewSelectionStart(cursorLine + 2);
}
private int CursorLine() {
int length = 0;
int lineNr = 0;
foreach (String s in lines) {
if (length <= SelectionStart && SelectionStart <= length + s.Length + 2) {
break;
length += s.Length + Environment.NewLine.Length;
lineNr++;
}
lineNr++;
}
return lineNr;
}
private int NewSelectionStart(int cursorLine) {
int length = 0;
for (int i = 0; i < cursorLine; i++) {
length += lines[i].Length + Environment.NewLine.Length;
}
return length + 3;
}
}
}
");
The InsertParagraphTags method is called by pressing the enter key in the textbox.
BTW, the break here is that there is a subscript out of range error if you try to hit enter at the end of the text. I'm sure I could figure out how to get around this but then my code won't look like his code; which is what I'm trying to learn.
Randy