N.B: Directly connected to a problem I had a few years ago, but I'd like to resolve the first issue there which wasn't otherwise part of the question, so please don't flag it as a duplicate of my earlier question.
I have a string centring function that centres the given string according to the given width (which is 113 characters):
std::string center(std::string input, int width = 113) {
return std::string((width - input.length()) / 2, ' ') + input;
}
I am using a game SDK in order to create a gameserver modification, and this game SDK supports coloured strings in the game's command console, which are denoted using a dollar sign and a number from 0-9 (i.e, $1) and are not printed in the console itself.
The string centring function above treats these markers as part of the total string, so I want to add the total amount of characters these markers take up to the width so that the string is actually centred.
I have tried modifying the function:
std::string centre(std::string input, int width = 113) {
std::ostringstream pStream;
for(std::string::size_type i = 0; i < input.size(); ++i) {
if (i+1 > input.length()) break;
pStream << input[i] << input[i+1];
CryLogAlways(pStream.str().c_str());
if (pStream.str() == "$1" || pStream.str() == "$2" || pStream.str() == "$3" || pStream.str() == "$4" || pStream.str() == "$5" || pStream.str() == "$6" || pStream.str() == "$7" || pStream.str() == "$8" || pStream.str() == "$9" || pStream.str() == "$0")
width = width+2;
pStream.clear();
}
return std::string((width - input.length()) / 2, ' ') + input;
}
The goal of the above function is to iterate through the string, add the current character and the next to an ostringstream, and evaluate the ostringstream.
This didn't exactly do as I wanted:
<16:58:57> 8I
<16:58:57> 8IIn
<16:58:57> 8IInnc
<16:58:57> 8IInncco
<16:58:57> 8IInnccoom
<16:58:57> 8IInnccoommi
<16:58:57> 8IInnccoommiin
<16:58:57> 8IInnccoommiinng
<16:58:57> 8IInnccoommiinngg
<16:58:57> 8IInnccoommiinngg C
<16:58:57> 8IInnccoommiinngg CCo
<16:58:57> 8IInnccoommiinngg CCoon
<16:58:57> 8IInnccoommiinngg CCoonnn
<16:58:57> 8IInnccoommiinngg CCoonnnne
(snippet from server log)
Here's a brief summary of the issue:
I think I might be missing how iteration works; what am I missing, and how can I make this function work in the way I want it to?
So, what you are really trying to do is count the instances of $N in your string, where N is a decimal digit. To do this, just look in the string for instances of $ using std::string::find, and then check the next character to see if it is a digit.
std::string::size_type pos = 0;
while ((pos = input.find('$', pos)) != std::string::npos) {
if (pos + 1 == input.size()) {
break; // The last character of the string is a '$'
}
if (std::isdigit(input[pos + 1])) {
width += 2;
}
++pos; // Start next search from the next char
}
In order to use std::isdigit, you need to first:
#include <cctype>
Related
I want to restrict the TextFormField to only accept numbers separated by commas and sometimes with dashes but I don't want them to come consecutive to each other and also don't want the same character consecutive.
Ex:-
1,3-4,9-11 is correct
1,,3--4,9-11 is wrong
1,-3-4,9-11 is wrong
1-,3-4,9-11 is wrong
To restrict things to only numbers, commas and dashes I'm using:-
FilteringTextInputFormatter(
RegExp("[0-9,-]"),
allow: true
)
But it is not restricting the consecutive behavior as shown in the wrong behavior in the examples.
So, how can I restrict my TextFormField to the correct behavior represented in the examples?
Thank you.
Update: I finally followed this approach for this problem.
If you want to validate on submit, you might write the pattern as:
^[0-9]+(?:[,-][0-9]+)*$
Regex demo
If a negative lookahead is supported, you an exclude matching 2 times one of - or , while validating on typing.
Note that this will allow , or - at the end:
^(?!.*[,-][,-])[0-9,-]*
Regex demo
For my problem above I finally combined FilteringTextInputFormatter with a custom TextInputFormatter specific to my case so I'm adding it below so that if anyone wants to do the same they can have a look at this approach:
class RangeTextInputFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
TextSelection newSelection = newValue.selection;
String truncated = newValue.text;
int oldValueLength = oldValue.text.length;
int newValueLength = newValue.text.length;
// Blocks comma and dash at start.
if ((oldValue.text.isEmpty || oldValue.text == "") &&
(newValue.text[newValueLength - 1] == "," ||
newValue.text[newValueLength - 1] == "-")) {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
// Allows numbers at start.
else if (oldValue.text.isEmpty || oldValue.text == "") {
truncated = newValue.text;
newSelection = newValue.selection;
} else {
// Blocks comma and dash after comma.
if (oldValue.text[oldValueLength - 1] == "," &&
(newValue.text[newValueLength - 1] == "," ||
newValue.text[newValueLength - 1] == "-")) {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
// Blocks comma and dash after dash.
else if (oldValue.text[oldValueLength - 1] == "-" &&
(newValue.text[newValueLength - 1] == "," ||
newValue.text[newValueLength - 1] == "-")) {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
// Blocks dash after number dash number. Ex: 48-58- <- this last dash is blocked
else if (oldValue.text.lastIndexOf('-') != -1) {
if (!(oldValue.text
.substring(oldValue.text.lastIndexOf('-'))
.contains(",")) &&
newValue.text[newValueLength - 1] == "-") {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
}
}
return TextEditingValue(
text: truncated,
selection: newSelection,
composing: TextRange.empty,
);
}
}
Now use it just like FilteringTextInputFormatter:
inputFormatters: [
FilteringTextInputFormatter(RegExp("[0-9,-]"), allow: true),
RangeTextInputFormatter(),
]
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I want to create the command /kick playername reason in C++, so I tried it:
if (IPlayer.IsOnline() && IPlayer.GetAdmin() >= 3
&& sscanf(command, "/kick %[a-z | A-Z | 0-9/<>|.,~*;`:!'^+%&/()=?_-£#${[]}€] %[a-z | A-Z | 0-9/<>|.,~*;`:!'^+%&/()=?_-£#${[]}€]",
&playername, &reasonkick) == 2)
But nothing happens when I use the command in game. Is the syntax wrong?
sscanf() does not support regular expressions. The way you are using the %[] placeholder is indeed wrong syntax. Try this instead:
if (IPlayer.IsOnline() &&
(IPlayer.GetAdmin() >= 3) &&
(sscanf(command, "/kick %N[^ ] %Ms", playername, reasonkick) == 2))
{
...
}
Where N and M are the max widths of the playername and reasonkick buffers, respectively, eg:
char playername[51] = {};
char reasonkick[129] = {};
if (IPlayer.IsOnline() &&
(IPlayer.GetAdmin() >= 3) &&
(sscanf(command, "/kick %50[^ ] %128s", playername, reasonkick) == 2))
{
...
}
The %N[^ ] will scan the input string until a space character is encountered or N characters have been scanned. No need to specify all of the individual characters that could be encountered. The %Ms will scan the rest of the input string until the end of the string is reached or until M characters have been scanned. If needed, you can use %M[^ ] for that parameter instead.
However, in C++, this would be better handled using std::istringstream and std::getline(), instead of sscanf(), eg:
if (IPlayer.IsOnline() && (IPlayer.GetAdmin() >= 3))
{
std::istringstream iss(command);
std::string cmd;
if (std::getline(iss, cmd, ' ') && (cmd == "/kick"))
{
std::string playername, reasonkick;
if (std::getline(iss >> std::ws, playername, ' ') &&
std::getline(iss >> std::ws, reasonkick, ' '))
{
...
}
}
}
I have a question about the following problem on Leetcode:
Given two strings S and T, return if they are equal when both are typed into empty text editors. # means a backspace character.
Example 1:
Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".
Example 2:
Input: S = "ab##", T = "c#d#"
Output: true
Explanation: Both S and T become "".
Example 3:
Input: S = "a##c", T = "#a#c"
Output: true
Explanation: Both S and T become "c".
Example 4:
Input: S = "a#c", T = "b"
Output: false
Explanation: S becomes "c" while T becomes "b".
Note:
1 <= S.length <= 200
1 <= T.length <= 200
S and T only contain lowercase letters and '#' characters.
Follow up:
Can you solve it in O(N) time and O(1) space?
My answer:
def backspace_compare(s, t)
if (s.match?(/[^#[a-z]]/) || t.match?(/[^#[a-z]]/)) || (s.length > 200 || t.length > 200)
return "fail"
else
rubular = /^[\#]+|([^\#](\g<1>)*[\#]+)/
if s.match?(/#/) && t.match?(/#/)
s.gsub(rubular, '') == t.gsub(rubular, '')
else
new_s = s.match?(/#/) ? s.gsub(rubular, '') : s
new_t = t.match?(/#/) ? t.gsub(rubular, '') : t
new_s == new_t
end
end
end
It works in the terminal and passes the given examples, but when I submit it on leetcode it tells me Time Limit Exceeded. I tried shortening it to:
rubular = /^[\#]+|([^\#](\g<1>)*[\#]+)/
new_s = s.match?(/#/) ? s.gsub(rubular, '') : s
new_t = t.match?(/#/) ? t.gsub(rubular, '') : t
new_s == new_t
But also the same error.
So far, I believe my code fulfills the O(n) time, because there are only two ternary operators, which overall is O(n). I'm making 3 assignments and one comparison, so I believe that fulfills the O(1) space complexity.
I have no clue how to proceed beyond this, been working on it for a good 2 hours..
Please point out if there are any mistakes in my code, and how I am able to fix it.
Thank you! :)
Keep in mind that with N <= 200, your problem is more likely to be linear coefficient, not algorithm complexity. O(N) space is immaterial for this; with only 400 chars total, space is not an issue. You have six regex matches, two of which are redundant. More important, regex is slow processing for such a specific application.
For speed, drop the regex stuff and do this one of the straightforward, brute-force ways: run through each string in order, applying the backspaces as appropriate. For instance, change both the backspace and the preceding letter to spaces. At the end of your checking, remove all the spaces in making a new string. Do this with both S and T; compare those for equality.
It may be easiest to start at the end of the string and work towards the beginning:
def process(str)
n = 0
str.reverse.each_char.with_object('') do |c,s|
if c == '#'
n += 1
else
n.zero? ? (s << c) : n -= 1
end
end.reverse
end
%w|ab#c ad#c ab## c#d# a##c #a#c a#c b|.each_slice(2) do |s1, s2|
puts "\"%s\" -> \"%s\", \"%s\" -> \"%s\" %s" %
[s1, process(s1), s2, process(s2), (process(s1) == process(s2)).to_s]
end
"ab#c" -> "ac", "ad#c" -> "ac" true
"ab##" -> "", "c#d#" -> "" true
"a##c" -> "c", "#a#c" -> "c" true
"a#c" -> "c", "b" -> "b" false
Let's look at a longer string.
require 'time'
alpha = ('a'..'z').to_a
#=> ["a", "b", "c",..., "z"]
s = (10**6).times.with_object('') { |_,s|
s << (rand < 0.4 ? '#' : alpha.sample) }
#=> "h####fn#fjn#hw###axm...#zv#f#bhqsgoem#glljo"
s.size
#=> 1000000
s.count('#')
#=> 398351
and see how long it takes to process.
require 'time'
start_time = Time.now
(u = process(s)).size
#=> 203301
puts (Time.now - start_time).round(2)
#=> 0.28 (seconds)
u #=> "ffewuawhfa...qsgoeglljo"
As u will be missing the 398351 pound signs in s, plus an almost equal number of other characters removed by the pound signs, we would expect u.size to be about:
10**6 - 2 * s.count('#')
#=> 203298
In fact, u.size #=> 203301, meaning that, at the end, 203301 - 203298 #=> 3 pound signs were unable to remove a character from s.
In fact, process can be simplified. I leave that as an exercise for the reader.
class Solution {
public boolean backspaceCompare(String s, String t) {
try {
Stack<Character> st1 = new Stack<>();
Stack<Character> st2 = new Stack<>();
st1 = convertToStack(s);
st2 = convertToStack(t);
if (st1.size() != st2.size()) {
return false;
} else {
int length = st1.size();
for (int i = 0; i < length; i++) {
if (st1.peek() != st2.peek())
return false;
else {
st1.pop();
st2.pop();
}
if (st1.isEmpty() && st2.isEmpty())
return true;
}
}
} catch (Exception e) {
System.out.print(e);
}
return true;
}
public Stack<Character> convertToStack(String s){
Stack<Character> st1 = new Stack<>();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != '#') {
st1.push(s.charAt(i));
} else if (st1.empty()) {
continue;
} else {
st1.pop();
}
}
return st1;
}
}
I have an Arduino web server project going. Right now I'm trying to parse url parameters. For instance, I wish to make a GET request to http://(localip)/info and then have the "info" returned and displayed.
I don't know if I've been staring at the screen too long today, but I can't seem to match a simple char which is a single space... This shouldn't be complex in the slightest! All the advice I've found do not work. Yet, I need to break the loop on a space because that is an indicator where my url parameter ends. I want to be able to use /info as well as /something else.
char *data = (char *) Ethernet::buffer + pos;
String buff;
for (int x = 5; x<15; x++) {
Serial.print(x); Serial.print(" - |"); Serial.print(data[x]); Serial.println("|");
if ((data[x] == " ") || (data[x] == char(" ")) || (strcmp(data[x], " ") == 0)) {
x = 999;
break;
} else {
buff += data[x];
}
delay(5);
}
Serial monitor output. It should break on #9 and buff should equal just "info"
5 - |i|
6 - |n|
7 - |f|
8 - |o|
9 - | |
10 - |H|
11 - |T|
12 - |T|
13 - |P|
14 - |/|
buff -> info HTTP/
You are using double quote marks (") in your char comparisons. In C++, the double quote marks are used to define strings, not characters. Characters are defined using single quotes (')
testing for a space: if (c == ' ').
Unlike strings, you can also use chars in switch statements:
switch (c)
{
case 'a': case 'A':
Serial.println("I've got an A!!");
break;
}
if (data[x] == 0x20 || (isspace(data[x])))
{
//is a white space
}
I am looking for a regular expression for a validating a password. The password rules are:
at least one lowercase letter
at least one upper case letter
at least one digit
length between 6 and 14
I created following regular expression but it's not working
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,14}$
It's accepting
qwerty1
QWERTY1
but not qwERTy
i.e. it's fulfilling only 2 conditions
at least one digit
length between 6 and 14
I'm not sure that's possible, but I'm sure that if it is, and it turns out a long complicated regex string, it's a wrong design decision. It will be unmaintainable, unclear and very error prone.
At the same time, this is easy to do, understand and maintain:
function isValid(password)
{
if(password.length < 6 || password.length > 14)
return false;
var valid = { hasLower: false, hasUpper: false, hasDigit: false };
for(var i = 0; i < password.length; i++) {
var c = password[i];
var upperC = c.toUpperCase();
valid.hasLower |= c != upperC;
valid.hasUpper |= c == upperC;
valid.hasDigit |= c >= '0' && c <= '9';
}
return valid.hasLower && valid.hasUpper && valid.hasDigit;
}
alert('"123abcDEF" valid = ' + isValid('123abcDEF'));
alert('"123 DEF" valid = ' + isValid('123 DEF'));
You can use \S instead of . for restricting spaces:
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])\S{6,14}$
^
See DEMO