I have got a code which should cipher text from my textArea with some sort of String key, after the button is pressed down. The problem is, that this method can cipher text using only one character, not the whole string. I need to have a little longer key, so string is needed here. How can I change that?
btnCipher.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent klik) {
String textToCipher = textArea.getText();
String cipherKey = textField.getText();
String cipheredText = "";
int xor;
char temp;
for (int i=0; i<textToCipher.length(); i++){
xor = textToCipher.charAt(i)^cipherKey; //error
temp = (char)xor;
cipheredText += temp;
}
textArea.setText(cipheredText);
}
});
If your strings are in UTF-8 encoding, then using exclusive or between two strings (assume equal length for a moment) may NOT produce a valid UTF-8 result (in particular, you may create a "continuation" character if you have "a" and "æ" in the same position). Look here for a deeper discusion.
What is the use-case that you are trying to handle?
Change that line to:
xor = textToCipher.charAt(i) ^ cipherKey.charAt(i % cipherKey.length);
As mentioned in this post, while String class doesn't really mind storing a string with unpaired surrogates (which is actually invalid for a UTF-16 format), you will run into trouble encoding/decoding the string in other charset and when using code point related methods.
Related
I want to split a String to a String[] array, whose elements meet following conditions.
s.getBytes(encoding).length should not exceed maxsize(int).
If I join the splitted strings with StringBuilder or + operator, the result should be exactly the original string.
The input string may have unicode characters which can have multiple bytes when encoded in e.g. UTF-8.
The desired prototype is shown below.
public static String[] SplitStringByByteLength(String src,String encoding, int maxsize)
And the testing code:
public boolean isNice(String str, String encoding, int max)
{
//boolean success=true;
StringBuilder b=new StringBuilder();
String[] splitted= SplitStringByByteLength(str,encoding,max);
for(String s: splitted)
{
if(s.getBytes(encoding).length>max)
return false;
b.append(s);
}
if(str.compareTo(b.toString()!=0)
return false;
return true;
}
Though it seems easy when the input string has only ASCII characters, the fact that it could cobtain multibyte characters makes me confused.
Thank you in advance.
Edit: I added my code impementation. (Inefficient)
public static String[] SplitStringByByteLength(String src,String encoding, int maxsize) throws UnsupportedEncodingException
{
ArrayList<String> splitted=new ArrayList<String>();
StringBuilder builder=new StringBuilder();
//int l=0;
int i=0;
while(true)
{
String tmp=builder.toString();
char c=src.charAt(i);
if(c=='\0')
break;
builder.append(c);
if(builder.toString().getBytes(encoding).length>maxsize)
{
splitted.add(new String(tmp));
builder=new StringBuilder();
}
++i;
}
return splitted.toArray(new String[splitted.size()]);
}
Is this the only way to solve this problem?
The class CharsetEncode has provision for your requirement. Extract from the Javadoc of the Encode method:
public final CoderResult encode(CharBuffer in,
ByteBuffer out,
boolean endOfInput)
Encodes as many characters as possible from the given input buffer, writing the results to the given output buffer...
In addition to reading characters from the input buffer and writing bytes to the output buffer, this method returns a CoderResult object to describe its reason for termination:
...
CoderResult.OVERFLOW indicates that there is insufficient space in the output buffer to encode any more characters. This method should be invoked again with an output buffer that has more remaining bytes. This is typically done by draining any encoded bytes from the output buffer.
A possible code could be:
public static String[] SplitStringByByteLength(String src,String encoding, int maxsize) {
Charset cs = Charset.forName(encoding);
CharsetEncoder coder = cs.newEncoder();
ByteBuffer out = ByteBuffer.allocate(maxsize); // output buffer of required size
CharBuffer in = CharBuffer.wrap(src);
List<String> ss = new ArrayList<>(); // a list to store the chunks
int pos = 0;
while(true) {
CoderResult cr = coder.encode(in, out, true); // try to encode as much as possible
int newpos = src.length() - in.length();
String s = src.substring(pos, newpos);
ss.add(s); // add what has been encoded to the list
pos = newpos; // store new input position
out.rewind(); // and rewind output buffer
if (! cr.isOverflow()) {
break; // everything has been encoded
}
}
return ss.toArray(new String[0]);
}
This will split the original string in chunks that when encoded in bytes fit as much as possible in byte arrays of the given size (assuming of course that maxsize is not ridiculously small).
The problem lies in the existence of Unicode "supplementary characters" (see Javadoc of the Character class), that take up two "character places" (a surrogate pair) in a String, and you shouldn't split your String in the middle of such a pair.
An easy approach to splitting would be to stick to the worst-case that a single Unicode code point can take at most four bytes in UTF-8, and split the string after every 99 code points (using string.offsetByCodePoints(pos, 99) ). In most cases, you won't fill the 400 bytes, but you'll be on the safe side.
Some words about code points and characters
When Java started, Unicode had less than 65536 characters, so Java decided that 16 bits were enough for a character. Later the Unicode standard exceeded the 16-bit limit, and Java had a problem: a single Unicode element (now called a "code point") no longer fit into a single Java character.
They decided to go for an encoding into 16-bit entities, being 1:1 for most usual code points, and occupying two "characters" for the exotic code points beyond the 16-bit limit (the pair built from so-called "surrogate characters" from a spare code range below 65535). So now it can happen that e.g. string.charAt(5) and string.charAt(6) must be seen in combination, as a "surrogate pair", together encoding one Unicode code point.
That's the reason why you shouldn't split a string at an arbitrary index.
To help the application programmer, the String class then got a new set of methods, working in code point units, and e.g. string.offsetByCodePoints(pos, 99) means: from the index pos, advance by 99 code points forward, giving an index that will often be pos+99 (in case the string doesn't contain anything exotic), but might be up to pos+198, if all the following string elements happen to be surrogate pairs.
Using the code-point methods, you are safe not to land in the middle of a surrogate pair.
The code below is intended to encrypt and decrypt messages input. When I encrypt and decrypt data, it sometimes works and at other times doesn't. The following is an example of the problems I am experiencing.
Encryption
Decryption
As you can see, when I try to decrypt, my program terminated and some gibberish is output into the console. What is the issue with my code?
I'm unsure if mentioning this helps, but I have Eclipse's file encoding set to UTF-8.
Please excuse any poor code. I'm still very much a beginner with Java and I'm puzzled as to why this is happening.
import java.util.Scanner;
public class Transcrypt {
static String mode = "",
msg,
key;
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while (!mode.equals("e") && !mode.equals("d")) { // Ask for mode until equal to "e" or "d"
System.out.print("Encrypt or decrypt? (e/d) ");
mode = input.nextLine().toLowerCase();
}
System.out.print("Message: "); // Ask for message
msg = input.nextLine();
System.out.print("Passkey: "); // Ask for key
key = input.nextLine();
input.close();
System.out.println(transcrypt(msg, key, mode.equals("d"))); // Transcrypt and output
}
public static String transcrypt(String msg, String key, boolean decode) {
String result = "";
for (int i=0; i<msg.length(); i++) {
// Add or subtract Unicode index of key.charAt(i % key.length()) and/from msg.msg.charAt(i) and convert back to character
result += (char) ((int) msg.charAt(i) + ((int) key.charAt(i % key.length())) * (decode ? -1 : 1));
}
return result;
}
}
Your encoded message looks like "¼ÊßàãÊãæÑ×", but actually, it is "¼Êßàã\u0085ÊãæÑ×\u0095".
Most notably, it contains the control character 0x85 in-between which has “new line” semantics. So when copying that string, you’re copying the control characters with it and when pasting into the console upon your application’s query for the message, you are basically entering ¼Êßàã as message, the input committed via the “new line” control character, causing the subsequent query for the password to consume the trailing ÊãæÑ× characters.
The garbage you see right after the Passkey: output, is the result of your attempt to decode ¼Êßàã using the key ÊãæÑ×, as there was no newline entered at this point as the characters being already in the console’s buffer have been used.
Generally, as already said by Nándor Előd Fekete in this comment, you should not write characters to the console, that are actually binary data, like the encoded string.
By the way, you shouldn’t declare variables as static fields that are actually local to a method, i.e. your main method. Further, you don’t need to cast char to int when doing computations. char values are already a subset of the int values.
User enter code word and text to encrypt and program should put XOR mask - code on text - and turn back to normal , but it just put on mask and dont turn back to normal look , why ?
public void onClick(View arg0) {
code = etCode.getText().toString();
text = etText.getText().toString();
while(code.length()<text.length()){
code+=code;
}
char[] Ccode = code.toCharArray();
char[] Ctext = text.toCharArray();
for(i=0;i<Ctext.length;i++){
Ctext[i]^=Ccode[i];
}
rezult=Ctext.toString();
for(i=0;i<Ctext.length;i++){
Ctext[i]^=Ccode[i];
}
rezult+="\n";
rezult+=Ctext.toString();
tvMain.setText(rezult);
}
});
if I enter code : code , text : text
it shows:
[C#40527808
[C#40527808
You output the address of the array. You want the content.
Arrays have not useful toString() mwthod.
change
rezult=Ctext.toString();
to
rezult=new String(Ctext);
same for
rezult+=Ctext.toString();
=>
rezult+=new String(Ctext);
You cannot use toString() to convert a char array to String.
Use tvMain.setText(new String(Ctext));
In Java, the default toString() operation on arrays is to convert to an internal identifier, which is what you're seeing. Try using:
rezult = new String(Ctext);
...
rezult += new String(Ctext);
or, depending on what you want to display (since it's not clear to me that Ctext always contains displayable characters):
rezult = Arrays.toString(Ctext);
...
rezult += Arrays.toString(Ctext);
This will give you a comma-separated array of character values, surrounded by square brackets.
I have sort of a funky question (that I hope hasn't been asked and answered yet). To start, I'll tell you the order of what I'm trying to do and how I'm doing it and then tell you where I'm having a problem:
Convert a string of characters into ASCII numbers
Convert those ASCII numbers into binary and store them in a string
Convert those binary numbers back into ASCII numbers
Convert the ASCII numbers back into normal characters
Here are the methods I've written so far:
public static String strToBinary(String inputString){
int[] ASCIIHolder = new int[inputString.length()];
//Storing ASCII representation of characters in array of ints
for(int index = 0; index < inputString.length(); index++){
ASCIIHolder[index] = (int)inputString.charAt(index);
}
StringBuffer binaryStringBuffer = new StringBuffer();
/* Now appending values of ASCIIHolder to binaryStringBuffer using
* Integer.toBinaryString in a for loop. Should not get an out of bounds
* exception because more than 1 element will be added to StringBuffer
* each iteration.
*/
for(int index =0;index <inputString.length();index ++){
binaryStringBuffer.append(Integer.toBinaryString
(ASCIIHolder[index]));
}
String binaryToBeReturned = binaryStringBuffer.toString();
binaryToBeReturned.replace(" ", "");
return binaryToBeReturned;
}
public static String binaryToString(String binaryString){
int charCode = Integer.parseInt(binaryString, 2);
String returnString = new Character((char)charCode).toString();
return returnString;
}
I'm getting a NumberFormatException when I run the code and I think it's because the program is trying to convert the binary digits as one entire binary number rather than as separate letters. Based on what you see here, is there a better way to do this overall and/or how can I tell the computer to recognize the ASCII characters when it's iterating through the binary code? Hope that's clear and if not I'll be checking for comments.
So I used OP's code with some modifications and it works really well for me.
I'll post it here for future people. I don't think OP needs it anymore because he probably figured it out in the past 2 years.
public class Convert
{
public String strToBinary(String inputString){
int[] ASCIIHolder = new int[inputString.length()];
//Storing ASCII representation of characters in array of ints
for(int index = 0; index < inputString.length(); index++){
ASCIIHolder[index] = (int)inputString.charAt(index);
}
StringBuffer binaryStringBuffer = new StringBuffer();
/* Now appending values of ASCIIHolder to binaryStringBuffer using
* Integer.toBinaryString in a for loop. Should not get an out of bounds
* exception because more than 1 element will be added to StringBuffer
* each iteration.
*/
for(int index =0;index <inputString.length();index ++){
binaryStringBuffer.append(Integer.toBinaryString
(ASCIIHolder[index]));
}
String binaryToBeReturned = binaryStringBuffer.toString();
binaryToBeReturned.replace(" ", "");
return binaryToBeReturned;
}
public String binaryToString(String binaryString){
String returnString = "";
int charCode;
for(int i = 0; i < binaryString.length(); i+=7)
{
charCode = Integer.parseInt(binaryString.substring(i, i+7), 2);
String returnChar = new Character((char)charCode).toString();
returnString += returnChar;
}
return returnString;
}
}
I'd like to thank OP for writing most of it out for me. Fixing errors is much easier than writing new code.
You've got at least two problems here:
You're just concatenating the binary strings, with no separators. So if you had "1100" and then "0011" you'd get "11000011" which is the same result as if you had "1" followed by "1000011".
You're calling String.replace and ignoring the return result. This sort of doesn't matter as you're replacing spaces, and there won't be any spaces anyway... but there should be!
Of course you don't have to use separators - but if you don't, you need to make sure that you include all 16 bits of each UTF-16 code point. (Or validate that your string only uses a limited range of characters and go down to an appropriate number of bits, e.g. 8 bits for ISO-8859-1 or 7 bits for ASCII.)
(I have to wonder what the point of all of this is. Homework? I can't see this being useful in real life.)
I am installing alarms, but they have an interface that I have to give name to various zones. The problem is that they can only accept greek characters that does not exist in english language else they have to be the English equivelant.
For example if I write "ΠΑΡΑΘΥΡΟ", the characthers 1,2,3,5,6,7 must enter in english because are the same with the greek ones in appearence. But chars 0 and 4 only must be in Greek.
I care only for capitals.
Any idea on how to do it with 2 simple jtextfields ?
Thank you!
Use a HashMap to translate characters. Since the problem domain is small and will probably never change, it's justifiable to hard-code the content of the map, like so:
private static final Map<Character, Character> GREEK_TO_ROMAN = new HashMap<>();
static {
GREEK_TO_ROMAN.put('\u0391', '\u0041'); // uppercase alpha
GREEK_TO_ROMAN.put('\u03A1', '\u0050'); // uppercase rho
// ...
}
Then get the input string's character array, translate characters as needed, and create a new String from the changed array:
String s = "ΠΑΡΑΘΥΡΟ";
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
Character repl = GREEK_TO_ROMAN.get(chars[i]);
if (repl != null)
chars[i] = repl;
}
s = new String(chars);
How JTextField would come into play I don't quite see, but maybe if you want you can subclass it, overwrite the getText() method and make sure that any String it yields is already converted.