Cần hỗ trợ Tách chuỗi 1 ô ra nhiều cột theo điều kiện

Liên hệ QC

phiphi2022

Thành viên mới
Tham gia
3/5/22
Bài viết
23
Được thích
4
Chào các anh chị !

Em có 1 bài toán rất khó với em, em đưa lên đây nhờ các anh chị biết giải quyết dùm với ạ !

Em có 1 cột dữ liệu ghi lại vị trí theo dạng : K01.1-5,14-16 ( giống với cách in giấy trang in )

Em cần tách ra các cột kế bên theo dạng : K01.01 K01.02 K01.03 K01.04 K01.05 K01.14 K01.15 K01.16

Viết vba càng tốt ạ.

Em có file đính kèm, các anh chị xem qua ạ !
 

File đính kèm

  • TachDataVitri.xlsx
    16.3 KB · Đọc: 35
Dữ liệu trong file có vẻ quy luật không giống nhau lắm
Dạ, cách nhập liệu là theo nguyên tắc giống đánh số trang in và theo quy luật :
Dấu "." là đánh dấu tên Kệ để hàng ạ
Dấu "-" là từ vị trí này đến vị trí khác liền kề
Dấu "," là ngăn cách 2 vị trí ạ
 
Upvote 0
1. Code có nhiều cụm IF ... END IF vì tôi xử lý tất cả (hi vọng là thế) các lỗi dữ liệu. Tôi không muốn bẫy lỗi kiểu On Error ... nên dùng IF.
Vd. với K01.13-16 thì có kết quả, còn với K0113-16 thì không có kết quả.

2. Code loại các lỗi kiểu như được nêu trong bài #4. Tức trong trường hợp đó thì không trả về kết quả.

3.
Tôi viết đến đâu nghĩ tới đó, khai báo biến tới đó. Không suy nghĩ và có thuật toán trước nên có thể code "lủng củng".

Tôi chú thích từng dòng nên bạn không thể không hiểu.

Mã:
Option Explicit

Sub tach_dulieu()
Dim r As Long, c As Long, lastRow As Long, pos As Long, start As Long, end_ As Long, k As Long, curr_col As Long, max_col As Long
Dim text As String, prefix As String, dong, gioihan, dulieu(), kq()
    With ThisWorkbook.Worksheets("Sheet1")
        .Range("B2").Resize(100000, 1000).ClearContents ' xoa ket qua cu
        lastRow = .Range("A" & Rows.Count).End(xlUp).Row
        If lastRow < 2 Then Exit Sub    ' khong co du lieu thi don do choi
        dulieu = .Range("A2:A" & lastRow + 1).Value ' lay du 1 dong cuoi
    End With
    For r = 1 To UBound(dulieu, 1) - 1  ' khong xet dong lay du
        prefix = ""
        curr_col = 1    ' chiso cot hien hanh de nhap ket qua, khi bat dau dong moi thi = 1.
        dong = Split(dulieu(r, 1), ",")     ' tach du lieu trong moi dong cua cot A thanh mang gia tri theo dau phay
        For c = LBound(dong) To UBound(dong)    ' duyet tung thanh phan cua dong hien hanh
            text = Trim(dong(c))    ' gia tri cua thanh phan hien hanh
            If Len(text) Then
                pos = InStr(1, text, ".")   ' tim dau cham la ky tu ket thuc cua prefix vd. k02., 6x4.
                If pos Then prefix = Mid(text, 1, pos)  ' neu co dau cham thi xac dinh prefix hien hanh, neu khong co thi lay prefix cua thanh phan truoc do
                If Len(prefix) Then ' de phong truong hop du lieu la vd. k0212-13 - khong co dau cham de xac dinh prefix hien hanh, khong co ca thanh phan truoc de lay prefix cua no
                    gioihan = Split(Mid(text, pos + 1), "-")    ' gioi han co dang a hoac a-b
                    If IsNumeric(gioihan(0)) Then   ' a phai la SO
                        start = CLng(gioihan(0))    ' gioi han dau
                        If UBound(gioihan) = 1 Then ' neu co gioi han cuoi tuc dang a-b
                            If IsNumeric(gioihan(1)) Then   ' gioi han cuoi cung phai la SO
                                end_ = CLng(gioihan(1)) ' gioi han cuoi
                            End If
                        Else
                            end_ = start    ' khi co dang a ma khong pha6i dang a-b
                        End If
                        If start <= end_ Then   ' chi xet du lieu chuan khi gioi han dau <= gioi han cuoi
                            If max_col < curr_col + end_ - start Then   ' neu so cot cua mang ket qua hien thoi < so cot can co thi mo rong them so cot
                                max_col = curr_col + end_ - start
                                ReDim Preserve kq(1 To UBound(dulieu, 1) - 1, 1 To max_col)
                            End If
                            For k = start To end_   ' nhap cac gia tri vao mang ket qua
                                kq(r, curr_col + k - start) = prefix & Format(k, "00")
                            Next k
                            curr_col = curr_col + end_ - start + 1  ' xac dinh vi tri nhap thanh phan tiep theo
                        End If
                    End If
                End If
            End If
        Next c
    Next r
    ThisWorkbook.Worksheets("Sheet1").Range("B2").Resize(UBound(kq, 1), UBound(kq, 2)).Value = kq   ' dap ket qua xuong sheet
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Dòng 104 nó lạ lắm, bắt đầu lớn hơn kết thúc: K12.13-12

Dòng 433 cũng vậy: KL02.333-34
Dạ, lỗi do em ạ, em nhập dữ liệu test bị sai ạ
Bài đã được tự động gộp:

1. Code có nhiều cụm IF ... END IF vì tôi xử lý tất cả (hi vọng là thế) các lỗi dữ liệu. Tôi không muốn bẫy lỗi kiểu On Error ... nên dùng IF.
Vd. với K01.13-16 thì có kết quả, còn với K0113-16 thì không có kết quả.

2. Code loại các lỗi kiểu như được nêu trong bài #4. Tức trong trường hợp đó thì không trả về kết quả.

3.
Tôi viết đến đâu nghĩ tới đó, khai báo biến tới đó. Không suy nghĩ và có thuật toán trước nên có thể code "lủng củng".

Tôi chú thích từng dòng nên bạn không thể không hiểu.

Mã:
Option Explicit

Sub tach_dulieu()
Dim r As Long, c As Long, lastRow As Long, pos As Long, start As Long, end_ As Long, k As Long, curr_col As Long, max_col As Long
Dim text As String, prefix As String, dong, gioihan, dulieu(), kq()
    With ThisWorkbook.Worksheets("Sheet1")
        .Range("B2").Resize(100000, 1000).ClearContents ' xoa ket qua cu
        lastRow = .Range("A" & Rows.Count).End(xlUp).Row
        If lastRow < 2 Then Exit Sub    ' khong co du lieu thi don do choi
        dulieu = .Range("A2:A" & lastRow + 1).Value ' lay du 1 dong cuoi
    End With
    For r = 1 To UBound(dulieu, 1) - 1  ' khong xet dong lay du
        prefix = ""
        curr_col = 1    ' chiso cot hien hanh de nhap ket qua, khi bat dau dong moi thi = 1.
        dong = Split(dulieu(r, 1), ",")     ' tach du lieu trong moi dong cua cot A thanh mang gia tri theo dau phay
        For c = LBound(dong) To UBound(dong)    ' duyet tung thanh phan cua dong hien hanh
            text = Trim(dong(c))    ' gia tri cua thanh phan hien hanh
            If Len(text) Then
                pos = InStr(1, text, ".")   ' tim dau cham la ky tu ket thuc cua prefix vd. k02., 6x4.
                If pos Then prefix = Mid(text, 1, pos)  ' neu co dau cham thi xac dinh prefix hien hanh, neu khong co thi lay prefix cua thanh phan truoc do
                If Len(prefix) Then ' de phong truong hop du lieu la vd. k0212-13 - khong co dau cham de xac dinh prefix hien hanh, khong co ca thanh phan truoc de lay prefix cua no
                    gioihan = Split(Mid(text, pos + 1), "-")    ' gioi han co dang a hoac a-b
                    If IsNumeric(gioihan(0)) Then   ' a phai la SO
                        start = CLng(gioihan(0))    ' gioi han dau
                        If UBound(gioihan) = 1 Then ' neu co gioi han cuoi tuc dang a-b
                            If IsNumeric(gioihan(1)) Then   ' gioi han cuoi cung phai la SO
                                end_ = CLng(gioihan(1)) ' gioi han cuoi
                            End If
                        Else
                            end_ = start    ' khi co dang a ma khong pha6i dang a-b
                        End If
                        If start <= end_ Then   ' chi xet du lieu chuan khi gioi han dau <= gioi han cuoi
                            If max_col < curr_col + end_ - start Then   ' neu so cot cua mang ket qua hien thoi < so cot can co thi mo rong them so cot
                                max_col = curr_col + end_ - start
                                ReDim Preserve kq(1 To UBound(dulieu, 1) - 1, 1 To max_col)
                            End If
                            For k = start To end_   ' nhap cac gia tri vao mang ket qua
                                kq(r, curr_col + k - start) = prefix & Format(k, "00")
                            Next k
                            curr_col = curr_col + end_ - start + 1  ' xac dinh vi tri nhap thanh phan tiep theo
                        End If
                    End If
                End If
            End If
        Next c
    Next r
    ThisWorkbook.Worksheets("Sheet1").Range("B2").Resize(UBound(kq, 1), UBound(kq, 2)).Value = kq   ' dap ket qua xuong sheet
End Sub
Dạ, đúng ý lắm ạ

Code chạy như ý rồi ạ

Ghi chú rất chi tiết, rất dễ hiểu ạ

Cảm ơn anh batman1 nhiều ạ !

Chúc anh cuối tuần vui vẻ ạ !
 
Upvote 0
Chào các anh chị !

Em có 1 bài toán rất khó với em, em đưa lên đây nhờ các anh chị biết giải quyết dùm với ạ !

Em có 1 cột dữ liệu ghi lại vị trí theo dạng : K01.1-5,14-16 ( giống với cách in giấy trang in )

Em cần tách ra các cột kế bên theo dạng : K01.01 K01.02 K01.03 K01.04 K01.05 K01.14 K01.15 K01.16

Viết vba càng tốt ạ.

Em có file đính kèm, các anh chị xem qua ạ !
Kiểm tra kết quả, có 3 dòng không đúng quy luật
Mã:
Option Explicit
Sub XYZ()
  Dim arr(), res(), S, T, V
  Dim sRow&, i&, j&, n&, c&, fV&, eV&, ke$, vt
  Const sCol& = 200 ' So cot ket qua lon nhat
 
  arr = Sheet1.Range("A2", Sheet1.Range("A1000000").End(xlUp)).Value
  sRow = UBound(arr)
  ReDim res(1 To sRow, 1 To sCol)
  For i = 1 To sRow
    c = 0: ke = Empty
    S = Split("," & arr(i, 1), ",")
    For j = 1 To UBound(S)
      If InStr(1, S(j), ".") Then
        T = Split(S(j), ".")
        ke = T(0) & "."
        vt = T(1)
      Else
        vt = S(j)
      End If
      If ke <> Empty Then
        If InStr(1, vt, "-") Then
          V = Split(vt, "-")
          fV = CLng(V(0))
          eV = CLng(V(1))
        Else
          fV = CLng(vt)
          eV = fV
        End If
        For n = fV To eV
          c = c + 1
          res(i, c) = ke & Format(n, "00")
        Next n
      End If
    Next j
  Next i
  Sheet1.Range("B2").Resize(sRow, sCol) = res
End Sub
 
Upvote 0
Bài này có thể dùng RegEx code sẽ dễ hơn.
pattern = "$.+\."
Chạy lần thứ nhất. Match lấy ra để dùng làm mã tiếp đầu (prefix).
Kế đó đổi mẫu
pattern = (?:[\.,])(\d+)(-\d)?
Chạy lần thứ nhì. Submatch 1 là số bắt đầu, submatch 2 là số kết thúc.

Dữ liệu không đúng chuẩn thì ráng chịu. Thớt có nói: giống như in trang giấy. Diễn giải: nếu đưa cái lệnh không đúng chuẩn thì driver của máy in sẽ:
- Báo lỗi, khong thèm in. HOẶC
- In đại, sai ráng chịu.
 
Lần chỉnh sửa cuối:
Upvote 0
Kiểm tra kết quả, có 3 dòng không đúng quy luật
Mã:
Option Explicit
Sub XYZ()
  Dim arr(), res(), S, T, V
  Dim sRow&, i&, j&, n&, c&, fV&, eV&, ke$, vt
  Const sCol& = 200 ' So cot ket qua lon nhat
 
  arr = Sheet1.Range("A2", Sheet1.Range("A1000000").End(xlUp)).Value
  sRow = UBound(arr)
  ReDim res(1 To sRow, 1 To sCol)
  For i = 1 To sRow
    c = 0: ke = Empty
    S = Split("," & arr(i, 1), ",")
    For j = 1 To UBound(S)
      If InStr(1, S(j), ".") Then
        T = Split(S(j), ".")
        ke = T(0) & "."
        vt = T(1)
      Else
        vt = S(j)
      End If
      If ke <> Empty Then
        If InStr(1, vt, "-") Then
          V = Split(vt, "-")
          fV = CLng(V(0))
          eV = CLng(V(1))
        Else
          fV = CLng(vt)
          eV = fV
        End If
        For n = fV To eV
          c = c + 1
          res(i, c) = ke & Format(n, "00")
        Next n
      End If
    Next j
  Next i
  Sheet1.Range("B2").Resize(sRow, sCol) = res
End Sub
Dạ, cảm ơn anh ạ ! Để mai em đi làm test thử ạ
Bài đã được tự động gộp:

Bài này có thể dùng RegEx code sẽ dễ hơn.
pattern = "$.+\."
Chạy lần thứ nhất. Match lấy ra để dùng làm mã tiếp đầu (prefix).
Kế đó đổi mẫu
pattern = (?:[\.,])(\d+)(-\d)?
Chạy lần thứ nhì. Submatch 1 là số bắt đầu, submatch 2 là số kết thúc.

Dữ liệu không đúng chuẩn thì ráng chịu. Thớt có nói: giống như in trang giấy. Diễn giải: nếu đưa cái lệnh không đúng chuẩn thì driver của máy in sẽ:
- Báo lỗi, khong thèm in. HOẶC
- In đại, sai ráng chịu.
Dạ, dữ liệu em đưa bị sai ấy ạ.

Nguyên lý giống như in trang giấy ấy ạ.

Cảm ơn anh đã chia sẽ cách làm ạ !
 
Upvote 0
Kiểm tra kết quả, có 3 dòng không đúng quy luật
Mã:
Option Explicit
Sub XYZ()
  Dim arr(), res(), S, T, V
  Dim sRow&, i&, j&, n&, c&, fV&, eV&, ke$, vt
  Const sCol& = 200 ' So cot ket qua lon nhat
 
  arr = Sheet1.Range("A2", Sheet1.Range("A1000000").End(xlUp)).Value
  sRow = UBound(arr)
  ReDim res(1 To sRow, 1 To sCol)
  For i = 1 To sRow
    c = 0: ke = Empty
    S = Split("," & arr(i, 1), ",")
    For j = 1 To UBound(S)
      If InStr(1, S(j), ".") Then
        T = Split(S(j), ".")
        ke = T(0) & "."
        vt = T(1)
      Else
        vt = S(j)
      End If
      If ke <> Empty Then
        If InStr(1, vt, "-") Then
          V = Split(vt, "-")
          fV = CLng(V(0))
          eV = CLng(V(1))
        Else
          fV = CLng(vt)
          eV = fV
        End If
        For n = fV To eV
          c = c + 1
          res(i, c) = ke & Format(n, "00")
        Next n
      End If
    Next j
  Next i
  Sheet1.Range("B2").Resize(sRow, sCol) = res
End Sub
Code của anh HieuCD chạy ổn áp lắm ạ, cũng giống code của anh batman1 ạ.
Cảm ơn anh HieuCD đã hỗ trợ nhiều ạ !
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom