Токенизатор подстановочных знаков Java Shell
-
21-09-2019 - |
Вопрос
Моя Java чрезвычайно устарела, и я застрял, пытаясь создать пользовательский интерфейс, который упрощает выполнение сценариев оболочки или пакетных файлов в зависимости от того, является ли это Linus или Win32 соответственно.Файлы имеют следующее соглашение об именовании.
module-verb-object-etc [args-list]
mysql-connect-grid
mysql-connect-rds
mysql-dump-grid
mysql-dump-grid-se314
в конечном счете, я хотел бы, чтобы он анализировал однозначные термины, чтобы я мог:
- обозначьте команды (например, разделенные символом "-") и сократите их до упрощенных терминов, таких как командное окно foxpro или cisco IOS (например, "my co gr" выполняет "mysql-connect-grid" в unix и *.cmd в win32).
- а также в стиле IOS позволяют пользователю вводить сокращенные команды, чтобы они могли ввести вопросительный знак (?), и это даст им подсказку относительно уникальных оставшихся (или следующих) параметров команды (например"my?" возвращает mysql, а "my ?" возвращает connect, или дамп).Другие возвращаемые значения были бы "неоднозначными" или "неизвестными" для команд, которые не являются уникальными или не могут быть сопоставлены.Это может показаться тривиальным, но в каждой папке много сотен команд, и мои пользователи не хотят думать...
Я написал функцию для извлечения списка файлов из каталога и повторной настройки массива fileanmes.Затем я преобразую это в двумерный массив, используя приведенный ниже метод, который возвращает сетку потенциальных команд динамического размера.
/**********************************************************************************
* MAKE GRID: Parses array of filenames and tokenizes AWS cmds.
* @param strs Array of filenames
**********************************************************************************/
public static String [][] makeGrid(String strs[], boolean bPrint) {
String tmpGrid[][];
int nMaxCols = 0;
int nRows = uniqueCount(strs);
int nGridRow = 0;
tmpGrid = new String [nRows][];
for (int nRow=0; nRow<nRows; nRow++) {
String cFilename = strs[nRow];
if (!cFilename.endsWith(".cmd") // just list unix files (filter for batch files)
&& cFilename.indexOf("-") > 0 ) // make sure there's a dash in the filename
{
String strTokens[] = tokenize(strs[nRow], "-"); // the dash is our token deliminator
int nCols = strTokens.length;
if (nCols>nMaxCols) nMaxCols=nCols;
tmpGrid[nGridRow] = new String [nCols];
for (int nCol=0; nCol<nCols; nCol++) {
tmpGrid[nGridRow][nCol] = strTokens[nCol];
if (bPrint) System.out.print(" "+tmpGrid[nGridRow][nCol]);
}
nGridRow++;
if (bPrint) System.out.println("");
} //end-if
}
String[][] cmdGrid = new String[nGridRow][nMaxCols];
System.arraycopy(tmpGrid, 0, cmdGrid, 0, nGridRow); // removes null rows (&NPEs!)
return cmdGrid;
}
Это возвращает двумерный массив (ниже), так что grid[Row-N][Col-0]
это совпадение.Я бы хотел извлекать только отдельные значения, где row[0]
соответствует ли подстановочный знак для cmdToken[0] && row[1]
это "как" cmdToken[1]
так что мои пользователи могут собирать команду по частям до тех пор, пока "my du gr ?"
ВОЗВРАТ "ENTER, [se314]"
- если в этом есть смысл...
String[][] makeGrid:
mysql dump grid se314
mysql connect grid
mysql dump grid
mysql connect rds
Мой Вызов:Кажется, я не могу разобраться в своей функции сопоставления в Java.Если бы это был SQL, это было бы что-то вроде:
"SELECT DISTINCT col2 FROM cmd_Grid
WHERE col1 LIKE 'cmdToken1%' "
или даже лучше:рекурсивная установка метки глубины int для каждого последующего столбца
`SELECT DISTINCT col+str(depthmark+1) FROM cmd_Grid
WHERE col+str(depthmark) LIKE 'cmdMatchedTokens%' "
пока у вас не будет точного совпадения.
Я нашел пакет под названием JoSQL, который я попробовал в отчаянии, но, похоже, я не могу заставить его работать в Java6.В любом случае:Я также надеялся на чистое java-решение, чтобы все могло содержаться в одном классе...
Возможно, с помощью сканера или чего-то еще для анализа моего многомерного массива на наличие уникальных значений...Я знаю, что, вероятно, делаю это намного сложнее, чем это должно быть.
был бы признателен за легкий толчок в правильном направлении.
ТИА
Решение
Одним из исчерпывающих решений могло бы быть создание HashMap таким образом, чтобы ключом была возможная короткая команда типа 'my co gr", а соответствующим значением было "mysql-connect-grid".Таким образом, в хэш-карте будут значения, которые будут иметь "mysql-connect-grid" в качестве значения.
Но это осуществимое решение только в том случае, если существует конечное число возможных ключей.Если это не так, то вы можете использовать встроенные методы синтаксического анализа строк.
Например:
String[][] makeGrid = new String[][]{{"mysql", "dump", "grid", "se314"},
{"mysql", "connect", "grid", ""},
{"mysql", "dump", "grid", ""},
{"mysql", "connect", "rds", ""}
};
String[] query2 = new String[]{"my", "du", "gr"};
String[][] matchingCommands = new String[4][4];
int resultSize = 0;
for(int i=0; i<makeGrid.length; i++)
{
String[] commandColumn = makeGrid[i];
boolean matches = false;
for(int cnt=0; cnt<commandColumn.length; cnt++)
{
String commandPart = commandColumn[cnt];
if(cnt < query2.length){
String queryPart = query2[cnt];
if(commandPart.startsWith(queryPart) || queryPart.equals("?")){
matches = true;
}else{
matches = false;
break;
}
}
}
if(matches){
matchingCommands[resultSize] = commandColumn;
resultSize++;
}
}
Этот фрагмент кода должен дать вам некоторое представление о том, как это сделать.Однако здесь следует отметить одну вещь.В Совпадающие команды массив был инициализирован для 4 строк и 4 столбцов, что является расточительным, поскольку совпадений будет меньше, чем это.Дайте мне знать, если вам нужна помощь, чтобы сделать это более эффективным.В противном случае, это рабочий фрагмент кода, который, я думаю, делает то, что вы хотите.
Другие советы
Вы также могли бы рассмотреть возможность использования некоторых более продвинутых структур данных, таких как ArrayList, вместо массива и использования StringTokenizer для генерации каждой командной части "на лету".
Это будет что-то вроде этого:
ArrayList<String> matchingCommands = new ArrayList<String>();
ArrayList<String> commandList = new ArrayList<String>();
commandList.add("mysql dump grid se314");
commandList.add("mysql connect grid");
commandList.add("mysql dump grid");
commandList.add("mysql connect rds");
String queryCommand = "my du gr ?";
for(int i=0; i<commandList.size(); i++)
{
boolean matches = false;
String command = commandList.get(i);
StringTokenizer commandTokenizer = new StringTokenizer(command, " "); // Using space as the deliminator
StringTokenizer queryTokenizer = new StringTokenizer(queryCommand, " "); // Using space as the deliminator
while(commandTokenizer.hasMoreTokens())
{
String queryPart = queryTokenizer.nextToken();
String commandPart = commandTokenizer.nextToken();
if(commandPart.startsWith(queryPart) || queryPart.equals("?")){
matches = true;
}else{
matches = false;
break;
}
}
if(matches){
matchingCommands.add(command);
}
}
System.out.println(matchingCommands);
Это гарантировало бы, что ваша программа может динамически расширяться, и также не было бы потери памяти из-за нулевых объектов.
прямо сейчас я возился с разбором каждой cmdString (запроса) на наличие разделителей пробелов и маркировкой массива.Что - то вроде:
Scanner sCmdString = new Scanner(cInput);
while (sCmdString.hasNext()) {
String cToken = sCmdString.next().toUpperCase().trim();
System.out.println(" "+cToken+" ");
// match cmdString[i..n] to cmdGrid
for (int nRow=0; nRow < cmdGrid.length; nRow++) {
for (int nCol=0; nCol < cmdGrid[nRow].length; nCol++) {
if (cmdGrid[nRow][nCol].equalsIgnoreCase(cToken) )
System.out.println("MATCH: "+cmdGrid[nRow][nCol]);
else System.out.println("NO MATCH:"+cmdGrid[nRow][nCol].toUpperCase()+":"+cToken+"...");
}
}
}
но я получал NPE из-за неравномерной длины рядов.
И мне нравится ваша идея выровнять колонны.
Я думаю, мне все равно пришлось бы удалять дубликаты...нет?