Interesting scenario with string manipulation in Golang, need explanation [duplicate] - string

This question already has answers here:
strings.Trim is removing the letter "i" (golang)
(2 answers)
Why TrimLeft doesn't work as expected?
(2 answers)
Closed 5 days ago.
There is this really weird scenario in which I am using strings.TrimLeft function to time a chunk from the left side of the string, but getting an unexpected result.
Here is the code
query := "INSERT INTO order (data_enum,id) VALUES (1,1);"
firstPart := "INSERT INTO order ("
// unexpected output
q := strings.TrimLeft(query, firstPart)
fmt.Println(q) // ata_enum,id) VALUES (1,1);
// expected output
q = query[len(firstPart):]
fmt.Println(q) // data_enum,id) VALUES (1,1);
This only happens in this specific case, if the original query is different, than the output is correct. For example
query := "INSERT INTO order (id,data_enum) VALUES (1,1);"
firstPart := "INSERT INTO order ("
q := strings.TrimLeft(query, firstPart)
fmt.Println(q) // id,data_enum) VALUES (1,1);
q = query[len(firstPart):]
fmt.Println(q) // id,data_enum) VALUES (1,1);
So I am wondering what is the reason behind this?
Here is the Go Playground link for the snippet to try

Here's the documentation for TrimLeft:
TrimLeft returns a slice of the string s with all leading Unicode code points contained in cutset removed.
So it removes the first d in the query as well, because d is in the cutset.
And the solution, again from the documentation:
To remove a prefix, use TrimPrefix instead.

Related

Ada - How do I split a string in two parts?

If I create a subprogram of type function that for instance orders you to type a string of a particular length and you type Overflow, it's supposed to type the last half of the string, so in this case it would be flow. But on the other end if I type an odd number of characters like Stack it's supposed to type the last half of the string + the middle letter, so in this case it would be "ack".
Let me make it clearer (text in bold is user input):
Type a string that's not longer than 7 characters: Candy
The other half of the string is: ndy
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
function Split_String (S : in String) return String is
begin
Mid := 1 + (S'Length / 2);
return S(Mid .. S'Last);
end Split_String;
S : String(1 .. 7);
I : Integer;
begin
Put("Type a string that's no longer than 7 characters: ");
Get_Line(S, I);
Put(Split_String(S));
end Split;
Let me tell you how I've been thinking. So I do a Get_Line to see how many characters the string contains. I then put I in my subprogram to determine if its evenly dividable by two or not. If it's dividable by two, the rest should be 0, thus it'll mean that typing out the other half of the string + THE MIDDLE CHARACTER is not needed. If in all the other cases, it's not dividable by two I have to type out the other half of the string + the middle character. But now I stumbled upon a big problem in my main program. I don't know how type out the other half of a string. If a string contains 4 words I can just type out Put(S(3 .. 4); but the thing is that I don't know a general formula for this. Help is appreciated! :) Have a good day!
You need a more general approach to your problem. Also, try to understand how Get_Line works for you.
For example, if you declare an input string with a large size such as
Input : String (1..1024);
You will have a string large enough to work with any likely input values.
Next, you need a variable to indicate how many characters were actually read by Get_Line.
Length : Natural;
The data returned by Get_Line will then be in the slice of the input string designated as
Input (1 .. Length);
Pass that slice to your function to return the second half of the string.
function last_half(S : string) return string;
last_half(Input(1..Length));
Now all you need is to calculate the last half of the string passed to the function last_half. The function will output a slice of the string passed to it. To find the first index of the last half of the input string you must perform the calculation
mid : Positive := 1 + (S'length / 2);
Then simply return the string S(mid .. S'Last).
It appears that the goal of this exercise is to learn how to use array slices. Concentrate on how slices work for you in the problem and the solution will be very simple.
One possible solution is
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
Input : String (1 .. 1_024);
Length : Natural;
function last_half (S : in String) return String is
Mid : Positive := 1 + (S'Length / 2);
begin
return S (Mid .. S'Last);
end last_half;
begin
Put ("Enter a string: ");
Get_Line (Input, Length);
Put_Line (Input (1 .. Length) & " : " & last_half (Input (1 .. Length)));
end Main;
Study how the solution uses array slices on the return value of Get_Line and on the parameter for the function last_half and on its return statement. It is also important to remember that the type String is defined as an unbounded array of character. This means that every slice of a string is also a string.
type String is array ( Positive range <> ) of Character;
Aside from being an untidy mess, your latest code edit (as of 20:11 GMT on 15 Nov 2021) doesn’t even compile. Please don’t show us code like this! (unless, of course, that’s the problem).
I’d like to strongly suggest this alternate way of inputting strings:
declare
S : constant String := Get_Line;
begin
-- do things with S, which is exactly as long as
-- the input you typed: no undefined characters at
-- the end to confuse the result, no need to worry
-- about overrunning an input buffer
end;
With this change, and obvious syntactic changes, your current code will do what you want.

Adding a integer value to string on a single statement

I was wondering how can I add an integer value to a string value like "10". I know I can accomplish this by converting the string into an int first and then after adding the integer I can convert it back into string. But can I accomplish this in a single statement in golang. For example I can do this with multiple lines like this:
i, err := strconv.Atoi("10")
// handle error
i = i + 5
s := strconv.Itoa(i)
But is there any way that I can accomplish this in a single statement?
There is no ready function in the standard library for what you want to do. And the reason for that is because adding a number to a number available as a string and having the result as another string is (terribly) inefficient.
The model (memory representation) of the string type does not support adding numbers to it efficiently (not to mention that string values are immutable, a new one has to be created); the memory model of int does support adding efficiently for example (and CPUs also have direct operations for that). No one wants to add ints to numbers stored as string values. If you want to add numbers, have your numbers ready just as that: numbers. When you want to print or transmit, only then convert it to string (if you must).
But everything becomes a single statement if you have a ready util function for it:
func add(s string, n int) (string, error) {
i, err := strconv.Atoi(s)
if err != nil {
return "", err
}
return strconv.Itoa(i + n), nil
}
Using it:
s, err := add("10", 5)
fmt.Println(s, err)
Output (try it on the Go Playground):
15 <nil>

Exercice with type String in Pascal

(Translated from German to Englisch)
I need help in this exercise :
Thread: String processing The user can make simple changes to an input sentence.
conditions
The program displays a menu for the user to select the following action. This is also displayed again after the action has been completed until the user terminates the program (a loop is therefore required).
The menu contains the following items, which should be executed when the specified letter is entered:
A. Enter the sentence
B. Determine the number of words
C. Determine the number of characters that are less than their sequence character
D. Replace all the words in the sentence with their uppercase initials
X. end
If the user enters a different letter, nothing happens or the menu is output again.
If the menu item A is selected, a prompt is issued to enter a set which is read into a string variable. This variable can not be changed by the actions of menu items B, C and D! Possibly. A copy of the set has to be prepared beforehand in another string variable.
In menu point B the number of all words in the block is to be counted. For simplicity, you can assume that there is always one space between two words. At the beginning and end of the sentence there are no spaces. The number of words is output after the calculation (e.g., "The set is 4 words").
If the user executes menu item C, the set is traversed character-by-character, and for each character it is checked whether it is smaller than its trailing character. Here is a simple character comparison (you can also write directly something like '1' <'d'). The number of characters so found is then output (e.g., "13 characters found in the sentence less than the trailing character").
In menu item D, the sentence is traversed and every word contained in it is replaced by its upper-case initial character. The capitalization is of course only made if the first character is a letter, otherwise the character remains unchanged. You can assume that the sentence never starts or ends with a space. Between two words there is always exactly one space and so it should be between the initial letters. For example, from "123 good mood" becomes "1 G L".
It is not permissible here to build up a completely new string piece by piece! Instead, you should work in a loop on a copy of the original sentence with pos, copy, length, delete and insert! It is also forbidden to "gather" the initial characters all at the beginning or end of the string; These should be inserted directly into the string at the position of the corresponding word!
Furthermore, a string can not be accessed at menu point D, because the work with string routines is to be practised explicitly here. Menu items B, C and D may only be selectable if a record has already been entered. Otherwise nothing happens or a fault message is entered when entering B, C or D in the menu and the menu is output again.
Each call to the menu items B, C or D will always work on the original set entered by the user and not on a set that has already been altered by previously executed menu items!
By entering the menu item A again, the entered block can be overwritten by a new one.
With an 'X' the user can terminate the program.
Use wherever it is the predefined string functions and do not write it yourself with difficulty loops, etc.! However, the use of the strreplace or reverseString functions is forbidden!
Here's my work till now, I only have problems with part D:
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
const
lz = ' ';
var
Satz: string;
Buchstabe: char;
i, p, j, zaehler2, index, count: integer;
writeln('A) Write Sentence');
readln(Satz);
'D':
begin
index := 2;
insert(lz, Satz, length(Satz)+1);
count := (pos(lz,Satz));
repeat
delete(Satz, index,(count - index));
index := index + 2;
count := pos(lz,copy(Satz,index,(length(Satz)-index)))+index-1;
until ;
writeln(uppercase(Satz));
end
I'm glad you've found your own solution, well done!
While you've been doing that, I've been writing the answer below and, as I
have finished it, I thought I'd post it here to show you another way
to go about the problem of extracting words from a string. There are
dozens of ways of doing that task, but I hope the one I've used is fairly
easy to follow.
Maybe the reason you were having a problem with this is that your string-indexing
expressions are a bit too complicated. I bet if you come back to your code in 6 months
it will take you a while to figure out what it is supposed to be doing and longer
to tell whether it is actually doing it. The key to avoiding problems like that
is to break your code up into chucks which are easier to follow and easier
to test. So instead of just telling you what your repeat condition should be,
I'll show you a way which is easier to follow.
The first thing to do is to extract a singe word from the input. So, first thing
I've written is a function, ExtractFirstWord which returns the first word in
the input string, whether or not the input includes spaces, and also returns
a Remainder string which is what is left or the input string after the first
word (and any spaces immediately following it have been removed). This is done
using some simple while loops which are coded to skip over the leading spaces
and then build a string from the non-space characters which follow.
Code
const
lz = ' ';
var
Satz: string;
FirstWord : String;
Remainder : String;
function ExtractFirstWord(const InputStr : String; var Remainder : String) : String;
var
P : Integer;
WordStart : Integer;
begin
Result := '';
P := 1;
// The following skips over any spaces at the start of InputStr
while (P <= Length(InputStr)) and (InputStr[P] = lz) do
Inc(P);
// Now we know where the first word starts
WordStart := P;
// Now we can get the first word, if there is one
while (P <= Length(InputStr)) and (InputStr[P] <> lz) do
Inc(P);
Result := Copy(InputStr, WordStart, P - WordStart);
Remainder := Copy(InputStr, P, Length(InputStr));
// the following is one way to remove spaces at the start of Remainder
while (Length(Remainder) > 0) and (Remainder[1] = lz) do
Delete(Remainder, 1, Length(lz));
// instead you could do something simlar to the first `while` loop above
end;
begin
Satz := ' cat dog ';
repeat
FirstWord := ExtractFirstWord(Satz, Remainder);
FirstWord := UpperCase(FirstWord);
Satz := Remainder;
writeln('First word: ', FirstWord, ' remainder: ', Remainder);
until Remainder = '';
readln;
end.
This particular way of doing it is not an ideal fit with the other requirements
specified in your task but should be easily adaptable to them. E.g, the upper-casing of words could be done "in place" on the input string by upper-casing the current character of it in the second While loop.
Btw, if you are using Delphi or Free Pascal/Lazarus, there is a much simpler
way of extracting the words from a string. It uses a TStringList. Try
looking it up in the online help and have a thing about how you might use it
to do the task.

Replace substring with binary strings

I want to perform a substring replace operation on binary strings. There is a function available that does this exact thing for strings of type text (c.f.):
replace(string text, from text, to text)
But unfortunately none for binary strings of type bytea (c.f.).
Now I wonder, do I need to reimplement this operation for binary strings or can I use the corresponding basic string function for this task? Are there edge cases that could break my application:
select replace('\000\015Hello World\000\015Hello World'::bytea::text,
'World',
'Jenny')::bytea
I couldn't find a specific note in the documentation so far. Can someone help me on that?
According to the suggestion by #DanielVérité I have implemented a plpgsql function that does a string replace with binary strings of type bytea.
In the implementation I only used functions from the binary strings section, so I think it should be safe to use.
Here's my code:
CREATE OR REPLACE FUNCTION
replace_binary(input_str bytea, pattern bytea, replacement bytea)
RETURNS bytea
AS $$
DECLARE
buf bytea;
pos integer;
BEGIN
buf := '';
-- validate input
IF coalesce(length(input_str), 0) = 0 OR coalesce(length(pattern), 0) = 0
THEN
RETURN input_str;
END IF;
replacement := coalesce(replacement, '');
LOOP
-- find position of pattern in input
pos := position(pattern in input_str);
IF pos = 0 THEN
-- not found: append remaining input to buffer and return
buf := buf || substring(input_str from 1);
RETURN buf;
ELSE
-- found: append substring before pattern to buffer
buf := buf || substring(input_str from 1 for pos - 1);
-- append replacement
buf := buf || replacement;
-- go on with substring of input
input_str := substring(input_str from pos + length(pattern));
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql
IMMUTABLE;
As for my test cases it works quite well:
with input(buf, pattern, replacement) as (values
('tt'::bytea, 't'::bytea, 'ttt'::bytea),
('test'::bytea, 't'::bytea, 'ttt'::bytea),
('abcdefg'::bytea, 't'::bytea, 'ttt'::bytea),
('\000\015Hello 0orld\000\015Hello 0orld'::bytea, '0'::bytea, '1'::bytea))
select encode(replace_binary(buf, pattern, replacement), 'escape') from input;
outputs as expected:
encode
------------------------------------
tttttt
tttesttt
abcdefg
\000\rHello 1orld\000\rHello 1orld
(4 rows)
The problem with casting to text and back to bytea is that it wouldn't work if the replacement strings involved quoted bytes in strings. Let's see with an example.
(I'm setting bytea_output to hex to better see the text, otherwise it's all hex numbers)
Initial query:
with input(x) as (values (('\000\015Hello World\000\015Hello World'::bytea)))
select replace(x::text, 'World', 'Jenny')::bytea from input;
The result is fine:
replace
----------------------------------------
\000\015Hello Jenny\000\015Hello Jenny
(1 row)
But if trying with a modified version that wants to replace the character 0 by 1
with input(x) as (values (('\000\015Hello 0orld\000\015Hello 0orld'::bytea)))
select replace(x::text, '0', '1')::bytea from input;
The result is:
replace
----------------------------------------
IMHello 1orldIMHello 1orld
whereas the desired result would be: \000\015Hello 1orld\000\015Hello 1orld.
This happens because the intermediate representation \000\015 gets replaced by \111\115

repeat string with LINQ/extensions methods [duplicate]

This question already has answers here:
Is there an easy way to return a string repeated X number of times?
(21 answers)
Closed 9 years ago.
Just a curiosity I was investigating.
The matter: simply repeating (multiplying, someone would say) a string/character n times.
I know there is Enumerable.Repeat for this aim, but I was trying to do this without it.
LINQ in this case seems pretty useless, because in a query like
from X in "s" select X
the string "s" is being explored and so X is a char. The same is with extension methods, because for example "s".Aggregate(blablabla) would again work on just the character 's', not the string itself. For repeating the string something "external" would be needed, so I thought lambdas and delegates, but it can't be done without declaring a variable to assign the delegate/lambda expression to.
So something like defining a function and calling it inline:
( (a)=>{return " "+a;} )("a");
or
delegate(string a){return " "+a}(" ");
would give a "without name" error (and so no recursion, AFAIK, even by passing a possible lambda/delegate as a parameter), and in the end couldn't even be created by C# because of its limitations.
It could be that I'm watching this thing from the wrong perspective. Any ideas?
This is just an experiment, I don't care about performances, about memory use... Just that it is one line and sort of autonomous. Maybe one could do something with Copy/CopyTo, or casting it to some other collection, I don't know. Reflection is accepted too.
To repeat a character n-times you would not use Enumerable.Repeat but just this string constructor:
string str = new string('X', 10);
To repeat a string i don't know anything better than using string.Join and Enumerable.Repeat
string foo = "Foo";
string str = string.Join("", Enumerable.Repeat(foo, 10));
edit: you could use string.Concat instead if you need no separator:
string str = string.Concat( Enumerable.Repeat(foo, 10) );
If you're trying to repeat a string, rather than a character, a simple way would be to use the StringBuilder.Insert method, which takes an insertion index and a count for the number of repetitions to use:
var sb = new StringBuilder();
sb.Insert(0, "hi!", 5);
Console.WriteLine(sb.ToString());
Otherwise, to repeat a single character, use the string constructor as I've mentioned in the comments for the similar question here. For example:
string result = new String('-', 5); // -----
For the sake of completeness, it's worth noting that StringBuilder provides an overloaded Append method that can repeat a character, but has no such overload for strings (which is where the Insert method comes in). I would prefer the string constructor to the StringBuilder if that's all I was interested in doing. However, if I was already working with a StringBuilder, it might make sense to use the Append method to benefit from some chaining. Here's a contrived example to demonstrate:
var sb = new StringBuilder("This item is ");
sb.Insert(sb.Length, "very ", 2) // insert at the end to append
.Append('*', 3)
.Append("special")
.Append('*', 3);
Console.WriteLine(sb.ToString()); // This item is very very ***special***

Resources