Загрузка данных
' ===== В САМОМ ВЕРХУ МОДУЛЯ (Personal.xlsb) =====
Private SavedRow As Long
Private SavedCol As Long
Private SavedNumLines As Long
Private SavedOldData As Variant
' =================================================
Sub ВставитьСписокИзWordСНумерацией()
On Error GoTo ErrorHandler
Dim clipText As String
Dim ws As Worksheet
Dim rngBefore As Range
Dim multiPart As Boolean
Dim cell2Text As String, cell3Text As String
Dim colLines As Collection
Dim i As Long, numLines As Long
Dim lineText As String, numPart As String, textPart As String
Dim dotPos As Long
' 1. Абсолютно безопасное получение текста из буфера
clipText = GetClipboardText()
If Len(clipText) = 0 Then
MsgBox "Буфер обмена пуст или содержит не текст.", vbInformation
Exit Sub
End If
' 2. Проверяем, есть ли табуляции (несколько ячеек)
multiPart = (InStr(clipText, vbTab) > 0)
cell2Text = ""
cell3Text = ""
If multiPart Then
' Ищем первую непустую строку с табуляциями
Dim rows() As String, rowText As String
rows = SplitTextToLines(clipText) ' наша безопасная функция
rowText = ""
For i = 0 To UBound(rows)
If Len(Trim(rows(i))) > 0 Then
rowText = rows(i)
Exit For
End If
Next i
If rowText <> "" Then
Dim parts() As String
parts = Split(rowText, vbTab)
If UBound(parts) >= 1 Then
' Первая часть до табуляции — основной текст
clipText = parts(0)
If UBound(parts) >= 2 Then
cell2Text = parts(1)
End If
If UBound(parts) >= 3 Then
cell3Text = parts(2)
End If
' Очищаем от невидимых символов
cell2Text = CleanString(cell2Text)
cell3Text = CleanString(cell3Text)
End If
End If
End If
' 3. Извлекаем непустые строки из основного текста
Dim allLines() As String
allLines = SplitTextToLines(clipText)
Set colLines = New Collection
For i = 0 To UBound(allLines)
lineText = CleanString(allLines(i))
If Len(Trim(lineText)) > 0 Then
colLines.Add lineText
End If
Next i
If colLines.Count = 0 Then
MsgBox "Не найдено ни одной строки текста.", vbInformation
Exit Sub
End If
numLines = colLines.Count
' 4. Сохраняем состояние ячеек
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 + 1))
If Not rngBefore Is Nothing Then
SavedOldData = rngBefore.Value
Else
SavedOldData = Empty
End If
' 5. Вставляем строки и заполняем
Application.ScreenUpdating = False
ws.Rows(SavedRow & ":" & SavedRow + numLines - 1).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
For i = 1 To numLines
lineText = colLines(i)
' Поиск номера с точкой и пробелом
dotPos = InStr(lineText, ". ")
If dotPos > 0 Then
Dim leftPart As String
leftPart = Left(lineText, dotPos - 1)
' Проверяем, что левая часть — число
If IsNumeric(leftPart) And Len(leftPart) > 0 Then
numPart = leftPart & ". "
textPart = Mid(lineText, dotPos + 2)
Else
numPart = ""
textPart = lineText
End If
Else
numPart = ""
textPart = lineText
End If
' Убираем возможные пробелы в начале текста
textPart = LTrim(textPart)
' Запись в столбцы
With ws.Cells(SavedRow + i - 1, SavedCol)
.Value = numPart
.Font.Bold = False
.HorizontalAlignment = xlRight
End With
With ws.Cells(SavedRow + i - 1, SavedCol + 1)
.Value = textPart
.Font.Bold = False
End With
If multiPart Then
ws.Cells(SavedRow + i - 1, 5).Value = cell2Text ' E
ws.Cells(SavedRow + i - 1, 5).Font.Bold = False
ws.Cells(SavedRow + i - 1, 6).Value = cell3Text ' F
ws.Cells(SavedRow + i - 1, 6).Font.Bold = False
End If
Next i
' 6. Автоподбор высоты
ws.Rows(SavedRow & ":" & SavedRow + numLines - 1).EntireRow.AutoFit
' 7. Удаление строк до объединённой ячейки в C/D
Dim lastInsertedRow As Long, nextMergedRow As Long, r As Long
lastInsertedRow = SavedRow + numLines - 1
nextMergedRow = 0
For r = lastInsertedRow + 1 To lastInsertedRow + 1000
If ws.Cells(r, 3).MergeCells Or ws.Cells(r, 4).MergeCells Then
If ws.Cells(r, 3).MergeCells Then
nextMergedRow = ws.Cells(r, 3).MergeArea.Row
Else
nextMergedRow = ws.Cells(r, 4).MergeArea.Row
End If
Exit For
End If
Next r
If nextMergedRow > 0 Then
Dim firstRowToDelete As Long, lastRowToDelete As Long
firstRowToDelete = lastInsertedRow + 1
lastRowToDelete = nextMergedRow - 1
If lastRowToDelete >= firstRowToDelete Then
ws.Rows(firstRowToDelete & ":" & lastRowToDelete).Delete Shift:=xlUp
End If
End If
Application.ScreenUpdating = True
Exit Sub
ErrorHandler:
Application.ScreenUpdating = True
MsgBox "Ошибка " & Err.Number & ": " & Err.Description & vbCrLf & _
"Процедура: ВставитьСписокИзWordСНумерацией" & vbCrLf & _
"Содержимое буфера (первые 100 символов): " & vbCrLf & Left(clipText, 100), vbCritical, "Ошибка в макросе"
End Sub
' ============================================================
' Безопасное получение текста из буфера обмена
' ============================================================
Private Function GetClipboardText() As String
On Error Resume Next
Dim dataObj As Object
Set dataObj = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
If Err.Number <> 0 Then
Err.Clear
Set dataObj = CreateObject("MSForms.DataObject")
End If
On Error GoTo 0
If dataObj Is Nothing Then
GetClipboardText = ""
Exit Function
End If
dataObj.GetFromClipboard
Dim txt As String
txt = ""
On Error Resume Next
txt = dataObj.GetText
If Err.Number <> 0 Then
txt = ""
End If
On Error GoTo 0
' Защита от Null/Empty
If IsNull(txt) Then txt = ""
GetClipboardText = txt
End Function
' ============================================================
' Разбиение текста на строки с любыми разделителями (CR, LF, CR+LF)
' ============================================================
Private Function SplitTextToLines(ByVal text As String) As String()
Dim result() As String
ReDim result(0 To 0)
If Len(text) = 0 Then
SplitTextToLines = result
Exit Function
End If
Dim i As Long, j As Long
Dim ch As String
Dim curLine As String
Dim col As New Collection
curLine = ""
For i = 1 To Len(text)
ch = Mid(text, i, 1)
If ch = vbCr Or ch = vbLf Then
' Сохраняем текущую строку, если не пустая
If Len(curLine) > 0 Then
col.Add curLine
curLine = ""
End If
' Если это CR, следующий символ может быть LF — пропускаем его
If ch = vbCr And i < Len(text) Then
If Mid(text, i + 1, 1) = vbLf Then
i = i + 1 ' пропускаем LF
End If
End If
Else
curLine = curLine & ch
End If
Next i
' Последняя строка
If Len(curLine) > 0 Then
col.Add curLine
End If
If col.Count = 0 Then
ReDim result(0 To 0)
result(0) = ""
Else
ReDim result(0 To col.Count - 1)
For i = 1 To col.Count
result(i - 1) = col(i)
Next i
End If
SplitTextToLines = result
End Function
' ============================================================
' Очистка строки от непечатных символов
' ============================================================
Private Function CleanString(ByVal txt As String) As String
If Len(txt) = 0 Then
CleanString = ""
Exit Function
End If
txt = Replace(txt, Chr(160), " ")
Dim i As Long
For i = 1 To 31
If i <> 9 And i <> 10 And i <> 13 Then
txt = Replace(txt, Chr(i), "")
End If
Next i
CleanString = txt
End Function
' ============================================================
' Отмена последней вставки
' ============================================================
Sub ОтменитьПоследнююВставкуСписка()
On Error GoTo CancelError
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 + 1))
rngRestore.Value = SavedOldData
End If
SavedNumLines = 0
SavedOldData = Empty
Application.ScreenUpdating = True
Exit Sub
CancelError:
Application.ScreenUpdating = True
MsgBox "Ошибка при отмене: " & Err.Description, vbExclamation
End Sub