Nhờ hỗ trợ tách số ra khỏi chuỗi

Liên hệ QC
Tôi tuân thủ nội quy khi đăng bài

noisy

Thành viên chính thức
Tham gia
9/5/20
Bài viết
65
Được thích
7
Dạ em chào Anh/Chị

Em đang gặp vấn đề với việc tách số ra khỏi chuỗi vì độ dài kí tự, vị trí kí tự cần lấy nằm ở nhiều vị trí khác nhau trong chuỗi nên mặc dù em đã dùng chức năng "Flash Fill" hoặc "text to column' cũng không được nên nhờ Anh Chị xem giúp em ạ.

Em cần tách quy cách hàng hóa ra các cột riêng biệt như sau ạ,

Mong nhận được hỗ trợ từ Anh/Chị ạ.
Em cảm ơn.

Tên hàng hóadàyrộngdài
hàng hóa E2, LMR, Premium 17mmx1220x2440, Laminate
17​
1220​
2440​
 

File đính kèm

  • Tách số khỏi chuỗi.xlsx
    10.1 KB · Đọc: 25
Bài này gần như không khả thi đối với công thức (làm được những rất rắc rối)
Dữ liệu của bạn lại không thống nhất - không chỉ riêng phần vị trí trong chuỗi như bạn diễn tả, mà nó lúc x lúc * tùm lum, vị trí của mm cũng tùm lum nốt.
Bài chỉ có thể dùng VBA, mà phải là dân giàu kinh nghiệm dọn rác mới làm nổi. Biết cách test lô gic và code nữa.

Test lô gic: sau khi tìm ra giải thuật, tự suy tính xem các lô gic đã bao hết các vấn đề chưa.
Test code: tìm các trường hợp rắc rối, kiểm chứng lại. Hoặc chia đoạn dữ liệu để test
 
Bài này gần như không khả thi đối với công thức (làm được những rất rắc rối)
Dữ liệu của bạn lại không thống nhất - không chỉ riêng phần vị trí trong chuỗi như bạn diễn tả, mà nó lúc x lúc * tùm lum, vị trí của mm cũng tùm lum nốt.
Bài chỉ có thể dùng VBA, mà phải là dân giàu kinh nghiệm dọn rác mới làm nổi. Biết cách test lô gic và code nữa.

Test lô gic: sau khi tìm ra giải thuật, tự suy tính xem các lô gic đã bao hết các vấn đề chưa.
Test code: tìm các trường hợp rắc rối, kiểm chứng lại. Hoặc chia đoạn dữ liệu để test
anh xem giúp em với ^^
 
Xem rồi mới phán vậy.
VBA có thể dùng RegEx để tìm các con số đó.
Nhưng bạn cần cho biết những trường hợp ngoại lê:
1. Độ dầy bình thường đặt trước bề rộng và bề dài. Nhưng cũng có trường hợp đặt sau. Vậy thì có phải sắp xếp: nhỏ nhất là dầy, kế đó là rộng, và lớn nhất là bề dài?
2. Có vài chỗ độ dầy (12 ly) nằm ngoài nhóm rộng+dài. Trường hợp này làm cách nào để nhận ra nó? Tôi chưa đạt đến trình độ code AI sao cho có thể đọc và hiểu như người.

Càng nhiều ngoại lệ, cái chuỗi Regex càng trông tá hỏa tam tinh.
Làm ơn đọc hết dữ liệu của mình và xác định hoặc cải chính các lô gic trên. Một khi tôi viết cái chuỗi Regex rồi thì tôi không muốn viết lại, trừ phi nó sai.
 
Tôi chịu thua. Có thép nào dày 1220 mm, rộng 12 và dài 2440. Hoặc dày 2440mm, rộng 12mm và dài 1220 mm như 2 dòng 12 và 13 và nhiều dòng khác

1709049519099.png

2. Có vài chỗ độ dầy (12 ly) nằm ngoài nhóm rộng+dài. Trường hợp này làm cách nào để nhận ra nó?
Làm ơn đọc hết dữ liệu của mình và xác định hoặc cải chính các lô gic trên.
Lại còn có khoảng trắng và không khoảng trắng. Logic thì sâu xa quá, dày 1220 mm và 2440 mm thì hiển hiện trước mắt mà cố làm được.
 
Tôi chịu thua. Có thép nào dày 1220 mm, rộng 12 và dài 2440. Hoặc dày 2440mm, rộng 12mm và dài 1220 mm như 2 dòng 12 và 13 và nhiều dòng khác
...
Lại còn có khoảng trắng và không khoảng trắng. Logic thì sâu xa quá, dày 1220 mm và 2440 mm thì hiển hiện trước mắt mà cố làm được.
1) Đây là ván Melamine, ván ép không có chiều. Vì vậy, cứ số nhỏ nhất là độ dầy (cự ly), kế là rộng (bề bản), và lớn nhất là dài.

2) Regex có thể dùng những mẫu để lấy, và * cũng dễ dàng đổi thành x. Chỉ cái lộn xộn nhất là dòng 11, độ dày lọt ra ngoài và gọi là "ly". Cái này cũng có thể vượt qua.
Nhưng cái mà tôi chịu thua là còn bao nhiêu kiểu định dạng nữa, ví dụ gọi là mil,...
 
1) Đây là ván Melamine, ván ép không có chiều. Vì vậy, cứ số nhỏ nhất là độ dầy (cự ly), kế là rộng (bề bản), và lớn nhất là dài.

2) Regex có thể dùng những mẫu để lấy, và * cũng dễ dàng đổi thành x. Chỉ cái lộn xộn nhất là dòng 11, độ dày lọt ra ngoài và gọi là "ly". Cái này cũng có thể vượt qua.
Nhưng cái mà tôi chịu thua là còn bao nhiêu kiểu định dạng nữa, ví dụ gọi là mil,...
Tôi hiểu ý anh ở bài 4, nhưng tôi viết bài 5 nói về sự cẩu thả cộng thêm vào việc dữ liệu rác. Tiêu đề cột là "Dày" mà bên dưới điền tay theo kiểu đó.
 
Dạ em cảm ơn góp ý của các Anh/Chị,

Em cũng thấy vấn đề là cái file dữ liệu lộn xộn quá nên chắc em phải làm thủ công lại rồi,

Em cảm ơn mọi người đã dành thời gian cho em ạ.
 
1. Bài này đầu tiên chỉ cần: =SUBSTITUTE(A12,"*","x") là đưa được 90% về giống.
2. Sau tìm cách code để lọc ra chuỗi có dạng " *x*x* ", rồi mid, left, subti tè le là ra.
3. Một số sai thì sửa thủ công, 10% đổ lại.

1 với tớ là muỗi. 2 với tớ là chịu trết, 3 là nông dân chân lấm tay bùn. --=0 --=0 --=0
 
Giải với Regex:

Dấu cách trong chuỗi không quan trọng và chỉ mất công làm chuỗi Regex dài hơn. Vì vậy, ta có thể dùng hàm Replace để loại hết các khoảng trắng trước khi Regex.Test

Cách 1:
- Chuỗi Regex: (\d+)m{0,2}[x\*](\d+)m{0,2}[x\*](\d+)m{0,2}
- Nếu Test đúng, sẽ capture được 3 submatches.
-- Sort 3 số này sẽ được dày x rộng x dài
- Nếu Test sai, chuyển chuỗi: (\d+)ly.*(\d+)m{0,2}[x\*](\d+)m{0,2}|(\d+)m{0,2}[x\*](\d+)m{0,2}.*(\d+)ly và Test lại.
-- Nếu Test đúng, sẽ capture được 3 submatches. Tuy nhiên, nhóm 1 cho ra 3 phần tử đầu (1,2,3) và nhóm 2 cho ra 3 phần tử kế (4,5,6); tùy theo cái nào trúng.

Cách 2:
- Chuỗi: (\d+)m{0,2}[x\*](\d+)m{0,2}[x\*](\d+)m{0,2}|(\d+)ly.*(\d+)m{0,2}[x\*](\d+)m{0,2}|(\d+)m{0,2}[x\*](\d+)m{0,2}.*(\d+)ly
- Chỉ Test 1 lần.

Chú thích: bài chỉ có tính cách minh họa giải pháp. Chuỗi Regex chỉ là đại khái, chưa thử code.
 
@noisy
Chạy thử code củ chuối dưới đây. Các dòng trống chắc phải làm tay
Mã:
Option Explicit

Sub tach()
Dim Nguon
Dim Kq
Dim Chuoi
Dim rws
Dim i, j, k

Nguon = Sheet1.Range("A2:A36")
rws = UBound(Nguon)
ReDim Kq(1 To rws, 1 To 3)
ReDim Tam(2)
With CreateObject("VBScript.RegExp")
    .Pattern = "[^a-zA-Z](\d+)\D+(\d{4})\D+(\d+)"
    
    For i = 1 To rws
        Nguon(i, 1) = Replace(Nguon(i, 1), "(", " ")
        If .test(Nguon(i, 1)) Then
            Chuoi = Trim(.Execute(Nguon(i, 1))(0))
            If IsNumeric(Left(Chuoi, 1)) Then
                Kq(i, 1) = CInt(.Execute(Nguon(i, 1))(0).submatches(0))
                Kq(i, 2) = CInt(.Execute(Nguon(i, 1))(0).submatches(1))
                Kq(i, 3) = CInt(.Execute(Nguon(i, 1))(0).submatches(2))
                
                For j = 1 To 2
                    For k = j + 1 To 3
                        If Kq(i, k) < Kq(i, j) Then
                            rws = Kq(i, k)
                            Kq(i, k) = Kq(i, j)
                            Kq(i, j) = rws
                        End If
                    Next k
                Next j
            End If
        End If
    Next i
End With

With Sheet1
    .Range("F2").Resize(UBound(Kq), UBound(Kq, 2)).Clear
    .Range("F2").Resize(UBound(Kq), UBound(Kq, 2)) = Kq
    .Range("F2").Resize(UBound(Kq), UBound(Kq, 2)).Columns.AutoFit
End With
End Sub
 
@noisy
Thêm 1 code củ chuối nữa (theo gợi ý 1 của bạn @cantl) cho bạn tham khảo (chưa test kỹ)
Mã:
Option Explicit

Sub Sua()
Dim i&, j&, Lr, t&, k&, n&, R&
Dim Arr(), Kq(), KQ1(), S, S1
Dim WF As Object
Dim b As Boolean
Dim Rng As Range

Set WF = Application.WorksheetFunction
With Sheet1
    Lr = .Cells(10000, 1).End(xlUp).Row
    Arr = .Range("A2:A" & Lr).Value
    R = UBound(Arr)
    ReDim Kq(1 To R, 1 To 3)

    For i = 1 To R
        Arr(i, 1) = WF.Substitute(Arr(i, 1), "(", " ")
        Arr(i, 1) = WF.Substitute(Arr(i, 1), ")", " ")
        Arr(i, 1) = WF.Substitute(Arr(i, 1), "mm", "")
        Arr(i, 1) = WF.Substitute(VBA.Trim(Arr(i, 1)), "ly", "X")
        Arr(i, 1) = WF.Substitute(VBA.Trim(Arr(i, 1)), "*", "x")
        Arr(i, 1) = WF.Substitute(VBA.Trim(Arr(i, 1)), "x", "X")
        Arr(i, 1) = WF.Substitute(VBA.Trim(Arr(i, 1)), " X ", "X")
        n = 0
        If InStr(Arr(i, 1), "X") Then
            S = Split(Trim(Arr(i, 1)), " ")
                For t = 0 To UBound(S)
                    If InStr(S(t), "X") Then
                        If InStr(S(t), ",") Then S(t) = Left(S(t), Len(S(t)) - 1)
                            S1 = Split(Trim(S(t)), "X")
                                For k = LBound(S1) To UBound(S1)
                                    S1(k) = VBA.Trim(S1(k))
                                    If InStr(S1(k), ",") Then S1(k) = WF.Substitute(S1(k), ",", ".")
                                    If IsNumeric(S1(k)) Then n = n + 1: Kq(i, n) = S1(k)
                                Next k
                            If n = 3 Then n = 0: Exit For
                    End If
                Next t
        End If
    Next i

.Range("B2").Resize(R, 3).ClearContents
.Range("B2").Resize(R, 3) = Kq
ReDim KQ1(1 To R, 1 To 3)
On Error Resume Next
For i = 2 To R + 2
b = True
If --.Range("E" & i) < --.Range("F" & i) And --.Range("F" & i) < --.Range("G" & i) Then b = True Else b = False
    If b = False Then
        Set Rng = .Range("E" & i & ":G" & i)
        KQ1(i - 1, 1) = WF.Small(Rng, 1)
        KQ1(i - 1, 2) = WF.Small(Rng, 2)
        KQ1(i - 1, 3) = WF.Small(Rng, 3)
    Else
        KQ1(i - 1, 1) = --.Range("E" & i)
        KQ1(i - 1, 2) = --.Range("F" & i)
        KQ1(i - 1, 3) = --.Range("G" & i)
    End If
Next i
.Range("B2").Resize(R, 3).ClearContents
.Range("B2").Resize(R, 3) = KQ1
End With
MsgBox "Done"
End Sub
 
Dạ em rất cảm ơn sự giúp đỡ của các Anh/Chị.
 
Thử mã sau:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
pmh = Table.PromoteHeaders(Source, [PromoteAllScalars=true]),
replaceValues = {"*", "mm", "X", " mặt AB",")"},
newValues = {"x", "", "x", "x0x0",""},
rpv = List.Accumulate(List.Zip({replaceValues, newValues}), pmh, (state, current) =>
Table.ReplaceValue(state, current{0}, current{1}, Replacer.ReplaceText, {"Tên hàng hóa"})),
Tachcottheodb = Table.SplitColumn(rpv, "Tên hàng hóa", Splitter.SplitTextByDelimiter("x", QuoteStyle.Csv), {"Dày","Rộng","Cao"}),
ExpandedTxtSplit = Table.AddColumn(Tachcottheodb, "Dàyt", each Text.Trim(List.Last(List.Select(Splitter.SplitTextByAnyDelimiter({"a".."z", " ", "("})([Dày]), (x) => x <> "")))),
daocot = Table.ReorderColumns(ExpandedTxtSplit,{"Dày", "Dàyt", "Rộng", "Cao"}),
Splitcot3 = Table.SplitColumn(daocot, "Cao", Splitter.SplitTextByCharacterTransition({"0".."9"}, (c) => not List.Contains({"0".."9"}, c)), {"Caot"}),
xulytxt = Table.TransformColumns(Splitcot3,{{"Rộng", Text.Trim, type text}, {"Caot", each Text.Trim(_, {"("}), type text}})
in
xulytxt
1709426256697.png
Kết quả vẫn bị sai dòng số 5, tôi chưa coi kỹ dòng số 5 khác dòng 3, dòng 4 như nào mà chạy sai. Nói chung là làm công việc mà số liệu chả có quy luật như vầy rất mệt. Thử tưởng tượng dữ liệu hàng ngàn, hàng vạn dòng mà quy luật tùm lum thì có mà chết toi
 
Thử mã sau:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
pmh = Table.PromoteHeaders(Source, [PromoteAllScalars=true]),
replaceValues = {"*", "mm", "X", " mặt AB",")"},
newValues = {"x", "", "x", "x0x0",""},
rpv = List.Accumulate(List.Zip({replaceValues, newValues}), pmh, (state, current) =>
Table.ReplaceValue(state, current{0}, current{1}, Replacer.ReplaceText, {"Tên hàng hóa"})),
Tachcottheodb = Table.SplitColumn(rpv, "Tên hàng hóa", Splitter.SplitTextByDelimiter("x", QuoteStyle.Csv), {"Dày","Rộng","Cao"}),
ExpandedTxtSplit = Table.AddColumn(Tachcottheodb, "Dàyt", each Text.Trim(List.Last(List.Select(Splitter.SplitTextByAnyDelimiter({"a".."z", " ", "("})([Dày]), (x) => x <> "")))),
daocot = Table.ReorderColumns(ExpandedTxtSplit,{"Dày", "Dàyt", "Rộng", "Cao"}),
Splitcot3 = Table.SplitColumn(daocot, "Cao", Splitter.SplitTextByCharacterTransition({"0".."9"}, (c) => not List.Contains({"0".."9"}, c)), {"Caot"}),
xulytxt = Table.TransformColumns(Splitcot3,{{"Rộng", Text.Trim, type text}, {"Caot", each Text.Trim(_, {"("}), type text}})
in
xulytxt
View attachment 299323
Kết quả vẫn bị sai dòng số 5, tôi chưa coi kỹ dòng số 5 khác dòng 3, dòng 4 như nào mà chạy sai. Nói chung là làm công việc mà số liệu chả có quy luật như vầy rất mệt. Thử tưởng tượng dữ liệu hàng ngàn, hàng vạn dòng mà quy luật tùm lum thì có mà chết toi
Dòng số 5: dấu trắng trước 11mm có mã là 160 nên không bắt được.

Bài này cố cưỡng có thể lấy được tất cả kết quả nhưng có lẽ là không nên làm. Lấy hết xong cũng không khẳng định mức độ chính xác của dữ liệu thì có khác gì ôm bom, chết lúc nào không biết.
 
Web KT
Back
Top Bottom