expressions: Implement the REPLACE string function.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 21 Feb 2016 01:12:17 +0000 (17:12 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 21 Feb 2016 01:12:17 +0000 (17:12 -0800)
Requested by Frans Houweling.

doc/expressions.texi
src/language/expressions/helpers.c
src/language/expressions/helpers.h
src/language/expressions/operations.def
tests/language/expressions/evaluate.at

index 7226116bd13e8897c29c2dbf120acf913ca17857..b7e23a5f96c3d0721a4614832a09292a47bd67e9 100644 (file)
@@ -264,7 +264,7 @@ The sections below describe each function in detail.
 * Set Membership::              ANY RANGE
 * Statistical Functions::       CFVAR MAX MEAN MIN SD SUM VARIANCE
 * String Functions::            CONCAT INDEX LENGTH LOWER LPAD LTRIM NUMBER 
-                                RINDEX RPAD RTRIM STRING SUBSTR UPCASE
+                                REPLACE RINDEX RPAD RTRIM STRING SUBSTR UPCASE
 * Time and Date::               CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx
                                 DATEDIFF DATESUM
 * Miscellaneous Functions::     LAG YRMODA VALUELABEL
@@ -620,6 +620,15 @@ right-padded with spaces.  If @var{string} is not in the correct
 format for @var{format}, system-missing is returned.
 @end deftypefn
 
+@cindex strings, replacing substrings
+@cindex replacing substrings
+@deftypefn {Function} {} REPLACE (@var{haystack}, @var{needle}, @var{replacement}[, @var{n}])
+Returns string @var{haystack} with instances of @var{needle} replaced
+by @var{replacement}.  If nonnegative integer @var{n} is specified, it
+limits the maximum number of replacements; otherwise, all instances of
+@var{needle} are replaced.
+@end deftypefn
+
 @cindex strings, searching backwards
 @deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle})
 Returns a positive integer indicating the position of the last
index 5aad13eb160b5f3f160481fcdaefa30a92b05354..cfc46290b785cfad8c4d5dd8182c65fe016774cd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008, 2010, 2011, 2015 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2010, 2011, 2015, 2016 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -25,6 +25,8 @@
 #include "libpspp/assertion.h"
 #include "libpspp/pool.h"
 
+#include "gl/minmax.h"
+
 const struct substring empty_string = {NULL, 0};
 
 double
@@ -675,3 +677,43 @@ round_nearest (double x, double mult, double fuzzbits)
   x = x >= 0. ? floor (x + adjustment) : -floor (-x + adjustment);
   return x * mult;
 }
+
+struct substring
+replace_string (struct expression *e,
+                struct substring haystack,
+                struct substring needle,
+                struct substring replacement,
+                double n)
+{
+  if (!needle.length
+      || haystack.length < needle.length
+      || n <= 0
+      || n == SYSMIS)
+    return haystack;
+
+  struct substring result = alloc_string (e, MAX_STRING);
+  result.length = 0;
+
+  size_t i = 0;
+  while (i <= haystack.length - needle.length)
+    if (!memcmp (&haystack.string[i], needle.string, needle.length))
+      {
+        size_t copy_len = MIN (replacement.length, MAX_STRING - result.length);
+        memcpy (&result.string[result.length], replacement.string, copy_len);
+        result.length += copy_len;
+        i += needle.length;
+
+        if (--n < 1)
+          break;
+      }
+    else
+      {
+        if (result.length < MAX_STRING)
+          result.string[result.length++] = haystack.string[i];
+        i++;
+      }
+  while (i < haystack.length && result.length < MAX_STRING)
+    result.string[result.length++] = haystack.string[i++];
+
+  return result;
+}
index 0d349f7fd9d91510a9f4674336179e44d5d7258d..51e1db0329a1bf3d8cd7c83f72e3023aadf5b6a1 100644 (file)
@@ -83,4 +83,10 @@ double idf_fdist (double P, double a, double b);
 
 double round_nearest (double x, double mult, double fuzzbits);
 
+struct substring replace_string (struct expression *,
+                                 struct substring haystack,
+                                 struct substring needle,
+                                 struct substring replacement,
+                                 double n);
+
 #endif /* expressions/helpers.h */
index 471fa1d30d331c4cb6e924b6c6822dcbe4570bde..203ccbb42a2b9825135d624c6700bc67b73e0cee 100644 (file)
@@ -1,7 +1,7 @@
 // -*- c -*-
 //
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2005, 2006, 2009, 2010, 2011, 2012, 2015 Free Software Foundation, Inc.
+// Copyright (C) 2005, 2006, 2009, 2010, 2011, 2012, 2015, 2016 Free Software Foundation, Inc.
 // 
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -399,7 +399,6 @@ function INDEX (string haystack, string needles, needle_len_d)
     }
 }
 
-
 function RINDEX (string haystack, string needle)
 {
   if (needle.length == 0)
@@ -504,6 +503,15 @@ absorb_miss string function LPAD (string s, n, string c)
     }
 }
 
+string function REPLACE (string haystack, string needle, string replacement)
+    expression e;
+  = replace_string (e, haystack, needle, replacement, DBL_MAX);
+
+absorb_miss string function REPLACE (string haystack, string needle,
+                                     string replacement, n)
+    expression e;
+  = replace_string (e, haystack, needle, replacement, n);
+
 absorb_miss string function RPAD (string s, n)
      expression e;
 {
index 43d6b0b877a642aea6b2731a128878ff49457c19..41104dd70b422d8287da58c604647a10d21ca340 100644 (file)
@@ -810,6 +810,21 @@ RINDEX(string, string, number).]],
   [[lower(1)], [error],
    [error: DEBUG EVALUATE: Type mismatch invoking LOWER(string) as lower(number).]])
 
+CHECK_EXPR_EVAL([replace],
+  [[replace('banana', 'an', 'AN')], ["bANANa"]],
+  [[replace('banana', 'an', 'a')], ["baaa"]],
+  [[replace('banana', 'an', '')], ["ba"]],
+  [[replace('banana', 'na', '')], ["ba"]],
+  [[replace('banana', 'ba', 'BA')], ["BAnana"]],
+  [[replace('banana', 'na', 'xyzzy')], ["baxyzzyxyzzy"]],
+  [[replace('banana', 'an', 'xyzzy', 1)], ["bxyzzyana"]],
+  [[replace('banana', 'an', 'xyzzy', 1.5)], ["bxyzzyana"]],
+  [[replace('banana', 'bananana', 'xyzzy')], ["banana"]],
+  [[replace('banana', '', 'xyzzy')], ["banana"]],
+  [[replace('banana', 'ba', '', 0)], ["banana"]],
+  [[replace('banana', 'ba', '', -1)], ["banana"]],
+  [[replace('banana', 'ba', '', $sysmis)], ["banana"]])
+
 CHECK_EXPR_EVAL([lpad number ltrim lpad rtrim rpad string substr upcase],
   [[lpad('abc', -1)], [""]],
   [[lpad('abc', 0)], ["abc"]],