Что нового

Создание XLS-файла на основе сравнения двух других XLS

inx

Знающий
Сообщения
43
Репутация
12
Версия AutoIt: 3.3.10.2

Описание:
Сравнить два файла excel и создать на основе встречающихся в обоих таблицах данных и еще пары условий (сравнения значений ячеек) новый файл.
Подробное описание во вложении, текстом как-то криво и сложно получается.
Если кто-нибудь захочет поломать голову, буду признателен.

Примечания:
Во вложениях:
compare.xls - описание, постарался внятно изложить на кратком примере.
compare.png - то же, только скриншотом :smile:

+ Пример побольше:
file.xls и file_b.xls - сравниваемые файлы.
result.xls - файл который должен образоваться в результате. [upd: в нем пропущена одна строчка, см. посты ниже.]

compare.png

compare.zip
 

---Zak---

Скриптер
Сообщения
455
Репутация
120
2 inx

У меня получилось на 1 строку больше, чем в файле "result.xls"...

Пересчитай, пожалуйста, свой результат...

ЗЫ: Ибо в файле "file.xls" дано 4 строки "100100|FAIL|10"... и это означает, что в "result.xls" должно быть тоже эти 4 строки - 100100|10|FAIL|lambda

И еще вопрос: тебе нужно в конечном итоге одинаковые результаты или их необходимо удалить. Допустим 4 одинаковые записи "100100|10|FAIL|lambda" ???
 
Автор
I

inx

Знающий
Сообщения
43
Репутация
12
2 inx

У меня получилось на 1 строку больше, чем в файле "result.xls"...

Пересчитай, пожалуйста, свой результат...

ЗЫ: Ибо в файле "file.xls" дано 4 строки "100100|FAIL|10"... и это означает, что в "result.xls" должно быть тоже эти 4 строки - 100100|10|FAIL|lambda

И еще вопрос: тебе нужно в конечном итоге одинаковые результаты или их необходимо удалить. Допустим 4 одинаковые записи "100100|10|FAIL|lambda" ???
Да, одна строчка оказалась пропущена..

В первом сравниваемом файле в стролбце A идентификаторы (которые "FAIL" во втором столбце) по идее должны быть не повторяющимися. (Пример создан методом "копипаста" поэтому там есть сходные строки).

Поэтому в итоге можно оставить одинаковые результаты.
 

---Zak---

Скриптер
Сообщения
455
Репутация
120
inx
Ок... тогда в ближайшее время скину скрипт.

Правда сам понимаешь - он может быть сырова :stars:
 

---Zak---

Скриптер
Сообщения
455
Репутация
120
В общем как-то так:

Код:
#include <Array.au3>
#include <Excel.au3>

$File_A		= 'file.xls' 	;~ какой файл открыть первым
	$rFileA_COD		= 'A' 	;~ в какой колонке "КОД"
	$rFileA_NAME	= 'B' 	;~ в какой колонке "НАИМЕНОВАНИЕ"
	$rFileA_NUM		= 'C' 	;~ в какой колонке "???"

	$FileA_FIND 	= 'FAIL' ;~ искомая строка в колонке "НАИМЕНОВАНИЕ"

	$nFileA_CASE 	= '<' 	;~ условие для первого файла
	$nFileA_NUM 	= 100 	;~ "???" "условие" "колонка ???"

$File_B		= 'file_b.xls' 	;~ какой файл открыть вторым
	$rFileB_COD		= 'A' 	;~ в какой колонке "КОД"
	$rFileB_NAME	= 'B'	;~ в какой колонке "НАИМЕНОВАНИЕ"
	$rFileB_NUM		= 'C'	;~ в какой колонке "???"

	$nFileB_CASE 	= '>'	;~ условие для второго файла
	$nFileB_NUM 	= 999	;~ "???" "условие" "колонка ???"


$File_C		= 'result.xls'	;~ результат записать в файл

	If ProcessExists("EXCEL.exe") Then ;~ проверяем открыт ли EXCEL - если ДА
		;~ ИНОГДА БЫВАЮТ ОШИБКИ, КОГДА EXCEL ОТКРЫТ
		MsgBox(0, 'Open EXCEL !!!', 'Please close Excel !!!') ;~ просим закрыть
		Exit ;~ выходим
	EndIf

	If Not ((FileExists(@ScriptDir&'\'&$File_A))And(FileExists(@ScriptDir&'\'&$File_B))) Then ;~ проверяем существует ли файлы - если НЕТ
		MsgBox(0, 'ERROR !!!', 'File ("'&$File_A&'" or "'&$File_B&'") does not exist !!!') ;~ выводим сообщение
		Exit ;~ выходим
	EndIf

	$ArrFile_A = _EXCtoARR(@ScriptDir&'\'&$File_A) ;~ читам данные из первого файла в массив
;~ 	_ArrayDisplay($ArrFile_A)
	$rFileA_COD 	= _EXCfROW($ArrFile_A, $rFileA_COD) ;~ пишем номер колонки с КОДом
	$rFileA_NAME	= _EXCfROW($ArrFile_A, $rFileA_NAME) ;~ пишем номер колонки с НАИМЕНОВАНИЕм
	$rFileA_NUM 	= _EXCfROW($ArrFile_A, $rFileA_NUM) ;~ пишем номер колонки с ???

	$i = 0
	While 1
		If $ArrFile_A[$i][$rFileA_NAME] = $FileA_FIND Then ;~ ещем в НАИМЕНОВАНИИ искомую строку
			$i = $i + 1
		Else
			_ArrayDelete($ArrFile_A, $i) ;~ удаляем из массива лишенее
		EndIf
		If UBound($ArrFile_A)-1 <= $i Then ExitLoop
	WEnd
;~ 	_ArrayDisplay($ArrFile_A)

	$ArrFile_A = _EXC_CASE($ArrFile_A, $rFileA_NUM, $nFileA_CASE, $nFileA_NUM) ;~ ищем по условию:
	;~ 	$ArrFile_A 		- ищем в первом массиве
	;~ 	$rFileA_NUM 	- в какой колонке
	;~ 	$nFileA_CASE 	- условие
	;~ 	$nFileA_NUN 	- "???"

;~ 	_ArrayDisplay($ArrFile_A)

	$ArrFile_B = _EXCtoARR(@ScriptDir&'\'&$File_B) ;~ читам данные из второго файла в массив
;~ 	_ArrayDisplay($ArrFile_B)
	$rFileB_COD 	= _EXCfROW($ArrFile_B, $rFileB_COD) ;~ пишем номер колонки с КОДом
	$rFileB_NAME	= _EXCfROW($ArrFile_B, $rFileB_NAME) ;~ пишем номер колонки с НАИМЕНОВАНИЕм
	$rFileB_NUM 	= _EXCfROW($ArrFile_B, $rFileB_NUM) ;~ пишем номер колонки с ???

	_ArrayDelete($ArrFile_B, 0) ;~ удаляем первую строку - в ней нет необходимости
	_ArrayDelete($ArrFile_B, 0) ;~ удаляем "вторую" строку - в ней нет необходимости

	For $iArrA = 0 To UBound($ArrFile_A)-1 ;~ перебираем первый массив
		For $iArrB = 0 To UBound($ArrFile_B)-1 ;~ перебираем второй массив
			 ;~ если КОД_1 = КОД_2
			If $ArrFile_A[$iArrA][$rFileA_COD] = $ArrFile_B[$iArrB][$rFileB_COD] Then

				Switch $nFileB_CASE  ;~ проверяем устовие для 2 файла
					 ;~ если в условии "="
					Case '='
						 ;~ Если "??? второго файла" = "??? для второго файла"
						If $ArrFile_B[$iArrB][$rFileB_NUM] = $nFileB_NUM Then
							 ;~ записываем в первый столбец первого массива данные
							$ArrFile_A[$iArrA][0] = $ArrFile_B[$iArrB][$rFileB_NAME]
							 ;~ выходим из цикла
							ExitLoop
						EndIf
					 ;~ если в условии "Б"
				 Case '<'
						 ;~ Если "??? второго файла" < "??? для второго файла"
						If $ArrFile_B[$iArrB][$rFileB_NUM] < $nFileB_NUM Then
							 ;~ записываем в первый столбец первого массива данные
							$ArrFile_A[$iArrA][0] = $ArrFile_B[$iArrB][$rFileB_NAME]
							 ;~ выходим из цикла
							ExitLoop
						EndIf
					Case '>'
						If $ArrFile_B[$iArrB][$rFileB_NUM] > $nFileB_NUM Then
							$ArrFile_A[$iArrA][0] = $ArrFile_B[$iArrB][$rFileB_NAME]
							ExitLoop
						EndIf
					Case '<='
						If $ArrFile_B[$iArrB][$rFileB_NUM] <= $nFileB_NUM Then
							$ArrFile_A[$iArrA][0] = $ArrFile_B[$iArrB][$rFileB_NAME]
							ExitLoop
						EndIf
					Case '>='
						If $ArrFile_B[$iArrB][$rFileB_NUM] >= $nFileB_NUM Then
							$ArrFile_A[$iArrA][0] = $ArrFile_B[$iArrB][$rFileB_NAME]
							ExitLoop
						EndIf
				EndSwitch

			EndIf
		Next
	Next
;~ 	_ArrayDisplay($ArrFile_A)

	;~ Открываем новую книгу с параметром "not visible"
	Local $oExcel = _ExcelBookNew(0)
	;~ Перебираем первый массив
	For $iArrA = 0 To UBound($ArrFile_A)-1
		_ExcelWriteCell($oExcel, $ArrFile_A[$iArrA][$rFileA_COD], $iArrA+1, 1) ;~ записываем в 1 колонку КОД
		_ExcelWriteCell($oExcel, $ArrFile_A[$iArrA][$rFileA_NUM], $iArrA+1, 2) ;~ записываем в 2 колонку ???
		_ExcelWriteCell($oExcel, $ArrFile_A[$iArrA][$rFileA_NAME], $iArrA+1, 3) ;~ записываем в 3 колонку НАИМЕНОВАНИЕ_1
		_ExcelWriteCell($oExcel, $ArrFile_A[$iArrA][0], $iArrA+1, 4) ;~ записываем в 4 колонку НАИМЕНОВАНИЕ_2
	Next

	_ExcelBookSaveAs($oExcel, @ScriptDir&'\'&$File_C, "xls", 0, 1) ;~ сохраняем файл
	_ExcelBookClose($oExcel) ;~ закрываем EXCEL


Func _EXC_CASE($aArray, $Column, $Case, $Num)
	$i = 0
	While 1
		 ;~ проверяем какое условие
		Switch $Case
			 ;~ если "="
			Case '='
				 ;~ поиск в массие в колонке $Column значение = переменной $Num
				If $aArray[$i][$Column] = $Num Then
					 ;~ Если ДА, то переходим к следующей "строке" массива
					$i = $i + 1
				Else
					 ;~ Если НЕТ - удаляем "строку" - в ней нет необходимости
					_ArrayDelete($aArray, $i)
				EndIf
			Case '<'
				If $aArray[$i][$Column] < $Num Then
					$i = $i + 1
				Else
					_ArrayDelete($aArray, $i)
				EndIf
			Case '>'
				If $aArray[$i][$Column] > $Num Then
					$i = $i + 1
				Else
					_ArrayDelete($aArray, $i)
				EndIf
			Case '<='
				If $aArray[$i][$Column] <= $Num Then
					$i = $i + 1
				Else
					_ArrayDelete($aArray, $i)
				EndIf
			Case '>='
				If $aArray[$i][$Column] >= $Num Then
					$i = $i + 1
				Else
					_ArrayDelete($aArray, $i)
				EndIf
		EndSwitch
		If UBound($aArray)-1 <= $i Then ExitLoop ;~ Если конец массива - выходим из цикла
	WEnd

	Return $aArray
EndFunc

Func _EXCtoARR($XLSFilePath)
	Local $oExcel = _ExcelBookOpen($XLSFilePath, 0) ;~ открываем EXCEL

		$aArray = _ExcelReadSheetToArray($oExcel, 1, 1, 0) ;~ записываем данные в массив

	_ExcelBookClose($oExcel, 0) ;~ закрываем EXCEL

	Return $aArray
EndFunc

 ;~ Возвращаем НОМЕР колонки массива, который соотв. строке $STR
Func _EXCfROW($aArray, $STR)
	For $i = 0 To $aArray[0][1]
		If $aArray[1][$i] = $STR Then
			Return $i
		EndIf
	Next
EndFunc

PS: комментарии написал - должно быть понятно.
PSS: Win XP x68 + Office 2007 + AutoIT 3.3.10.2
 
Автор
I

inx

Знающий
Сообщения
43
Репутация
12
Спасибо,

последняя строчка из первого file.xls из не проверялась на условия.

заменил (170)
Код:
If UBound($aArray)-1 <= $i Then ExitLoop ;~ Если конец массива - выходим из цикла

на
Код:
If UBound($aArray) <= $i Then ExitLoop ;~ Если конец массива - выходим из цикла


и (49)
Код:
If UBound($ArrFile_A)-1 <= $i Then ExitLoop

на
Код:
If UBound($ArrFile_A) <= $i Then ExitLoop


Теперь вроде работает, но протестирую еще :smile:
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
inx,
Так немного быстрее должно работать.
Код:
#include <Excel.au3>
#include <Array.au3>

Opt('MustDeclareVars', 1)

Global $sExcelA = @ScriptDir & '\file.xls', $sExcelB = @ScriptDir & '\file_b.xls', $sTxtA = @ScriptDir & '\tmpA.txt', $sTxtB = @ScriptDir & '\tmpB.txt', _
		$aArrayA, $aArrayB, $iTmp, $i_ColumnA, $i_ColumnB, $sSearchA = 'FAIL', $sSearchB = '\d', $sText, $sExcelR = @ScriptDir & '\result.xls', _
		$sTxtR = @ScriptDir & '\result.txt'

$iTmp = _Excell_to_Txt($sExcelA, 1, $sTxtA)
If (@error) Or (Not $iTmp) Then Exit 1
$iTmp = _Excell_to_Txt($sExcelB, 1, $sTxtB)
If (@error) Or (Not $iTmp) Then Exit 2
$aArrayA = _ExcelTxt_To_Array($sTxtA, $sSearchA)
If @error Then Exit 3
$i_ColumnA = @extended
$aArrayB = _ExcelTxt_To_Array($sTxtB, $sSearchB)
If @error Then Exit 4
$i_ColumnB = @extended

;~ _ArrayDisplay($aArrayA, 'A column: ' & $i_ColumnA)
;~ _ArrayDisplay($aArrayB, 'B column: ' & $i_ColumnB)

;~ здесь сравнивайте значения в массивах (я не понял по каким условиям идет сравнение)
;~ для примера:
$sText = 'A' & @TAB & 'B' & @TAB & 'C' & @TAB & 'D' & @CRLF
For $i = 0 To UBound($aArrayA) - 1
	For $j = 0 To UBound($aArrayB) - 1
		If $aArrayA[$i][0] == $aArrayB[$j][0] Then
			For $q = 0 To $i_ColumnA - 1
				$sText &= $aArrayA[$i][$q] & @TAB
			Next
			$sText &= $aArrayB[$j][1] & @CRLF
			ExitLoop
		EndIf
	Next
Next
If FileExists($sTxtR) Then FileDelete($sTxtR)
FileWrite($sTxtR, $sText)
$iTmp = _Txt_to_Excell($sTxtR, $sExcelR)
If (@error) Or (Not $iTmp) Then Exit 5
FileDelete($sTxtA)
FileDelete($sTxtB)
FileDelete($sTxtR)


Func _ExcelTxt_To_Array($s_FileExcelTxt, $s_Search = '', $i_Casesense = 1)
	Local $s_Text, $s_Pattern, $a_Tmp, $i_Ub_1, $i_Ub_2, $a_Res[1], $a_Str

	If Not FileExists($s_FileExcelTxt) Then Return SetError(-1)
	$s_Text = StringRegExpReplace(StringStripCR(FileRead($s_FileExcelTxt)), '\n*$', '')
	If $s_Search Then
		If $i_Casesense Then
			$s_Pattern = '(?m)'
		Else
			$s_Pattern = '(?im)'
		EndIf
		$s_Pattern &= '^(.*' & $s_Search & '.*)$'
		$a_Tmp = StringRegExp($s_Text, $s_Pattern, 3)
	Else
		$a_Tmp = StringSplit($s_Text, @LF, 2)
	EndIf
	$i_Ub_1 = UBound($a_Tmp)
	If Not $i_Ub_1 Then Return SetError(1)
	StringReplace($a_Tmp[0], @TAB, '')
	$i_Ub_2 = @extended + 1
	If $i_Ub_2 > 1 Then
		ReDim $a_Res[$i_Ub_1][$i_Ub_2]
		For $i = 0 To $i_Ub_1 - 1
			$a_Str = StringSplit($a_Tmp[$i], @TAB)
			For $j = 1 To $a_Str[0]
				$a_Res[$i][$j - 1] = $a_Str[$j]
			Next
		Next
	ElseIf $i_Ub_2 = 1 Then
		$a_Res = $a_Tmp
	Else
		Return SetError(2)
	EndIf
	Return SetExtended($i_Ub_2, $a_Res)
EndFunc   ;==>_ExcelTxt_To_Array

Func _Excell_to_Txt($s_FileExcel, $v_List, $s_FileTxt = '', $i_Visible = 0)
	Local $o_Excel, $i_Err = 1

	If Not FileExists($s_FileExcel) Then Return SetError(-1)
	Do
		$o_Excel = _ExcelBookOpen($s_FileExcel, $i_Visible)
		If @error Then ExitLoop
		$i_Err += 1
		_ExcelSheetActivate($o_Excel, $v_List)
		If @error Then ExitLoop
		$i_Err += 1
		If $s_FileTxt Then
			$s_FileTxt = StringRegExpReplace($s_FileTxt, '\..*$', '')
		Else
			$s_FileTxt = StringRegExpReplace($s_FileExcel, '\..*$', '')
		EndIf
		If @extended <> 1 Then ExitLoop
		$i_Err += 1
		_ExcelBookSaveAs($o_Excel, $s_FileTxt, 'txt', 0, 1)
		If @error Then ExitLoop
		$i_Err = 0
	Until 1
	If IsObj($o_Excel) Then _ExcelBookClose($o_Excel)
	Return SetError($i_Err, 0, FileExists($s_FileTxt & '.txt'))
EndFunc   ;==>_Excell_to_Txt

Func _Txt_to_Excell($s_FileTxt, $s_FileExcel, $s_Ext = 'xls', $i_Visible = 0)
	Local $o_Excel, $i_Err = 1

	If Not FileExists($s_FileTxt) Then Return SetError(-1)
	Do
		$o_Excel = _ExcelBookOpen($s_FileTxt, $i_Visible)
		If @error Then ExitLoop
		$i_Err += 1
		If $s_FileExcel Then
			$s_FileExcel = StringRegExpReplace($s_FileExcel, '\..*$', '')
		Else
			$s_FileExcel = StringRegExpReplace($s_FileTxt, '\..*$', '')
		EndIf
		If @extended <> 1 Then ExitLoop
		$i_Err += 1
		_ExcelBookSaveAs($o_Excel, $s_FileExcel, $s_Ext, 0, 1)
		If @error Then ExitLoop
		$i_Err = 0
	Until 1
	If IsObj($o_Excel) Then _ExcelBookClose($o_Excel)
	Return SetError($i_Err, 0, FileExists($s_FileExcel & '.' & $s_Ext))
EndFunc   ;==>_Txt_to_Excell
 
Автор
I

inx

Знающий
Сообщения
43
Репутация
12
В варианте от ---Zak---

Во втором файле тоже не проверяется последняя строчка
Пример: file_b.xls

Как исправить пока не понял.
 

---Zak---

Скриптер
Сообщения
455
Репутация
120
inx
В твоем примере в файле "file_b.xls" последней строкой написано:
Код:
300300 | ошибка | 80

Если смотреть на твои условия проверки второго файла:
"ячейка C25 должна быть больше 999" или другими словами "80 больше 999"

Пропиши в Excel
Код:
300300 | ошибка | 8000

PS: с остальным согласен - исправляй, тестируй - влавствуй
 
Верх