Finding and replacing characters in a string

Started by Ultra Magnus, Fri 06/03/2009 15:16:50

Previous topic - Next topic

Ultra Magnus

Greetings.

I have a name input box that capitalises the first letter, and it goes a little something like this...
Code: ags

function myNamePlayer()
{
 namestring=txtNameinput.Text;

 if (namestring=="") {
   SetTextWindowGUI (5);
   DisplayAtY(402, "Please enter a name before proceeding.");
   SetTextWindowGUI (16);
 }

 else {
   String namestringtemp=namestring.UpperCase();
   char initial=namestringtemp.Chars[0];
   namestringtemp=namestring.LowerCase();
   namestring=namestringtemp.ReplaceCharAt(0, initial);

   cStephanie.Say("It's nice to meet you, %s.", namestring);
 }
}


I'm looking for a way to also auto-detect if said string contains a space and/or hyphen, then capitalise the following letter(s).
Also, to see if the last character is some kind of punctuation (and/or maybe number), and delete it if so.

For example, if the player types their name as "joey jo-jo jr!!1!", I'd like the game to auto-parse that as "Joey Jo-Jo Jr".

I thank you in advance for any help.
I don't mean to sound bitter, cold, or cruel, but I am, so that's how it comes out.

I'm tired of pretending I'm not bitchin', a total frickin' rock star from Mars.

RickJ

#1
I think you are looking for the String.StartsWith, String.EndsWith and String.IndexOf functions.

http://www.adventuregamestudio.co.uk/manual/String.EndsWith.htm

[edit]
and of course String.Replace and String.Append.

Ultra Magnus

Thanks for the reply, but I've already read that particular manual page, and searched the forum, several times over.
It doesn't help.

I already use Sting.Contains in a couple of other places, and I know how to use String.Chars to return which character is in a certain position, but not how to return which position a certain character is in.
Or how to shift focus from that position to the following character.

And String.EndsWith is useless to me right now, as I don't know how to differentiate between characters.
Well, short of making an if statement for each letter in the alphabet and then an else to delete anything those statements don't cover.
And I assume there's an easier way than that.
I don't mean to sound bitter, cold, or cruel, but I am, so that's how it comes out.

I'm tired of pretending I'm not bitchin', a total frickin' rock star from Mars.

Pumaman

In pseudocode, something like:

Code: ags

String CapitaliseLetter(String namestring, int characterIndex)
{
  if (characterIndex < namestring.Length)
  { 
     String namestringtemp = namestring.UpperCase();
     char capitalCharacter = namestringtemp[characterIndex];
     namestring = namestring.ReplaceCharAt(characterIndex, capitalCharacter);
  }
  return namestring;
}


// main code:
namestring = CapitaliseLetter(namestring, 0);   // capitalise first letter
int i = 0;
while (i < namestring.Length)
{
  if (namestring.Chars[i] == ' ') namestring = CapitaliseLetter(namestring, i + 1);   // capitalise first letter after space
  i++;
}

Trent R

I wish there was a String.Split function. But you can't really do that without foreach or List<String>. For AGS as it is now, you'd have to store them into String[], which should be doable.


~Trent
To give back to the AGS community, I can get you free, full versions of commercial software. Recently, Paint Shop Pro X, and eXPert PDF Pro 6. Please PM me for details.


Current Project: The Wanderer
On Hold: Hero of the Rune

monkey0506

#5
As you said Trent, it would be "doable" to store it in a dynamic array, like this:

Code: ags
import String[] SplitByString(this String*, String otherString, bool include=true);

String[] SplitByString(this String*, String otherString, bool include) {
  String s[];
  int i = this.IndexOf(otherString);
  if ((String.IsNullOrEmpty(otherString)) || (i == -1)) {
    s = new String[2];
    s[0] = "1";
    s[1] = this;
    return s;
  }
  s = new String[this.Length + 1];
  String buffer = this;
  int lineCount = 0;
  while (i != -1) {
    lineCount++;
    if (include) i += otherString.Length;
    s[lineCount] = buffer.Substring(0, i);
    if (!include) i += otherString.Length;
    buffer = buffer.Substring(i, buffer.Length);
    i = buffer.IndexOf(otherString);
  }
  String t[] = new String[lineCount + 1];
  i = 0;
  while (i <= lineCount) {
    t[i] = s[i];
    i++;
  }
  t[0] = String.Format("%d", lineCount);
  return t;
}


I pad the array to allow storing the size since AGS doesn't currently have any way to get the size of a dynamic array. This allows us to return the figure with the rest of our data. We can now use it like this:

Code: ags
String text = "-Michael: I know you're not a gamer.-Sam: Oh, I know you didn't just say that.-Michael: Oh, but I did.-Sam: It's on now.";
String lines[] = text.SplitByString("-");
Display("line count: %d", lines[0].AsInt); // displays "line count: 4"
int i = 0;
int count = lines[0].AsInt;
while (i <= count) {
  Display("lines\[%d]: \"%s\"", i, lines[i]); // displays each of the 4 lines starting with "-"
  i++;
}


Inversely you could of course just do:

Code: ags
String MergeArray(String s[], String glue) {
  if ((s == null) || (String.IsNullOrEmpty(s[0])) || (s[0].AsInt <= 0)) return null;
  if (String.IsNullOrEmpty(glue)) glue = "";
  int size = s[0].AsInt;
  int i = 1;
  String buffer = "";
  while (i <= size) {
    buffer = String.Format("%s%s%s", buffer, glue, s[i]);
    i++;
  }
  return buffer;
}

AdamM

So what about detecting if a character is a symbol or number rather than a letter? I was trying to script this and had the idea of checking the ASCII value as 65-90 or 97-122, before I realised there is no way to check the ASCII value of a character. You could of course check every character against every possible symbol but that's way too impractical and would require too many strings.

Gilbert

Quote from: AdamM on Sat 07/03/2009 19:56:39
... before I realised there is no way to check the ASCII value of a character.

A char variable is actually a 1-byte integer, so say if you have a char variable called blah, you can do all kind of things like:
Code: ags

if (blah<100 and blah>12) blablabla...;


This applies to the elements of the Chars[] array of a String, so:
Code: ags

if (somestring.Chars[i]<100 and somestring.Chars[i]>12) blablabla...;



RickJ

Just to expand upon what Gilbet is saying ...

To determine if a character  is numeric you could create a function such as this.

Code: ags

// Function definition
bool IsNumber(char c) {
   if (c>='0')&&(c<='9') return true;
   else return false;
}

// Example usage
char somechar = '2';
if (IsNumber(somechar)) Display("It's a number");
else Display("It's not a number");


A char variable is actually a 1-byte integer, so say if you have a char variable called blah, you can do all kind of things like:
Code:

if (blah<100 and blah>12) blablabla...;


This applies to the elements of the Chars[] array of a String, so:
Code:

if (somestring.Chars<100 and somestring.Chars>12) blablabla...;


   Report to moderator   Logged
Pages: [1]    

AdamM

I'm confused. Are you saying with 'if (blah<100)' you can check a char against its ASCII decimal? Or merely its numeric value?

RickJ

Quote
I'm confused. Are you saying with 'if (blah<100)' you can check a char against its ASCII decimal? Or merely its numeric value?
He'sa comparing it to the ASCII decimal.  IMHO it's much easier to just use a single quoted char instead like I used in my example.

Ultra Magnus

Thanks for the help, guys.

I finally got the chance to get back to this, and after a bit of tweaking the code that CJ posted it works great.
Here's what I ended up with, for the record.

Code: ags

String CapitaliseLetter(int characterIndex)
{
  if (characterIndex < namestring.Length)
  { 
    String namestringtemp = namestring.UpperCase();
    char capitalCharacter = namestringtemp.Chars[characterIndex];
    namestring = namestring.ReplaceCharAt(characterIndex, capitalCharacter);
  }
  return namestring;
}

function myNamePlayer()
{
 namestring=txtNameinput.Text;

 if (namestring=="") {
   SetTextWindowGUI (5);
   DisplayAtY(402, "Please enter a name before proceeding.");
   SetTextWindowGUI (16);
 }

 else {
   namestring = namestring.LowerCase();
   namestring = CapitaliseLetter(0);
   int i = 0;
   while (i < namestring.Length)
   {
     if (namestring.Chars[i] == ' ' || namestring.Chars[i] == '-') namestring = CapitaliseLetter(i + 1);
     i++;
   }

   cStephanie.Say("It's nice to meet you, %s.", namestring);
 }
}


How about the number/punctuation thing?
How do I delete characters from the string?

I got as far as...
Code: ags
if (namestring.Chars[i] > '0' && namestring.Chars[i] < '9') namestring = namestring.ReplaceCharAt(i, );

...but I don't know how to finish it.

Is there a way to say replace char with nothing, i.e. delete the character?
I don't mean to sound bitter, cold, or cruel, but I am, so that's how it comes out.

I'm tired of pretending I'm not bitchin', a total frickin' rock star from Mars.

Pumaman

Quote from: Ultra Magnus on Wed 11/03/2009 00:14:19
Is there a way to say replace char with nothing, i.e. delete the character?

Use two Substring calls to get the left and the right parts of the string either side of the character you want to remove, then join them back together.

Ultra Magnus

#13
Quote from: Pumaman on Wed 11/03/2009 18:52:01
Use two Substring calls to get the left and the right parts of the string either side of the character you want to remove, then join them back together.

Thanks, CJ.
To that end I now have...
Code: ags

String NixCharacter (int characterIndex)
{
  int e = namestring.Length;
  String namestringend = namestring.Substring(characterIndex + 1, e);
  namestring = namestring.Truncate(characterIndex);
  namestring = namestring.Append(namestringend);
  return namestring;
}

//main code
   namestring = namestring.LowerCase();
   namestring = CapitaliseLetter(0);
   int i = 0;
   while (i < namestring.Length)
   {
     if (namestring.Chars[i] == ' ' || namestring.Chars[i] == '-') {
       if (namestring.Chars[i+1] == ' ' || namestring.Chars[i+1] == '-') {namestring = NixCharacter(i); i--;}
       else {namestring = CapitaliseLetter(i + 1);}
     else if (namestring.Chars[i] > '0' && namestring.Chars[i] < '9') {namestring = NixCharacter(i); i--;}
     i++;
   }


Is there a way to set up a "catch all" for punctuation, like how the >0 & <9 works for numbers, without having to put them all in separately?

Also, whereabouts should I put the if Sting.EndsWith code?
It doesn't seem to want to work wherever I try to put it.
I don't mean to sound bitter, cold, or cruel, but I am, so that's how it comes out.

I'm tired of pretending I'm not bitchin', a total frickin' rock star from Mars.

Ultra Magnus

Okay, scratch that.
I've opted for a different approach now, with this...

if (namestring.Contains(".")) Display("Please only use letters from the alphabet.");

Surprisingly (to me), that one example of a "." catches any and all punctuation or numbers in the string.

However, this also includes spaces and hyphens, and I'd really rather it didn't.
Is there a way around this, or is it very much an all-or-nothing deal?
I don't mean to sound bitter, cold, or cruel, but I am, so that's how it comes out.

I'm tired of pretending I'm not bitchin', a total frickin' rock star from Mars.

Pumaman

Quote from: Ultra Magnus on Fri 13/03/2009 01:06:53
if (namestring.Contains(".")) Display("Please only use letters from the alphabet.");

Surprisingly (to me), that one example of a "." catches any and all punctuation or numbers in the string.

That is precisely the reason why String.Contains was obsoleted and renamed to String.IndexOf -- it's confusing. The way you are using it implies it returns a true/false value like its name implies, but actually returns the index into the string -- which will be -1 (true) if the character isn't in the string. You need to use a "if (namestring.IndexOf(".") >= 0" check to get the behaviour you want.

Anyway, rather than having a list of punctuation characters that you want to ban, it's simpler to define a list of safe characters that you want to allow -- eg. A-Z and a space.

QuoteAlso, whereabouts should I put the if Sting.EndsWith code?
It doesn't seem to want to work wherever I try to put it.

What do you mean? Why do you need to use EndsWith?

Ultra Magnus

Quote from: Pumaman on Fri 13/03/2009 23:29:18
You need to use a "if (namestring.IndexOf(".") >= 0" check to get the behaviour you want.

I actually knew that.
I've used it properly elsewhere in the script, I just forgot with this one, I guess.

Quote from: Pumaman on Fri 13/03/2009 23:29:18
Anyway, rather than having a list of punctuation characters that you want to ban, it's simpler to define a list of safe characters that you want to allow -- eg. A-Z and a space.

How would I do that though?
Using the new method of warning the player to change their name rather than just changing it automatically for them with potentially disastrous results, how can I write an if namestring contains characters other than these, display this clause?

Quote from: Pumaman on Fri 13/03/2009 23:29:18
What do you mean? Why do you need to use EndsWith?

Ignore that.
I was going to have something like...
if (namestring.EndsWith(".") || namestring.EndsWith("!")) namestring=namestring.Truncate(namestring.Length - 1);
But I changed my mind.


Thanks again for the help.
I realise I must be tedious asking so many stupid questions that most people would probably be able to figure out on their own.
I don't mean to sound bitter, cold, or cruel, but I am, so that's how it comes out.

I'm tired of pretending I'm not bitchin', a total frickin' rock star from Mars.

Pumaman

Just walk through all the characters like you're already doing and check them. Something like:

Code: ags

   bool nameIsOk = true;
   int i = 0;
   while (i < namestring.Length)
   {
     if ((namestring.Chars[i] >= 'A') && (namestring.Chars[i] <= 'Z')) { }
     else if ((namestring.Chars[i] >= 'a') && (namestring.Chars[i] <= 'z')) { }
     else if ((namestring.Chars[i] >= '0') && (namestring.Chars[i] <= '9')) { }
     else if (namestring.Chars[i] == ' ') { }
     else { nameIsOk = false; }
      i++;
    }
 
    if (nameIsOk == false)
   {
       Display("Please enter a valid name");
   }

Ultra Magnus

Okay, cool. Thanks again.

One final question: is there a way to allow apostrophes with this method, maybe by their ASCII number or something?
Typing namestring.Chars[] == ''' doesn't work, for obvious reasons.
I don't mean to sound bitter, cold, or cruel, but I am, so that's how it comes out.

I'm tired of pretending I'm not bitchin', a total frickin' rock star from Mars.

monkey0506

Just for reference Ultra Magnus, the character-literals (such as 'A', 'B', 'C', etc.) all equate to their ASCII equivalent. That is: 'A' == 65, 'B' == 66, 'C' == 67, etc. So you could do: if (namestring.Chars[i] == 39) {} // apostrophe (')

Reference

SMF spam blocked by CleanTalk