Что нового

[Массивы] StringRegExp и двумерный массив

Gzzk

Новичок
Сообщения
104
Репутация
1
Здравствуйте.
Есть такой код:
Код:
#include <Array.au3>
Global $aHero_list[1][1]
$sRequest = 'GET /heroes HTTP/1.1' & @CRLF ;Request-строка, параметры GET-запроса
$sRequest &= 'Host: dotabuff.com' & @CRLF
$sRequest &= 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'& @CRLF
$sRequest &= 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36' & @CRLF
$sRequest &= 'Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4' & @CRLF & @CRLF

$sData=_SendPacket($sRequest,"dotabuff.com",80,2)
ConsoleWrite($sData&@CRLF)
$Hero_list=StringRegExp($sData,'<div class="name">([0-9a-zA-Z_ ]{1,})</div>',3)
ReDim $aHero_list[1][UBound($Hero_list)]
 For $i=0 To UBound($Hero_list)-1
    $aHero_list[1][$i]=$Hero_list[$i]
	ConsoleWrite($Hero_list[$i]&@CRLF)
Next

Func _SendPacket($sRequest,$sHost,$iPort=80,$iReturn=1) ; для сокращения кода и приведение его в более удобочитаемый вид
;1 - заголовки, 2 - тело, 3 - всё.
If TCPStartup() = 0 Then ;запускаем TCP/UDP-службы
    return @error
EndIf

$sIP = TCPNameToIP($sHost) ;получаем ip-адрес

If @error Then
    return @error
EndIf

$iSocket = TCPConnect($sIP, $iPort) ;соединяемся с сервером

If @error Then
    return @error
EndIf

TCPSend($iSocket, $sRequest) ;отправляем сообщение

If @error Then
    return @error
EndIf

$sData = Binary('')
$iTimer = TimerInit() ;запускаем таймер

Do
    $sBuffer = TCPRecv($iSocket, 1024, 1)
    $iError = @error
    If $sBuffer Then
        $sData &= BinaryToString($sBuffer, 4)
        $iTimer = TimerInit() ;обнуляем таймер
    EndIf
Until $iError <> 0 Or TimerDiff($iTimer) > 5000 ;если при получении сообщения произошла ошибка (данные получены, соединение закрыто) или в течении 5 секунд ничего не было получено, выход из цикла
TCPCloseSocket($iSocket)
TCPShutdown()
if($iReturn = 3) Then
	Return $sData
Else
$aData = StringRegExp($sData, '(?s)^(.+?)[\r\n]{4}(.*)$', 3) ; разделяем заголовки и тело ответа
If Not IsArray($aData) Then
return @error
Else
Switch $iReturn
	Case 1
		Return $aData[0]
    Case 2		
		Return $aData[1]
	EndSwitch
EndIf
EndIf
EndFunc;==> _SendPacket

Но у меня не получается спарсить в двумерный массив/переместить содержимое того,что я напарсил в двумерный массив.В двумерном массиве нужно только два поля для каждого героя(парсит отлично),а именно:его имя,дробное число.
Вот и вся проблема.
Заранее благодарен за ответы.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Gzzk,
Имя - понятно, а что за дробное число?
 

madmasles

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

;~ HttpSetProxy(1)

Global $aHero_list[1]

Local $s_Url = 'http://dotabuff.com/heroes', $s_Read, $a_Tmp, $i_Ub
		
;~ HttpSetUserAgent('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36')
$s_Read = InetRead($s_Url, 17)
If @error Then Exit 1
$s_Read = BinaryToString($s_Read, 4)
$a_Tmp = StringRegExp($s_Read, '(?i)<div class=[''"]name[''"]>(.+?)<', 3)
$i_Ub = UBound($a_Tmp)
If Not $i_Ub Then Exit 2
;~ вариант один в один:
ReDim $aHero_list[$i_Ub][2]
For $i = 0 To $i_Ub - 1
	$aHero_list[$i][0] = $a_Tmp[$i]
Next
_ArrayDisplay($aHero_list, 'First')
;~ вариант с кол-вом 'героев' в элементе [0][0]:
ReDim $aHero_list[$i_Ub + 1][2]
$aHero_list[0][0] = $i_Ub
For $i = 0 To $i_Ub - 1
	$aHero_list[$i + 1][0] = $a_Tmp[$i]
Next
$a_Tmp = 0
_ArrayDisplay($aHero_list, 'Second')
Или так.
Код:
#include <Array.au3>

;~ HttpSetProxy(1)

Global $aHero_list[1]

Local $s_Url = 'http://dotabuff.com/heroes/luna/matchups', $s_Read, $a_Tmp, $i_Ub

;~ HttpSetUserAgent('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36')
$s_Read = InetRead($s_Url, 17)
If @error Then Exit 1
$s_Read = BinaryToString($s_Read, 4)
$a_Tmp = StringRegExp($s_Read, '(?i)class=[''"]hero-link[''"]>(.+?)</a></td><td>(.+?)<', 3)
$i_Ub = UBound($a_Tmp)
If (Not $i_Ub) Or (Mod($i_Ub, 2)) Then Exit 2
ReDim $aHero_list[$i_Ub / 2 + 1][2]
For $i = 0 To $i_Ub - 1 Step 2
	$aHero_list[0][0] += 1
	For $j = 0 To 1
		$aHero_list[$aHero_list[0][0]][$j] = $a_Tmp[$i + $j]
	Next
Next
_ArrayDisplay($aHero_list)
Или так.
Код:
#include <Array.au3>

;~ HttpSetProxy(1)

Global $aHero_list[1]

Local $s_Url = 'http://dotabuff.com/heroes/luna/matchups', $s_Read, $a_Tmp, $i_Ub, _
		$a_Header[4] = [3, 'Advantage', 'Luna Win Rate', 'Matches Played']

;~ HttpSetUserAgent('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36')
$s_Read = InetRead($s_Url, 17)
If @error Then Exit 1
$s_Read = BinaryToString($s_Read, 4)
$a_Tmp = StringRegExp($s_Read, '(?i)class=[''"]hero-link[''"]>(.+?)<.+?>([^<>]+?)<.+?>([^<>]+?)<.+?>([^<>]+?)<', 3)
$i_Ub = UBound($a_Tmp)
If (Not $i_Ub) Or (Mod($i_Ub, 4)) Then Exit 2
ReDim $aHero_list[$i_Ub / 4 + 1][4]
For $i = 1 To $a_Header[0]
	$aHero_list[0][$i] = $a_Header[$i]
Next
For $i = 0 To $i_Ub - 1 Step 4
	If StringInStr($a_Tmp[$i], '&#x') Then _Replace($a_Tmp[$i])
	$aHero_list[0][0] += 1
	For $j = 0 To 3
		$aHero_list[$aHero_list[0][0]][$j] = $a_Tmp[$i + $j]
	Next
Next
_ArrayDisplay($aHero_list)

Func _Replace(ByRef $s_Str)
	Local $s_Hex

	$s_Hex = StringRegExpReplace($s_Str, '.*&#x(\d+);.*', '$1')
	If @extended <> 1 Then Return
	$s_Str = StringReplace($s_Str, '&#x' & $s_Hex & ';', Chr(Dec($s_Hex)))
EndFunc   ;==>_Replace


Добавлено:
Сообщение автоматически объединено:

Замените в последнем варианте &#38;#x на &#x (сайт меняет символы, а у меня не получилось экранировать их в коде).
 

Z_Lenar

Продвинутый
Сообщения
209
Репутация
52
Gzzk
Немного переделал указанный скрипт:
Код:
#include <Array.au3>
Global $aHero_list[1][1]
$sRequest = BuildRequest('/heroes')

$sData = _SendPacket($sRequest, "dotabuff.com", 80, 2)
;ConsoleWrite($sData&@CRLF)
$Hero_list = StringRegExp($sData, '<div class="name">([0-9a-zA-Z_ ]{1,})</div>', 3)
ReDim $aHero_list[UBound($Hero_list) + 1][UBound($Hero_list) + 1]
$aHero_list[0][0] = UBound($Hero_list)
For $i = 1 To UBound($Hero_list)
	$aHero_list[$i][0] = $Hero_list[$i - 1]
	$aHero_list[0][$i] = $Hero_list[$i - 1]

	$sData = StringLower($Hero_list[$i - 1])
	$sData = StringReplace($sData, ' ', '-')
	$sRequest = BuildRequest('/heroes/' & $sData & '/matchups')
	$sData = _SendPacket($sRequest, "dotabuff.com", 80, 2)
	TrayTip('title', 'Processing ' & $i & ' of ' & UBound($Hero_list), 1000)
	For $k = 1 To UBound($Hero_list)
		$res = StringRegExp($sData, '(?s)' & $Hero_list[$k - 1] & '.*?(-?\d+\.\d+)%', 1)
		If @error = 0 Then
			$aHero_list[$i][$k] = $res[0]
;		Else
;			FileDelete($Hero_list[$i - 1] & '_vs_' & $Hero_list[$k - 1] & '.txt')
;			FileWrite($Hero_list[$i - 1] & '_vs_' & $Hero_list[$k - 1] & '.txt', $sData)
		EndIf
	Next
Next
; На выходе массив размером $aHero_list[N+1][N+1], где N-количество героев
;   Элемент = [0][0] - количество героев
;   Элементы [1][0] по [N][0] - имена героев для matchup
;   Элементы [0][1] по [0][N] - имена героев для adventage
; Т.е. для 'Alchemist'(2) adventage против 'Abadon'(1) равен [2][1]
; Также регулярное выражение захватывает 'Win Rate' будет находиться в [X][X]
;   X - индекс героя
_ArrayDisplay($aHero_list)

Func BuildRequest($sPath)
	Local $ret = 'GET ' & $sPath & ' HTTP/1.1' & @CRLF ;Request-строка, параметры GET-запроса
	$ret &= 'Host: dotabuff.com' & @CRLF
	$ret &= 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' & @CRLF
	$ret &= 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36' & @CRLF
	$ret &= 'Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4' & @CRLF & @CRLF

	Return $ret
EndFunc   ;==>BuildRequest

Func _SendPacket($sRequest, $sHost, $iPort = 80, $iReturn = 1) ; для сокращения кода и приведение его в более удобочитаемый вид
	;1 - заголовки, 2 - тело, 3 - всё.
	If TCPStartup() = 0 Then ;запускаем TCP/UDP-службы
		Return @error
	EndIf

	$sIP = TCPNameToIP($sHost) ;получаем ip-адрес

	If @error Then
		Return @error
	EndIf

	$iSocket = TCPConnect($sIP, $iPort) ;соединяемся с сервером

	If @error Then
		Return @error
	EndIf

	TCPSend($iSocket, $sRequest) ;отправляем сообщение

	If @error Then
		Return @error
	EndIf

	$sData = Binary('')
	$iTimer = TimerInit() ;запускаем таймер

	Do
		$sBuffer = TCPRecv($iSocket, 1024, 1)
		$iError = @error
		If $sBuffer Then
			$sData &= BinaryToString($sBuffer, 4)
			$iTimer = TimerInit() ;обнуляем таймер
		EndIf
		; <-------------------------------->
		; 2. Until "выходит" из цикла при срабатывании условия
		; <-------------------------------->
	Until StringInStr($sData, '</html>') Or TimerDiff($iTimer) > 5000 ;если при получении сообщения произошла ошибка (данные получены, соединение закрыто) или в течении 5 секунд ничего не было получено, выход из цикла
	TCPCloseSocket($iSocket)
	TCPShutdown()
	If ($iReturn = 3) Then
		Return $sData
	Else
		$aData = StringRegExp($sData, '(?s)^(.+?)[\r\n]{4}(.*)$', 3) ; разделяем заголовки и тело ответа
		If Not IsArray($aData) Then
			Return @error
		Else
			Switch $iReturn
				Case 1
					Return $aData[0]
				Case 2
					Return $aData[1]
			EndSwitch
		EndIf
	EndIf
EndFunc   ;==>_SendPacket
 
Верх