I want to check if an IP address is in a certain range, matching by "*" only. For example, "202.121.189.8" is in "202.121.189.*".
The scenario is that I have a list of banned IPs, some of them contains "*", so I wrote a function, it works fine so far:
static bool IsInRange(string ip, List<string> ipList)
{
if (ipList.Contains(ip))
{
return true;
}
var ipSets = ip.Split('.');
foreach (var item in ipList)
{
var itemSets = item.Split('.');
for (int i = 0; i < 4; i++)
{
if (itemSets[i] == "*")
{
bool isMatch = true;
for (int j = 0; j < i; j++)
{
if (ipSets[i - j - 1] != itemSets[i - j - 1])
{
isMatch = false;
}
}
if (isMatch)
{
return true;
}
}
}
}
return false;
}
Test code:
string ip = "202.121.189.8";
List<string> ipList = new List<string>() { "202.121.168.25", "202.121.189.*" };
Console.WriteLine(IsInRange(ip, ipList));
But I think what i wrote is very stupid, and I want to optimize it, does anyone have an idea how to simplify this function? not to use so many "for....if...".
A good idea would be to represent the banned subnets in a form of a pair: mask + base address. So your check will look like that:
banned = (ip & mask == baseaddress & mask);
For 11.22.33.* the base address will be 11*0x1000000 + 22*0x10000 + 33*0x100, mask will be 0xffffff00.
For single address 55.44.33.22 the address will be 55*0x1000000 + 44*0x10000 * 33*0x100 + 22, mask will be 0xffffffff.
You'll need to convert the address to a 32-bit int as a separate procedure.
After that all, your code will look like that:
int numip = ip2int(ip);
bool isIpBanned = banList.Any(item =>
numip & item.mask == item.baseaddress & item.mask);
By the way, this way you'll be able to represent even bans on smaller subsets.
int ip2int(string ip) // error checking omitted
{
var parts = ip.Split('.');
int result = 0;
foreach (var p in parts)
result = result * 0x100 + int.Parse(p);
}
class BanItem { public int baseaddres; public int mask; }
BanItem ip2banItem(string ip)
{
BanItem bi = new BanItem() { baseaddres = 0, mask = 0 };
var parts = ip.Split('.');
foreach (var p in parts)
{
bi.baseaddress *= 0x100;
bi.mask *= 0x100;
if (p != "*")
{
bi.mask += 0xff;
bi.baseaddress += int.Parse(p);
}
}
return bi;
}
banList = banIps.Select(ip2banItem).ToList();
I think you should keep a separate list for IP with * and those without asterick.
say IpList1 contains IP's without *
and
IpList2 --those contain * ..actually what we will be storing is the part before .* in this list. for e.g. 202.121.189.* would be stored as 202.121.189 only..
Thus for a given IP addrerss you just need to check for that IP address in IpList1,if it is not found over there then
for each Ip in IPList 2 you need to check whether it is a substring of input IP or not.
Thus no requirement of complex for and if loops.
Written In Java (Untested):
static boolean IsInRange(String ip, Vector<String> ipList) {
int indexOfStar = 0;
for (int i=0; i<ipList.size(); i++) {
if (ipList.contains("*")) {
indexOfStar = ipList.indexOf("*");
if ((ip.substring(0, indexOfStar)).equals(ipList.get(i).substring(0, indexOfStar))) {
return true;
}
}
}
return false;
}
I would use a space filling curve like in the xkcd comic: http://xkcd.com/195/. It's the function H(x,y) = (H(x),H(y)) and it reduces the 2 dimension to 1 dimension. It would also show that you are a real b*** coder.
Related
This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 8 years ago.
I have to make a program that interacts with the user interface that ask a user for a number and a base to convert to binary, hex, or octo. I made a program that works but when the user types in "0 0", it is suppose to terminate and end the program. My program on the other hand doesn't do that but keep going in the while loop.
Here is the code:
import java.util.*; //for Scanner
public class BaseConversionApp {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
String combo = numbers(console);
while(combo != "0 0") {
if(combo.length() > 0) { //breaks the string into integers to do math to it
Scanner s = new Scanner(combo);
int count = s.nextInt();
int countSecond = s.nextInt();
s.close();
conversion(count,countSecond);
System.out.println();
//now if it goes 0 0 or
String again = numbers(console);
// conversion(count,countSecond);
}
}
//prompts the user for two numbers and checks if the bases are 16, 8, 2.
public static String numbers(Scanner console) {
String combination = "";
String nothing = "0 0";
System.out.print("Enter an integer and the new base: ");
int integer = console.nextInt();
int base = console.nextInt();
if(base == 16 || base == 2 || base == 8) {
combination = integer + " " + base;
return combination;
} else if (base == 0 && integer == 0){
System.out.println("Good bye!");
return nothing;
} else {
System.out.println("Sorry, that is an invalid base. Please enter 2, 8, or 16
only.");
}
return "";
}
//converts the integers into binary, hexa, or octo.
public static void conversion (int integer, int base) {
//takes cares of the special case if the user wants to know hexidecimal
if(base <= 16) {
String calculations = Integer.toString(integer, base);
if(integer > 0 && base > 0) {
System.out.println(integer + " in binary -> " + Integer.toString(integer,
base));
}
}
}
}
You can't compare strings like that, you have to use the String object's equals() method. So, you should have:
while(!"0 0".equals(combo)) {
...
}
Notice that I've put the constant "0 0" first -- that protects you against combo being null. Otherwise you'd have !combo.equals("0 0"), and if combo were null you'd get a NullPointerException when you try to call equals() on a null value.
Try this code instead. Yours looks complicated. Btw, your code works for me.
import java.util.Scanner;
public class NewClass {
static Scanner inp = new Scanner(System.in);
static String line1 = "";
static String line2 = "";
static String exit = "exit";
public static void main(String[] args) {
System.out.println("Enter int first and then base...");
System.out
.println("Enter the word exit to exit!");
while (true) {
line1 = inp.next();
if (line1.equalsIgnoreCase(exit)) {
break;
}
line2 = inp.next();
try {
conversion(Integer.parseInt(line1), Integer.parseInt(line2));
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
System.out.println("Bye...");
}
public static void conversion(int integer, int base) {
// takes cares of the special case if the user wants to know hexadecimal
if (base <= 16) {
String calculations = Integer.toString(integer, base);
if (integer > 0 && base > 0) {
System.out.println(integer + " in binary -> "
+ Integer.toString(integer, base));
}
}
}
}
is it possible to use a Kleene Operator (Kleene Star) for the Formatters?
I want to use a phoneFormatter, which puts a minus after the 5th number and afterwards it should be possible to have a variable number of numbers.
E.g.: 0172-555666999, 0160-44552 etc.
That is how I started, but I don't know which character belongs after the last hash (it is not a star, I already tried it ;-) ):
<fx:Declarations>
<mx:PhoneFormatter id="mPhoneFormat"
formatString="####-#"/>
</fx:Declarations>
The default PhoneFormatter expects the input string to have the same number of characters as the format string. They don't support regular expression patterns (like * to match the element zero or more times).
However, it's pretty easy to make your own formatter. To do this, I extended the PhoneFormatter class and overrode its format() method. I copied and pasted the original format() method and made the following modifications:
comment out the code that compared the length of the source string with the length of the format string
compare the length of the formatted string. If the original string is longer, append the remaining chars from the original string to the formatted string.
This probably won't handle all of your use cases, but it should be pretty straightforward to modify this to your needs.
package
{
import mx.formatters.PhoneFormatter;
import mx.formatters.SwitchSymbolFormatter;
public class CustomPhoneNumberFormatter extends PhoneFormatter
{
public function CustomPhoneNumberFormatter()
{
super();
}
override public function format(value:Object):String
{
// Reset any previous errors.
if (error)
error = null;
// --value--
if (!value || String(value).length == 0 || isNaN(Number(value)))
{
error = defaultInvalidValueError;
return "";
}
// --length--
var fStrLen:int = 0;
var letter:String;
var n:int;
var i:int;
n = formatString.length;
for (i = 0; i < n; i++)
{
letter = formatString.charAt(i);
if (letter == "#")
{
fStrLen++;
}
else if (validPatternChars.indexOf(letter) == -1)
{
error = defaultInvalidFormatError;
return "";
}
}
// if (String(value).length != fStrLen)
// {
// error = defaultInvalidValueError;
// return "";
// }
// --format--
var fStr:String = formatString;
if (fStrLen == 7 && areaCode != -1)
{
var aCodeLen:int = 0;
n = areaCodeFormat.length;
for (i = 0; i < n; i++)
{
if (areaCodeFormat.charAt(i) == "#")
aCodeLen++;
}
if (aCodeLen == 3 && String(areaCode).length == 3)
{
fStr = String(areaCodeFormat).concat(fStr);
value = String(areaCode).concat(value);
}
}
var dataFormatter:SwitchSymbolFormatter = new SwitchSymbolFormatter();
var source:String = String(value);
var returnValue:String = dataFormatter.formatValue(fStr, value);
if (source.length > returnValue.length)
{
returnValue = returnValue + source.substr(returnValue.length-1);
}
return returnValue;
}
}
}
Hey guys I'm having a huge problem when Encrypting a message in an older phone in comparison with the newer ones.
I've compiled the code to run on older hardware (CLDC1.0, MIDP2.0), and for some reason, when I do a TEA Encryption in a Nokia N70 I end up having one ruined character when it goes from plain-text to TEA. (yes I know, from a lot of chars only that one little char gets ruined...)
When I run exactly the same app on the N8 and other more recent phones however I get it encrypting correctly.
before I post the code however here's a small explanation on what it does:
basically it receives a String and a boolean inputs, the boolean states if it's for encryption or decryption purposes, whilst the string is what I want to encode or decode.
from there, I basically strip the String into a byte array, treat it accordingly (if for encrypt or decrypt) and later turn it into a String, which I then return (decrypt) or I encode in Base64 (encrypt).
The reason to encapsulate in Base64 is so it can be sent by sms, since this encoding uses non-special characters it keeps the sms limit up to 160 characters, which is desirable for the app.
now for the code:
private String HandleTEA(String input, boolean aIsEncryption) throws UnsupportedEncodingException
{
System.out.println(input);
String returnable = "";
try
{
TEAEngine e = new TEAEngine();
if (aIsEncryption)
{
e.init(true, TEAkey);
}
else
{
if(getDebug())
{
input = input.substring(1);
}
input = base64.decodeString(input);
e.init(false, TEAkey);
}
byte[] aData = input.getBytes("ISO-8859-1");
byte[] textToUse = aData;
int len = ((textToUse.length + 16 - 1) / 16) * 16;
byte[] secondUse = new byte[len];
for(int i = 0; i < textToUse.length; i++)
{
secondUse[i] = textToUse[i];
}
for(int i = textToUse.length; i < secondUse.length; i++)
{
secondUse[i] = 0;
}
int blockSize = e.getBlockSize();
byte[] outBytes = new byte[secondUse.length];
for (int chunkPosition = 0; chunkPosition < secondUse.length; chunkPosition += blockSize)
{
int chunkSize = Math.min(blockSize, (textToUse.length - (chunkPosition * blockSize)));
e.processBlock(secondUse, chunkPosition, outBytes, chunkPosition);
}
if(aIsEncryption)
{
Baseless = new String(outBytes, "ISO-8859-1");
String encodedString = base64.encodeString(Baseless);
char[] theChars = new char[encodedString.length()+1];
for(int i = 0; i < theChars.length; i++)
{
if(i == 0)
{
theChars[i] = '1';
}
else
{
theChars[i] = encodedString.charAt(i-1);
}
}
byte[] treating = new byte[theChars.length];
for(int i = 0; i < theChars.length; i++)
{
treating[i] = (byte)theChars[i];
}
returnable = new String(treating, "ISO-8859-1");
}
else
{
char[] theChars = new String(outBytes, "ISO-8859-1").toCharArray();
String fixed ="";
for(int i = 0; i < theChars.length; i++)
{
char c = theChars[i];
if (c > 0) fixed = fixed + c;
}
returnable = fixed;
}
}
catch(Exception e)
{
e.printStackTrace();
}
return returnable;
}
Anyone have any idea on what might be happening?
for comparison this is what I'm getting from the N70:
e+TgV/fU5RUOYocMRfG7vqpQT+jKlujU6eIzZfEjGhXdFwNB46wYNSiUj5H/tWbta26No6wjQylgTexhS6uqyw==
and from the N8:
e+TgV/fU5RUOYocMRfG7vqpQT+jKlujU6eIzZfEjgBXdFwNB46wYNSiUj5H/tWbta26No6wjQylgTexhS6uqyw==
as you can see everything looks similar, but in the middle of the code what gets encoded as Gh on the N70 shows up as gB on the N8...
when decrypting the data encrypted by the N70 we get some really weird chars:
will add this here tomorrow since I don't have the saved output with me
both are using the same key (in real-life tho they'll be using a key that's randomly generated on startup)
here's the key used:
0b1b5e0167aaee06
Hope you can help me out with this and Thanks for your interest and assistance!
your code is hard to understand, but Baseless = new String(outBytes, "ISO-8859-1"); and any similar constructs are almost certainly incorrect. Why do you want to make a String out of cipher? Just base64 encode outBytes directly.
I must display very small values (capacitor) in a Flex-AdvancedDataGrid
I use spark.formatters.NumberFormatter.
If I use 3, 6 or 9 for fractionalDigits, everything is fine.
But if I use 12, because I need 12 digits after decimal separator, then the value is cut after 9 digits!
Is there a way to get more then 9 digits after separator.
Or is there a way to use a formatting like "4.7 E-12" (Must be E-9, E-12, E-15 and so on)
toPrecision and toFixed works fine up to 20 digits. Thats enough.
I will write a function on this base to get results like 4.7 E-12.
Thanks for the help
Jan
I created a custom pattern formatter class that allows you to specify how you want a string/number formatted using whatever symbols or structure you want. Depending on your requirements, this may help you. Feel free to modify this class as needed.
CustomPatternFormatter.as
package
{
import mx.formatters.Formatter;
public class CustomPatternFormatter extends Formatter
{
private static const VALID_PATTERN_CHARACTERS:String = "#,.-";
// Use # as a placeholder for a regular
// character in the input string.
// Then add other characters between the
// # symbol for the desired output format.
// ex. The pattern ##,##,##.## with input 12345678
// will output 12,34,56.78
public var formatPattern:String;
// If True, the input string must match the number
// of # characters in the formatPattern.
public var inputMustMatchPatternLength:Boolean;
//Constructor
public function CustomPatternFormatter()
{
super();
formatPattern = "";
inputMustMatchPatternLength = false;
}
// Override format().
override public function format(value:Object):String
{
// Reset error if it exists.
if (error)
error = null;
// If value is null, or empty String just return ""
// but treat it as an error for consistency.
// Users will ignore it anyway.
if (!value || (value is String && value == ""))
{
error = "Cannot convert an empty value";
return "";
}
// Check to see if the input value must match the format pattern
if (inputMustMatchPatternLength && String(value).length != countOccurrences(formatPattern, "#"))
{
error = "The input value length does not match the format pattern length.";
return "";
}
// If the value is valid, format the string.
var fStrLen:int = 0;
var letter:String;
var n:int;
var i:int;
var v:int;
// Make sure the formatString is valid.
n = formatPattern.length;
for (i = 0; i < n; i++)
{
letter = formatPattern.charAt(i);
if (letter == "#")
{
fStrLen++;
}
else if (VALID_PATTERN_CHARACTERS.indexOf(letter) == -1)
{
error = "You can only use the following symbols in the formatPattern: " + VALID_PATTERN_CHARACTERS;
return "";
}
}
var returnString:String = "";
var vStr:String = String(value).replace(".", "").split("").reverse().join("");
var fArr:Array = formatPattern.split("").reverse();
var fChar:String;
// Format the string
for (v = 0; v < vStr.length; v++) { if (fArr.length > 0)
{
do
{
fChar = fArr.shift();
if (fChar != "#")
returnString += fChar;
} while (fChar != "#" && fArr.length > 0);
}
returnString += vStr.charAt(v);
}
// Return the formatted string
return returnString.split("").reverse().join("");
}
protected function countOccurrences(str:String, char:String):int
{
var count:int = 0;
for (var i:int=0; i < str.length; i++)
{
if (str.charAt(i) == char)
{
count++;
}
}
return count;
}
}
}
I have some issues with Arduino about how to match text.
I have:
String tmp = +CLIP: "+37011111111",145,"",,"",0
And I am trying to match:
if (tmp.startsWith("+CLIP:")) {
mySerial.println("ATH0");
}
But this is not working, and I have no idea why.
I tried substring, but the result is the same. I don't know how to use it or nothing happens.
Where is the error?
bool Contains(String s, String search) {
int max = s.length() - search.length();
for (int i = 0; i <= max; i++) {
if (s.substring(i) == search) return true; // or i
}
return false; //or -1
}
Otherwise you could simply do:
if (readString.indexOf("+CLIP:") >=0)
I'd also recommend visiting:
https://www.arduino.cc/en/Reference/String
I modified the code from gotnull. Thanks to him to put me on the track.
I just limited the search string, otherwise the substring function was not returning always the correct answer (when substrign was not ending the string). Because substring search always to the end of the string.
int StringContains(String s, String search) {
int max = s.length() - search.length();
int lgsearch = search.length();
for (int i = 0; i <= max; i++) {
if (s.substring(i, i + lgsearch) == search) return i;
}
return -1;
}
//+CLIP: "43660417XXXX",145,"",0,"",0
if (strstr(command.c_str(), "+CLIP:")) { //Someone is calling
GSM.print(F("ATA\n\r"));
Number = command.substring(command.indexOf('"') + 1);
Number = Number.substring(0, Number.indexOf('"'));
//Serial.println(Number);
} //End of if +CLIP:
This is how I'm doing it. Hope it helps.
if (tmp.startsWith(String("+CLIP:"))) {
mySerial.println("ATH0");
}
You can't put the string with quotes only you need to cast the variable :)