Member 13958412 Ответов: 1

Преобразование Юникода Java


У меня возникла проблема с преобразованием Юникода. Главная проблема заключалась в том, что передаваемый Строковый параметр был чем-то вроде неподдерживаемых символов.

Вот пример кода:

У меня есть пакетный скрипт, который будет выполняться на сервере.
Тем временем должен быть предоставлен журнал состояния выполнения. Этот скрипт содержит некоторые японские символы, как указано ниже, которые на самом деле должны быть отображены так, как они есть в журнале. Из-за чего у меня возникли проблемы.

При этом самая верхняя строка "String command = sshcommand.getCommand();" содержит некоторую "удаленную команду с параметрами в виде строки".

Что я уже пробовал:

package jp.co.kentaku.kikan.util.ssh;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.csc.qre.iseries.cl.CallCommand.ParamKey;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;


/**
 * SSH Handler for ssh communication with servers Linux as well as Windows
 * @author ssharma365
 *
 */
public class SSHHandler {
	/**
	 * Enabling logger for this class
	 * 
	 */
	private static final Logger logger = LoggerFactory.getLogger(SSHHandler.class);

	/**
	 * Jsch channel
	 */
	private JSch jschSSHChannel;
	/**
	 * Remote machine User name
	 */
	private String strUserName;
	/**
	 * Remote machine server name or IP
	 */
	private String strConnectionIP;
	/**
	 * The port on which the remote machine will open the ssh communiction. by default 22
	 */
	private int intConnectionPort;
	/**
	 * Remote password for the above user
	 */
	private String strPassword;
	/*
	 * SSH session reference
	 */
	private Session sesConnection;
	/**
	 *  Time out 
	 */
	private int intTimeOut;

	private void doCommonConstructorActions(String userName, String password, String connectionIP,
			String knownHostsFileName) {
		jschSSHChannel = new JSch();

		try {
			jschSSHChannel.setKnownHosts(knownHostsFileName);
		} catch (JSchException jschX) {
			logError(jschX.getMessage());
		}

		strUserName = userName;
		strPassword = password;
		strConnectionIP = connectionIP;
	}

	public SSHHandler(String userName, String password, String connectionIP, String knownHostsFileName) {
		doCommonConstructorActions(userName, password, connectionIP, knownHostsFileName);
		intConnectionPort = 22;
		intTimeOut = 60000;
	}

	
	public String connect() throws Exception {
		String errorMessage = null;

		
			sesConnection = jschSSHChannel.getSession(strUserName, strConnectionIP, intConnectionPort);
			sesConnection.setPassword(strPassword);
	
			sesConnection.setConfig("StrictHostKeyChecking", "no");
			sesConnection.connect(intTimeOut);
		
		return errorMessage;
	}

	private String logError(String errorMessage) {
		if (errorMessage != null) {
			logger.error(strConnectionIP + ":" + intConnectionPort + " - " + errorMessage);
			}
		return errorMessage;
	}

	private String logWarning(String warnMessage) {
		if (warnMessage != null) {
			logger.error(strConnectionIP + ":" + intConnectionPort + " - " + warnMessage);
			}
		return warnMessage;
	}

	public String sendCommand(String command) throws Exception {
		String errorMsg = null;
		StringBuilder outputBuffer = new StringBuilder();

		
			Channel channel = sesConnection.openChannel("exec");
			((ChannelExec) channel).setCommand(command);
			InputStream commandOutput = channel.getInputStream();
			InputStream commandErrorOutput = ((ChannelExec) channel).getErrStream();
			channel.connect();
			int readByte = commandOutput.read();

			while (readByte != 0xffffffff) {
				outputBuffer.append((char) readByte);
				readByte = commandOutput.read();
			}

			int readErrorByte = commandErrorOutput.read();
			boolean error = false;
			while (readErrorByte != 0xffffffff) {
				error = true;
				outputBuffer.append((char) readErrorByte);
				readErrorByte = commandErrorOutput.read();
			}
			channel.disconnect();
			int exitStatus = channel.getExitStatus();
			if (error) {
				errorMsg = outputBuffer.toString();
				logger.error("Exception occured while executing the commnad:(" + command + ") exitStatus received:"
						+ exitStatus + " Message:" + errorMsg);
				logger.error("Message Id:CPF91CB Message:" + errorMsg);
				//throw new Cpf91cb("CPF91CB", errorMsg);
				throw new Exception(errorMsg);
			}
		

		return outputBuffer.toString();
	}

	public void close() {
		sesConnection.disconnect();
	}

	public static void executeSSHCommand(SSHCommand sshcommand) throws Exception {
		logger.info("Entering the executeSSHCommand()");
		/**
		 * YOU MUST CHANGE THE FOLLOWING FILE_NAME: A FILE IN THE DIRECTORY USER: LOGIN
		 * USER NAME PASSWORD: PASSWORD FOR THAT USER HOST: IP ADDRESS OF THE SSH SERVER
		 **/
		String command = sshcommand.getCommand();
												
		String userName = sshcommand.getUserName();
		String password = sshcommand.getPassword();
		String connectionIP = sshcommand.getServerName();
		String ccsid = sshcommand.getCcsid();
		logger.info("Attempting connection to Server:" + connectionIP + " user:" + userName + " using password:"
				+ password);
		
		SSHHandler instance = new SSHHandler(userName, password, connectionIP, "");
		String errorMessage = "";
		try {
			errorMessage = instance.connect();
			logger.info("Established connection to Server:" + connectionIP + " user:" + userName + " using password:"
					+ password);
		} catch (Exception e) {
			logger.error("Message ID : CPF91CB ; Message: Could not connect to Server:" + connectionIP + " user:"
					+ userName + " using password:" + password);
			//throw new Cpf91cb("CPF91CB", "Could not connect to the server :" + connectionIP + " with User:" + userName,e);
			throw e;
		}
		if (errorMessage != null) {
			logger.error("Could not connect to the server :" + connectionIP + " with User:" + userName + " message:"
					+ errorMessage);
			/*throw new Cpf91cb("CPF91CB", "Could not connect to the server :" + connectionIP + " with User:"
					+ userName + " message:" + errorMessage, new Exception(errorMessage));*/
			throw new Exception(errorMessage);
		}

		// call sendCommand for each command and the output
		// (without prompts) is returned
		String result = null;
		try {
			logger.info("Trying to execute command" + command + " on " + connectionIP);
			result = instance.sendCommand(command);
			logger.info("Executed command :" + command + " on Server:" + connectionIP + " user:" + userName
					+ " using password:" + password);
			logger.info("CHARSET found=" + ccsid);
			if (ccsid != null && ccsid.equals("943")) {
				logger.info("Lined up for conversion");
				result = convertUTF8ToShiftJ(result);
			}
			logger.info("Command output : " + result);
		} catch (Exception e) {
			errorMessage = "Exception while executing the command:" + command + " on Server:" + connectionIP;
			errorMessage = errorMessage + " message:" + e.getMessage();
			// e.printStackTrace();
			logger.error(errorMessage);
			/*throw new Cpf91cb("CPF91CB", "Could not connect to the server :" + connectionIP + " with User:"
					+ userName + " message:" + errorMessage,new Exception(errorMessage));*/
			throw e;
		}
		// close only after all commands are sent
		instance.close();
	}

	private static String convertUTF8ToShiftJ(String uft8Str) {
		String shftJStr = null;
		try {
			
			byte[] b = uft8Str.getBytes(UTF_8);
			shftJStr = new String(b, Charset.forName("SHIFT-JIS"));
			logger.info("Converted to the string :" + shftJStr);
			System.out.println(shftJStr);
		} catch (Exception e) {
			e.printStackTrace();
			return uft8Str;
		}
		return shftJStr;
	}

	/**
	 * Get parameter value.
	 * 
	 * @param params
	 *            set of parameters
	 * @param paramKey
	 *            parameter key to retrieve value
	 * @param defaultValue
	 *            default value if value is null or not found
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getParam(final Map<ParamKey, Object> params, final ParamKey paramKey, final T defaultValue) {
		if (params == null) {
			return defaultValue;
		}

		T value = (T) params.get(paramKey);
		if (value == null) {
			return defaultValue;
		}

		return value;
	}

	/**
	 * Get parameter value.
	 * 
	 * @param params
	 *            set of parameters
	 * @param paramKey
	 *            parameter key to retrieve value
	 * @param defaultValue
	 *            default value if value is null or not found
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getParam(final Map<ParamKey, Object> params, final ParamKey paramKey) {
		if (params == null) {
			return null;
		}

		return (T) params.get(paramKey);
	}

}

Выход:
*** UX0025.SH ツ開ツ始ツ ツ(startedツ)
*** UX0025.SH ツ偲?ツ行ツ陳?ツ(executing...ツ)
*** UX0025.SH ツ終ツ猟ケツ ツ(endedツ ツ)


Но на самом деле так и должно быть:
*** UX0025.SH 開始 (started)
*** UX0025.SH 実行中(executing...)
*** UX0025.SH 終了 (ended )


Проблема, по-видимому, связана со строковым параметром "uft8Str", который содержит некоторые неподдерживаемые символы. Так что кто-нибудь, пожалуйста, помогите мне с этим.
Спасибо.

Richard MacCutchan

Ваш код не соответствует выходным данным, которые вы показываете, и не дает никакой информации о входных данных.

Member 13958412

Привет, я попытался изменить свой вопрос. Надеюсь, это немного прояснит ситуацию.

Jochen Arndt

Я не понимаю, почему вы хотите преобразовать строку Unicode в многобайтовую строку, используя определенную кодовую страницу. Это должно быть необходимо только в том случае, если вы должны передать его с этой конкретной кодовой страницей. Но все, что вы делаете, это передаете его лесорубу. Это обычно преобразует входные данные в кодировку, используемую внутри регистратора, которая, скорее всего, является Юникодом.

Другой сценарий состоял бы в том, чтобы иметь многобайтовую строку с определенной кодировкой в качестве входных данных. Затем преобразуйте его в Юникод для дальнейшей обработки.

Если вы преобразуете строку Unicode в кодовую страницу без Unicode / CCSID, то все символы, не поддерживаемые этой кодовой страницей, будут потеряны.

Member 13958412

Хорошо, как вы уже упомянули, я не прохожу через него, вместо этого, если я сделаю [String shftJStr = new String(uft8Str.getBytes());] то я получу результат как:
*** UX0025.SH �Дж�Н�@�istarted�Дж
*** UX0025.SH �À�с���iexecuting...�Дж
*** UX0025.SH �я�1�@�iended�@�Дж

Jochen Arndt

А что произойдет, если вы зарегистрируете результат как есть, не применяя никаких преобразований?

Member 13958412

Без преобразования также кажется, что такой же выход, как и выше.

Jochen Arndt

Проблема возникает в вашей функции sendCommand (), поскольку она не возвращает допустимую строку Java в кодировке UTF-16.

Я напишу ответ.

Member 13958412

Спасибо. Это будет полезно.

Member 13958412

Привет, извините, теперь я вышел из зоны доступа к серверу. Так что реализовать его я смогу только в понедельник. Тогда я вернусь к тебе.

Member 13958412

Привет, я попытался реализовать логику байтового буфера.
Вместо "результате = экземпляр.sendCommand(команда);", если я реализую "результат строка = новая строка(буфера, 0, rxCount, "в формате utf8");", то будет ошибка удаленного выполнения. Может быть, реализация была неправильной или что-то еще!!

Jochen Arndt

Вы должны реализовать его в своей функции sendCommand (), используя байтовый буфер вместо StringBuilder.

Member 13958412

Привет! К сожалению, я старался изо всех сил, но у меня ничего не вышло. Во-первых, utf8 не поддерживался, поэтому я переключил его на 16. Но все равно вывод не показывает правильный.

Jochen Arndt

Сначала вы должны знать, какая кодировка используется сервисом (отправка по сети), но UTF-8 является обычным явлением. Затем вы должны преобразовать его в строку Java, которая внутренне является UTF-16. UTF-8 поддерживается Java. Если он не работает, он также может быть получен в вашем получающем коде.

Снова:
То, что вы получаете, - это байты. Всегда. Значение байтов определяется используемым протоколом. С SSH это текст, который обычно кодируется UTF-8. Если вам нужно обработать это с помощью Java as string, вы должны преобразовать его в строку Java, как показано в моем решении, используя кодировку, используемую сервером.

Member 13958412

Спасибо за руководство, сэр!

1 Ответов

Рейтинг:
6

Jochen Arndt

Проблема коренится в вашем sendCommand() функция, потому что это не возвращает допустимый Java String (Кодировка UTF-16).

Полученные данные являются байтами. Таким образом, вы должны использовать байтовый буфер для приема.

Непроверенный пример с нуля:

// Assuming knowing the max. response length for simplicity
byte[] buffer = new byte[maxResponseLength];
int rxCount = 0;

// Receive data into buffer using and incrementing rxCount accordingly

// Create a Java String from the data.
// This requires knowing the encoding of the data which is probably UTF-8 
//  for responses from SSH servers.
String result = new String(buffer, 0, rxCount, "UTF8");