ID:1914606
 
(See the best response by Kaiochao.)
Code:


Problem description:
________________________________________

Hi i need your help. I wanna show on output only 10 last letters from var.

Ex

I have
var/Text="qwertyuiopasdfghjklzxcvbnm"
and i need only "jklzxcvbnm"

________________________________________________________

I have 2'nd ask. Is posible to finde in text special symbols/text like {/(and here you can use some symbols)} like {/:P}. I need this for emotion in chat.

Ex
emolist=list(":p",":)")
var/text="Hi my name is Marek {/:P}"
var/emote=findtext(text..... magic {/[pick(emolist)]})
and i have
Hi my name is Marek <B>:P<B>
<dm>

Sorry for my torrible english. :/
Best response
Marekssj3 wrote:
Hi i need your help. I wanna show on output only 10 last letters from var.

Ex

> I have
> var/Text="qwertyuiopasdfghjklzxcvbnm"
> and i need only "jklzxcvbnm"
>

var last_10 = copytext(Text, -10)


I have 2'nd ask. Is posible to finde in text special symbols/text like {/(and here you can use some symbols)} like {/:P}. I need this for emotion in chat.

Ex
> emolist=list(":p",":)")
> var/text="Hi my name is Marek {/:P}"
> var/emote=findtext(text..... magic {/[pick(emolist)]})
> and i have
> Hi my name is Marek <B>:P<B>
>

I'm not sure about this one, but you could probably use findtext() and copytext().

If you're searching for text, what you need isn't copytext() (which is just for selecting part of a string), but findtext(). That does a case-insensitive search for matching text, and returns 0 if it finds nothing. findtextEx() does the same, except it's case-sensitive; it's probably the one you want for emoticons.

Once you've found the text, it looks like you want to do a replacement. For that you will need copytext(), to grab the parts of the string before and after your match.

var/list/emolist = list(":p",":)")

proc/Magic(emoticon)
... // not sure what this does, but I assume it does some special formatting

proc/ReplaceEmoticons(msg)
var/idx = 1, elen, rep, rlen
for(var/E in emolist)
idx = findtextEx(msg, E)
rep = null
while(idx)
if(isnull(rep)) // do a few one-time calculations
elen = length(E)
rep = Magic(E)
rlen = length(rep)
// do the replacement if found
msg = copytext(msg, 1, idx) + rep + copytext(msg, idx + elen)
idx += rlen
idx = findtextEx(msg, E, idx)
return msg

So the gist of the code works like this: You need to loop through each emoticon in the list in order to search for each. You'll want to search more than once, because maybe the same emoticon was used more than once, so there's a while() loop.

The replacement has three pieces:

1) The first part of the string starts at index 1 and ends just past idx. The third value in copytext() is always the last character plus 1 (or 0 if you want to copy to the end).

2) The middle part is the text you're replacing.

3) The last part is the end of the string, which starts at idx+length(E). Since you want to copy to the end, the third argument to copytext() is left out and therefore counts as 0, to copy to the end of the string.

After replacing, idx is incremented by length(rep)--to skip over the text that was just replaced--and findtextEx() is called again with that as the new starting point.

Now this may not actually be the best possible algorithm. It will always replace emoticons in the order that they appear in your list, and it's possible for a different emoticon to be detected inside text that was already replaced. As an example, suppose you had >:) and :) on your list. If you replace :) first, it will skip over >:) and never detect it properly; if you replace >:) first, then :) could be detected in the replacement text and you end up double-replacing.

This is a slightly more advanced algorithm:

var/list/emolist = list(":p",":)",">:)")

proc/Magic(emoticon)
...

proc/ReplaceEmoticons(msg)
var/idx=1, E, best, bestidx, found, rep
while(1)
best = null
for(E in emolist)
found = findtextEx(msg, E, idx)
if(found)
// This is our new best match if:
// 1) we don't have any other match yet, or
// 2) this appears earlier than the best match, or
// 3) this pattern is at the same spot but is longer (more specific)
if(!best || found < bestidx ||\
(found == bestidx && length(E) > length(best)))
best = E
bestidx = found
if(!best) break // no match, so we're done
rep = Magic(best)
idx = bestidx
msg = copytext(msg, 1, idx) + rep + copytext(msg, idx + length(best))
idx += length(rep)
return msg

This better algorithm works by repeatedly searching for any emoticon match, starting at the current index (which starts at 1). Whenever a match is found, it checks to see if it's a better one than the one it already has (if any). If no match is found for any emoticons starting at idx, the loop terminates. Otherwise, the best match is replaced, and idx is advanced to just after the replaced text.
I totally agree with kai and Lummox and I advise you to follow their advice but I just want to share another possible solution for your Emoticon Replace problem (because knowledge is always welcome).
mob/Player/verb/ConvertEmoticons(t as text)

var/tmp/list/emoticons = list(":p",":)") //Emoticons List here
var/tmp/MySTR = t

for(var/each in emoticons) //Loop through the emoticons list
if(findtext(t,each) <> 0) //If the "each" emoticon exists in the text(MySTR / t) that the client typed, proced and update MySTR

MySTR = ReplaceEmoticon(MySTR,each) //This will replace the emoticons with a bold version and Update the MySTR string

world<<MySTR //Displays the final version of the MySTR string with all the typed emoticons in bold


mob/Player/proc/ReplaceEmoticon(STRING,TARG) //This proc will replace the emoticons with the correspondent bold version

var/tmp/each = TARG //The emoticon
var/tmp/MyString = STRING //The Original string
var/tmp/ResultString = ""
var/tmp/AnalyseString = MyString
var/tmp/count = 0
var/tmp/LENGTH_MATH = 0
var/tmp/FOUND= 0



while(length(MyString) > LENGTH_MATH)

count+=1

if(findtext(AnalyseString,each))
FOUND = 1

var/STOP_AT = findtext(AnalyseString,each)
var/NEW_STR = "[copytext(AnalyseString,1,STOP_AT)]<b>[each]</b>"
ResultString +=NEW_STR
LENGTH_MATH = length(ResultString) - (7 * count)

AnalyseString = copytext(MyString,LENGTH_MATH+1)

else
if(FOUND == 1)
var/N_STR = copytext(MyString,LENGTH_MATH+1)
ResultString += N_STR
LENGTH_MATH += length(N_STR)

return ResultString



But like I said Lummox's solution is probably more correct and well constructed than mine but knowing multiple solutions to the same problem will help you think and make decisions on the coding world.

I hope I could help.

Hug,
,MistY
In response to Misticone
Your solution uses the same approach as my first code (the more basic version that will fail when emoticons are too similar), except it's harder to follow, has more proc call overhead and is therefore slower, it's outright broken, and it has a terrible naming convention.

Seriously, the naming convention you're using is terrible. In pretty much all modern programming languages, long var names that are all caps are considered to be constants.

This is also not good:

        if(findtext(AnalyseString,each))
FOUND = 1

var/STOP_AT = findtext(AnalyseString,each)

You're calling findtext, ignoring its result, and then having to call it again because you threw the result away before. The logic in the rest of this if() appears to be basically incorrect though, and as a result the string is being mangled; this will not work the way you think it will.

This is also a terrible habit:

if(FOUND == 1)

Considering you're using that simply as a true or false value, you shouldn't be comparing the value to 1. When you want to check for true or false on a var, use if(myvar) or if(!myvar), respectively.
I don't like either approach.

IMO, the system should only search for easily identifiable smiley markers.

1) Search for all instances of ":" in the string.

  1.1) Check for eyebrow chars to the left

  1.2a) Check for nose chars to the right

    1.2a.1) Check for mouth chars to the right

  1.2b) Check for mouth chars to the right

  1.3) if smiley is valid, check associative list for matching smiley

  1.4) if smiley is in associative list, replace with image tag

2) Search for all instances of "=" in the string.

  2.1) Check for eyebrow chars to the left

  2.2a) Check for nose chars to the right

    2.2a.1) Check for mouth chars to the right

  2.2b) Check for mouth chars to the right

  2.3) if smiley is valid, check associative list for matching smiley

  2.4) if smiley is in associative list, replace with image tag

3) Search for all instances of "8" in the string.

  3.1) Check for eyebrow chars to the left

  3.2a) Check for nose chars to the right

    3.2a.1) Check for mouth chars to the right

  3.2b) Check for mouth chars to the right

  3.3) if smiley is valid, check associative list for matching smiley

  3.4) if smiley is in associative list, replace with image tag

4) Search for all instances of "_" in the string.

  4.1) Check for eye chars to the left

  4.2) Check for eye chars to the right

  4.3) if smiley is valid, check associative list for matching smiley

  4.4) if smiley is in associative list, replace with image tag

4) Search for all instances of "." in the string.

  4.1) Check for eye chars to the left

  4.2) Check for eye chars to the right

  4.3) if smiley is valid, check associative list for matching smiley

  4.4) if smiley is in associative list, replace with image tag

5) Search for all instances of "o" in the string.

  5.1) Check for eye chars to the left

  5.2) Check for eye chars to the right

  5.3) if smiley is valid, check associative list for matching smiley

  5.4) if smiley is in associative list, replace with image tag

6) Search for all instances of "x" in the string.

  6.1) Check for eye chars to the left

  6.2) Check for eye chars to the right

  6.3) if smily is invalid, look for nose/mouth on the right. If found, revalidate.

  6.4) if smiley is valid, check associative list for matching smiley

  6.5) if smiley is in associative list, replace with image tag



If you have lots of valid smileys in your string, or in your global list, it's gonna be a lot faster if you investigate all potential smileys using text2ascii rather than searching for each possible variant.

But... Yeah, emoticons are stupid.
Yeah, both algorithms will break down with a large list. Ultimately the very best approach would be if BYOND had an equivalent to strcspn(), which it does not. (At least not presently.) At that point, text2ascii() and something like Markov chains could be employed.