mIRC Home    About    Download    Register    News    Help

Print Thread
#237990 21/06/12 01:10 PM
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
$replacex is not respecting the order of the parameters passed to it. Instead, it uses the parameter that matches best the start of the string, $replace does not behave like that and I think it should:
Code:
$replacex(1234,234,z,123,u) returns u4
$replace(1234,234,z,123,u) returns 1z


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2011
Posts: 22
P
Ameglian cow
Offline
Ameglian cow
P
Joined: Dec 2011
Posts: 22
$replacex reads through the string & if any sub-string matches any of the strings-to-be-replaced, it is replaced at that moment, as it is not going to read through the string again

---

$replace reads through the string multiple times, each time checking the next string-to-be-replaced in the order it was set to be replaced & then once all strings-to-be-replace have been replaced it will check them all again until nothing can possibly be replaced

It's the equivalent of $replace($replace(1234,234,z),123,u)
If you didn't want that you could just put the highest priority string-to-be-replaced first, as $replace(1234,123,u,234,z) couldn't possibly return 'uz'

---

$replace prioritizes the strings-to-be-replaced by checking the entire string against the next unchecked string-to-be-replaced & then reading through the entire string again (after any replacements have been made by the previous string-to-be-replaced, if any)

$replacex checks the entire string one character at a time against all of the strings-to-be-replaced

In both cases if you wanted to replace '12' with 'z' & '123' with 'u' you would need '123' to be checked before '12'

Last edited by ParadoxDragon; 23/06/12 11:11 AM.
Joined: Oct 2004
Posts: 8,330
Hoopy frood
Offline
Hoopy frood
Joined: Oct 2004
Posts: 8,330
I think you're missing what he said. It's replacing the last item before the first item.

$replacex(1234,234,u,123,z)

You would expect that to replace the 234 first since it's first in your list and then when it got to the 123, there wouldn't be a match.

Another odd thing about it is that $replacex(1234,234,u,123,z,12,x) also returns z4. So it isn't reading the matches backwards and it isn't checking "1" then "12" then "123" or whatever. And both of these return the same thing:

$replacex(1234,234,u,123,z)
$replacex(1234,123,z,234,u)

That means you have no ability to get 1u.


Invision Support
#Invision on irc.irchighway.net
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Originally Posted By: Wims
$replace does not behave like that and I think it should:
I meant that $replacex should behave like $replace, not the opposite


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Quote:
Another odd thing about it is that $replacex(1234,234,u,123,z,12,x) also returns z4. So it isn't reading the matches backwards and it isn't checking "1" then "12" then "123" or whatever
It's like I said, it doesn't try to match each parameter seperatly, it 'browses' the string and then try to match the parameter, "123" will always match first because it appears first in the string, the position of that parameter isn't important


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
This is counter-intuitive behaviour if you're used to $replace's semantics. The logic itself makes sense in general (replacing from left to right in the string, not order of arguments), but does not make sense in the context of $replace.

$replacex should behave like $replace with respect to order of arguments.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Quote:
$replacex should behave like $replace with respect to order of arguments.
Yeah, I replied to myself to make that clear smile


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2002
Posts: 5,420
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,420
Quote:
$replacex checks the entire string one character at a time against all of the strings-to-be-replaced

Yes, that is how it works: it moves through the string a character at a time, from beginning to end, checking for a match at that point in the string against all replacement strings. When a replacement is made, it increments the pointer to the end of the replacement and continues. It never searches the string from the beginning again. It has worked this way ever since it was created in 2004.

I think many of you are expecting it to behave in this way: search the whole string for matches against the first replacement string and perform replacements as necessary. Remember on a per character basis which part of the string was modified. Then do the same thing with the next replacement string, ensuring that no previously modified characters are used in any matching.

Anyone interested in coming up with a C function that does that? :-)

Joined: Apr 2004
Posts: 871
Sat Offline
Hoopy frood
Offline
Hoopy frood
Joined: Apr 2004
Posts: 871
Originally Posted By: Khaled
Anyone interested in coming up with a C function that does that? :-)

There really isn't much more to it than what you described. This is how I would do it, I guess..
Code:
#include <windows.h>

#define LINE_MAX 4149 // NOTE: this is excluding the terminating null character!

typedef enum {
  OK,
  INVALID_PARAMS_ERROR,
  LINE_TOO_LONG_ERROR
} myerr_t; // whatever

static __inline int char_equal(TCHAR c1, TCHAR c2, int cs)
{
  if (cs) return c1 == c2;

  return CharLower((LPTSTR) c1) == CharLower((LPTSTR) c2);
}

static ssize_t find_sub(TCHAR *buf, size_t buflen, char *map, const TCHAR *str, size_t len, size_t start, int cs)
{
  size_t i;
  ssize_t j;

  // go through the rest of the buffer, one position at a time
  for (i = start; i + len <= buflen; i++) {
    // see if we can match the substring here
    for (j = len - 1; j >= 0; j--) {
      // if any part is not original, skip to the start of the next original part
      // we're looping back-to-front so as to skip as much as possible in that case
      if (map[i + j]) {
        i += j;
        break;
      }
      // actually compare characters
      if (!char_equal(buf[i + j], str[j], cs)) break;
    }
    if (j < 0) return i; // a match; return the starting position
  }

  return -1; // no match
}

static ssize_t replacex_one(TCHAR *buf, size_t buflen, char *map, const TCHAR *substr, const TCHAR *newstr, int cs)
{
  size_t sublen, newlen;
  ssize_t pos, tail;

  sublen = lstrlen(substr);
  newlen = lstrlen(newstr);

  if (!sublen) return buflen; // can't replace nothing

  pos = 0;
  // repeatedly find the next all-original occurrence of the substring to replace
  while ((pos = find_sub(buf, buflen, map, substr, sublen, pos, cs)) >= 0) {
    // and replace it - at least, if the buffer is big enough
    if (buflen - sublen + newlen >= LINE_MAX)
     return -1;

    // move everything after the substring we found, if necessary
    tail = buflen - sublen - pos;
    if (sublen != newlen && tail > 0) {
      memmove(&buf[pos + newlen], &buf[pos + sublen], sizeof(buf[0]) * tail);
      memmove(&map[pos + newlen], &map[pos + sublen], sizeof(map[0]) * tail);
    }

    // put the new string in the gap, marking it as changed in the map
    memcpy(&buf[pos], newstr, sizeof(buf[0]) * newlen);
    memset(&map[pos],      1, sizeof(map[0]) * newlen);

    buflen += newlen - sublen;
    pos += newlen; // the new substring will obviously never match
  }

  return buflen;
}

/*
 * replacex() - implements mIRC's $replacex[cs](buf,substr,newstr,..)
 *
 * The caller must provide the (null-terminated) input string in 'buf'.
 * 'params' must be an array of strings, where the first string is the first
 * substr to match, the second string is the first newstr to replace matches
 * with, the third string is the second substr, the fourth string is the
 * second newstr, etcetera. 'nparams' must be the number of strings in the
 * array; it must obviously be a multiple of two, and at least one pair must
 * be given. 'cs' indicates case sensitivity (0 = case-insensitive, 1 =
 * case-sensitive).
 *
 * Upon success, OK is returned, and 'buf' contains the (null-terminated)
 * result string. Upon failure, an error is returned, and the contents of
 * 'buf' are undefined.
 */
myerr_t replacex(TCHAR *buf, const TCHAR *param[], int nparams, int cs)
{
  char map[LINE_MAX];
  ssize_t buflen;
  int i;

  // must be given one or more substr/newstr pairs
  if (!nparams || nparams % 2) return INVALID_PARAMS_ERROR;

  // track which characters have already been replaced before
  // initially, everything is still original, so the map is zeroed
  buflen = lstrlen(buf);
  memset(map, 0, sizeof(map[0]) * buflen);

  // replace each pair in the parameter array
  for (i = 0; i < nparams; i += 2) {
    buflen = replacex_one(buf, buflen, map, param[i], param[i + 1], cs);

    if (buflen < 0) return LINE_TOO_LONG_ERROR;
  }

  // the resulting buffer may not be properly null-terminated, so do that now
  buf[buflen] = 0;

  return OK;
}

void example(void)
{
  TCHAR buf[LINE_MAX+1] = TEXT("1234");
  TCHAR *param[] = { TEXT("234"), TEXT("z"), TEXT("123"), TEXT("u") };

  if (replacex(buf, param, sizeof(param)/sizeof(param[0]), FALSE) == OK)
    printf(TEXT("result string: %s"), buf); // will be "1z"
}

Not very thoroughly tested. Public domain.


Saturn, QuakeNet staff
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Did you get a chance to look at Saturn's implementation? Not that I need the functionality in particular (it is likely that someone reported the issue to me) but well, if the code is there, let's use it!


#mircscripting @ irc.swiftirc.net == the best mIRC help channel

Link Copied to Clipboard