Загрузка данных
' ===== В САМОМ ВЕРХУ МОДУЛЯ (Personal.xlsb) =====
Private SavedRow As Long
Private SavedCol As Long
Private SavedNumLines As Long
Private SavedOldData As Variant
' =================================================
Sub ВставитьСписокИзWordСНумерацией()
Dim dataObj As Object, clipText As String, linesArr As Variant
Dim i As Long, lineText As String
Dim numPart As String, textPart As String, extra1 As String, extra2 As String
Dim dotPos As Long, numLines As Long
Dim ws As Worksheet
Dim rngBefore As Range
Dim firstField As String
Dim lastE As String, lastF As String
' 1. Читаем буфер обмена
On Error Resume Next
Set dataObj = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
If Err.Number <> 0 Then
On Error GoTo 0
Set dataObj = CreateObject("MSForms.DataObject")
End If
On Error GoTo 0
If dataObj Is Nothing Then
MsgBox "Не удалось получить доступ к буферу обмена.", vbExclamation
Exit Sub
End If
dataObj.GetFromClipboard
clipText = dataObj.GetText
If Len(Trim(clipText)) = 0 Then
MsgBox "Буфер обмена пуст или содержит не текст.", vbInformation
Exit Sub
End If
' 2. Разбиваем на строки, отбрасываем полностью пустые
Dim rawLines As Variant
rawLines = Split(clipText, vbCrLf)
If UBound(rawLines) < 0 Then rawLines = Split(clipText, vbLf)
Dim col As New Collection
Dim rawLine As Variant
For Each rawLine In rawLines
If Len(Trim(CStr(rawLine))) > 0 Then
col.Add CStr(rawLine)
End If
Next rawLine
If col.Count = 0 Then
MsgBox "Не найдено ни одной строки текста.", vbInformation
Exit Sub
End If
ReDim linesArr(0 To col.Count - 1)
For i = 0 To col.Count - 1
linesArr(i) = col(i + 1)
Next i
numLines = col.Count
' 3. Сохраняем состояние ячеек (столбцы C-F) начиная с активной
Set ws = ActiveSheet
SavedRow = ActiveCell.Row
SavedCol = ActiveCell.Column
SavedNumLines = numLines
Set rngBefore = ws.Range(ws.Cells(SavedRow, SavedCol), ws.Cells(SavedRow + numLines - 1, SavedCol + 3))
If Not rngBefore Is Nothing Then
SavedOldData = rngBefore.Value
Else
SavedOldData = Empty
End If
' 4. Вставляем ЦЕЛЫЕ строки ПОСЛЕ активной, заполняем начиная с активной
Application.ScreenUpdating = False
ws.Rows(SavedRow + 1 & ":" & SavedRow + numLines).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
firstField = ""
lastE = ""
lastF = ""
For i = 0 To numLines - 1
lineText = linesArr(i)
If Len(lineText) = 0 Then GoTo NextLine
Dim parts As Variant
parts = Split(lineText, vbTab)
Dim v0 As Variant, v1 As Variant, v2 As Variant
v0 = parts(0)
If UBound(parts) >= 1 Then v1 = parts(1) Else v1 = Empty
If UBound(parts) >= 2 Then v2 = parts(2) Else v2 = Empty
Dim field1 As String, field2 As String, field3 As String
field1 = SafeString(v0)
field2 = SafeString(v1)
field3 = SafeString(v2)
If Len(field2) > 0 Then lastE = field2
If Len(field3) > 0 Then lastF = field3
If i = 0 Then firstField = field1
numPart = ""
textPart = ""
If Len(field1) > 0 Then
On Error Resume Next
dotPos = InStr(field1, ". ")
If dotPos > 0 And IsNumeric(Left(field1, dotPos - 1)) Then
numPart = Left(field1, dotPos + 1)
textPart = Trim(Mid(field1, dotPos + 2))
Else
textPart = field1
End If
If Err.Number <> 0 Then
numPart = ""
textPart = field1
Err.Clear
End If
On Error GoTo 0
End If
With ws.Cells(SavedRow + i, SavedCol) ' C
.Value = numPart
.Font.Bold = False
.HorizontalAlignment = xlRight
End With
With ws.Cells(SavedRow + i, SavedCol + 1) ' D
.Value = textPart
.Font.Bold = False
End With
With ws.Cells(SavedRow + i, SavedCol + 2) ' E
.Value = field2
.Font.Bold = False
End With
With ws.Cells(SavedRow + i, SavedCol + 3) ' F
.Value = field3
.Font.Bold = False
End With
NextLine:
Next i
' 5. Автоподбор высоты строк
ws.Rows(SavedRow & ":" & SavedRow + numLines - 1).EntireRow.AutoFit
' 6. Удаление полностью пустых строк (кроме первой)
Dim lastRow As Long
lastRow = SavedRow + numLines - 1
For i = lastRow To SavedRow + 1 Step -1
If WorksheetFunction.CountA(ws.Rows(i)) = 0 Then
ws.Rows(i).Delete Shift:=xlUp
numLines = numLines - 1
End If
Next i
lastRow = SavedRow + numLines - 1
' 7. Объединяем C и D в первой строке
If numLines > 0 Then
Dim rngFirstCD As Range
Set rngFirstCD = ws.Range(ws.Cells(SavedRow, SavedCol), ws.Cells(SavedRow, SavedCol + 1))
If rngFirstCD.MergeCells Then rngFirstCD.UnMerge
rngFirstCD.Merge
rngFirstCD.Value = firstField
rngFirstCD.Font.Bold = False
End If
' 8. Принудительно объединяем E и F на всю высоту блока
If numLines > 0 Then
Dim rngE As Range, rngF As Range
Set rngE = ws.Range(ws.Cells(SavedRow, SavedCol + 2), ws.Cells(lastRow, SavedCol + 2))
Set rngF = ws.Range(ws.Cells(SavedRow, SavedCol + 3), ws.Cells(lastRow, SavedCol + 3))
If rngE.MergeCells Then rngE.UnMerge
rngE.ClearContents
rngE.Merge
If Len(lastE) > 0 Then
rngE(1).Value = lastE
rngE(1).Font.Bold = False
End If
If rngF.MergeCells Then rngF.UnMerge
rngF.ClearContents
rngF.Merge
If Len(lastF) > 0 Then
rngF(1).Value = lastF
rngF(1).Font.Bold = False
End If
End If
' 9. Объединяем ВСЕ ОСТАЛЬНЫЕ СТОЛБЦЫ (кроме C и D), но только если в них НЕТ ФОРМУЛ
If numLines > 0 Then
Dim maxCol As Long
On Error Resume Next
maxCol = ws.Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
If Err.Number <> 0 Then maxCol = 8
On Error GoTo 0
If maxCol < 8 Then maxCol = 8
Dim colIdx As Long
For colIdx = 1 To maxCol
' Пропускаем столбцы C (3) и D (4)
If colIdx = 3 Or colIdx = 4 Then GoTo ContinueCol
Dim rngCol As Range
Set rngCol = ws.Range(ws.Cells(SavedRow, colIdx), ws.Cells(lastRow, colIdx))
' Если в диапазоне есть хоть одна формула — не трогаем этот столбец
If rngCol.HasFormula Then GoTo ContinueCol
' Снимаем предыдущее объединение (если есть)
If rngCol.MergeCells Then rngCol.UnMerge
' Ищем последнее непустое значение в этом столбце (снизу вверх)
Dim lastVal As String
lastVal = ""
Dim rSearch As Long
For rSearch = lastRow To SavedRow Step -1
Dim cellVal As Variant
cellVal = ws.Cells(rSearch, colIdx).Value
If Not IsEmpty(cellVal) Then
Dim cleanVal As String
cleanVal = DeepClean(CStr(cellVal))
If Len(cleanVal) > 0 Then
lastVal = cleanVal
Exit For
End If
End If
Next rSearch
' Очищаем диапазон, объединяем и вставляем значение, если есть
rngCol.ClearContents
rngCol.Merge
If Len(lastVal) > 0 Then
rngCol(1).Value = lastVal
rngCol(1).Font.Bold = False
End If
ContinueCol:
Next colIdx
End If
' 10. Финальное удаление последней строки, если она полностью пустая
If numLines > 1 Then
If WorksheetFunction.CountA(ws.Rows(SavedRow + numLines - 1)) = 0 Then
ws.Rows(SavedRow + numLines - 1).Delete Shift:=xlUp
numLines = numLines - 1
lastRow = lastRow - 1
End If
End If
' 11. Удаление старых строк до следующего значимого рубежа
Dim nextStopRow As Long
nextStopRow = 0
Dim maxRow As Long
maxRow = ws.Rows.Count
Dim r As Long
For r = lastRow + 1 To WorksheetFunction.Min(lastRow + 1000, maxRow)
If ws.Cells(r, 3).MergeCells Or ws.Cells(r, 4).MergeCells Then
If ws.Cells(r, 3).MergeCells Then
nextStopRow = ws.Cells(r, 3).MergeArea.Row
Else
nextStopRow = ws.Cells(r, 4).MergeArea.Row
End If
Exit For
End If
If Not IsEmpty(ws.Cells(r, 1).Value) Or Not IsEmpty(ws.Cells(r, 2).Value) Or _
Not IsEmpty(ws.Cells(r, 7).Value) Or Not IsEmpty(ws.Cells(r, 8).Value) Then
nextStopRow = r
Exit For
End If
Next r
If nextStopRow > 0 Then
Dim firstRowToDelete As Long, lastRowToDelete As Long
firstRowToDelete = lastRow + 1
lastRowToDelete = nextStopRow - 1
If lastRowToDelete >= firstRowToDelete Then
ws.Rows(firstRowToDelete & ":" & lastRowToDelete).Delete Shift:=xlUp
End If
End If
Application.ScreenUpdating = True
End Sub
' Глубокая очистка от всех невидимых символов
Private Function DeepClean(txt As String) As String
If Len(txt) = 0 Then Exit Function
Dim result As String
result = txt
result = Replace(result, ChrW(160), "")
result = Replace(result, ChrW(8203), "")
result = Replace(result, ChrW(8204), "")
result = Replace(result, ChrW(8205), "")
result = Replace(result, ChrW(65279), "")
result = Replace(result, ChrW(173), "")
result = Replace(result, ChrW(8206), "")
result = Replace(result, ChrW(8207), "")
result = Replace(result, vbTab, "")
result = Replace(result, vbCrLf, "")
result = Replace(result, vbLf, "")
result = Replace(result, vbCr, "")
Dim i As Long
Dim cleanStr As String
cleanStr = ""
For i = 1 To Len(result)
Dim charCode As Long
charCode = AscW(Mid(result, i, 1))
If (charCode >= 32 And charCode <= 126) Or charCode > 159 Then
cleanStr = cleanStr & Mid(result, i, 1)
End If
Next i
result = cleanStr
While InStr(result, " ") > 0
result = Replace(result, " ", " ")
Wend
DeepClean = Trim(result)
End Function
Private Function SuperClean(txt As String) As String
If Len(txt) = 0 Then Exit Function
Dim result As String
result = txt
result = Replace(result, Chr(160), " ")
result = Replace(result, vbTab, " ")
result = Replace(result, vbCrLf, " ")
result = Replace(result, vbLf, " ")
result = Replace(result, vbCr, " ")
While InStr(result, " ") > 0
result = Replace(result, " ", " ")
Wend
SuperClean = Trim(result)
End Function
Private Function SafeString(v As Variant) As String
If IsNull(v) Or IsError(v) Then
SafeString = ""
Else
SafeString = Trim(CStr(v))
End If
End Function
Sub ОтменитьПоследнююВставкуСписка()
If SavedNumLines <= 0 Then
MsgBox "Нет данных для отмены последней вставки.", vbInformation
Exit Sub
End If
Dim ws As Worksheet
Set ws = ActiveSheet
Application.ScreenUpdating = False
ws.Rows(SavedRow & ":" & SavedRow + SavedNumLines - 1).Delete Shift:=xlUp
If Not IsEmpty(SavedOldData) Then
Dim rngRestore As Range
Set rngRestore = ws.Range(ws.Cells(SavedRow, SavedCol), ws.Cells(SavedRow + SavedNumLines - 1, SavedCol + 3))
rngRestore.Value = SavedOldData
End If
SavedNumLines = 0
SavedOldData = Empty
Application.ScreenUpdating = True
End Sub