2011-08-16 37 views
14

Tôi có một vấn đề đơn giản mà tôi hy vọng sẽ giải quyết mà không cần sử dụng VBA nhưng nếu đó là cách duy nhất nó có thể được giải quyết, vì vậy hãy là nó.Làm thế nào để trích xuất văn bản trong một chuỗi văn bản

Tôi có tệp có nhiều hàng (tất cả một cột). Mỗi hàng có dữ liệu trông giống như sau:

1 7.82E-13> gi | 297848936 | ref | XP_00 | 4-hydroxide gi | 297338191 | gb | 23343 | randomrandom

2 5.09E-09> gi | 168010496 | ref | xp_00 | 2-pyruvate

vv ...

Điều tôi muốn là cách nào đó để trích xuất chuỗi số bắt đầu bằng "gi |" và kết thúc bằng dấu "|". Đối với một số hàng này có thể có nghĩa là nhiều như 5 số gi, đối với những người khác nó sẽ chỉ là một.

Những gì tôi hy vọng sản lượng sẽ trông như thế sẽ là một cái gì đó như:

297848936,297338191

vv ...

Trả lời

31

Đây là câu trả lời VBA rất linh hoạt khi sử dụng đối tượng regex. Những gì các chức năng hiện là trích xuất tất cả các nhóm phụ phù hợp với nó tìm thấy (thứ bên trong dấu ngoặc đơn), cách nhau bởi bất cứ chuỗi bạn muốn (mặc định là ",").Bạn có thể tìm thông tin về biểu thức thông thường ở đây: http://www.regular-expressions.info/

Bạn sẽ gọi nó là như thế này, giả định rằng chuỗi đầu tiên là trong A1:

=RegexExtract(A1,"gi[|](\d+)[|]") 

Kể từ khi điều này có vẻ cho tất cả sự xuất hiện của "gi |" tiếp theo là một loạt các con số và sau đó một "|", cho dòng đầu tiên trong câu hỏi của bạn, điều này sẽ cung cấp cho bạn kết quả này:

297848936, 297338191 

Chỉ cần chạy này xuống cột và tất cả các bạn đã hoàn tất!

Function RegexExtract(ByVal text As String, _ 
         ByVal extract_what As String, _ 
         Optional separator As String = ", ") As String 

Dim allMatches As Object 
Dim RE As Object 
Set RE = CreateObject("vbscript.regexp") 
Dim i As Long, j As Long 
Dim result As String 

RE.pattern = extract_what 
RE.Global = True 
Set allMatches = RE.Execute(text) 

For i = 0 To allMatches.count - 1 
    For j = 0 To allMatches.Item(i).submatches.count - 1 
     result = result & (separator & allMatches.Item(i).submatches.Item(j)) 
    Next 
Next 

If Len(result) <> 0 Then 
    result = Right$(result, Len(result) - Len(separator)) 
End If 

RegexExtract = result 

End Function 
+4

Oh người đàn ông này là đẹp. Tuyệt đối tuyệt vời. Nghiêm túc, tại sao bạn làm điều này? Nó rất hữu ích nhưng tôi chỉ tò mò tại sao mọi người cho thời gian của họ cho một cái gì đó như thế này? Nó hoàn toàn từ thiện của tất cả các bạn. – Brandon

+15

Bạn đang rất hoan nghênh! Đối với lý do tại sao tôi dành thời gian: Tôi làm điều đó bởi vì những người khác làm điều đó. Tôi nghĩ nó giống như 'trả tiền về phía trước'. Tôi giúp đỡ người khác bởi vì một ngày, họ sẽ giúp tôi với một số mã, và những người tôi giúp đỡ sẽ giúp những người khác, vv :) ​​ – aevanko

+2

Regex là một cách tuyệt vời để đi. 1 Đối với bản thân mình, tôi trả lời các câu hỏi bởi vì đó là niềm vui và một cách tuyệt vời để học/thực hành. Hơn nữa, như Issun nói, tôi đã nhận được sự giúp đỡ tuyệt vời từ những người hào phóng và tài năng trong các nhóm tin và các diễn đàn khác trong những năm qua. –

5

Ở đây nó được (giả sử dữ liệu trong cột A)

=VALUE(LEFT(RIGHT(A1,LEN(A1) - FIND("gi|",A1) - 2), 
FIND("|",RIGHT(A1,LEN(A1) - FIND("gi|",A1) - 2)) -1)) 

Không phải công thức đẹp nhất, b ut nó sẽ làm việc để trích xuất số.

Tôi vừa nhận thấy vì bạn có hai giá trị mỗi hàng với đầu ra được phân tách bằng dấu phẩy. Bạn sẽ cần phải kiểm tra xem có một trận đấu thứ hai, trận đấu thứ ba, vv để làm cho nó hoạt động với nhiều số cho mỗi ô.

Trong tham chiếu đến mẫu chính xác của bạn (giả sử 2 giá trị tối đa cho mỗi cell) đoạn mã sau sẽ làm việc:

=IF(ISNUMBER(FIND("gi|",$A1,FIND("gi|", $A1)+1)),CONCATENATE(LEFT(RIGHT($A1,LEN($A1) 
- FIND("gi|",$A1) - 2),FIND("|",RIGHT($A1,LEN($A1) - FIND("gi|",$A1) - 2)) -1), 
", ",LEFT(RIGHT($A1,LEN($A1) - FIND("gi|",$A1,FIND("gi|", $A1)+1) 
- 2),FIND("|",RIGHT($A1,LEN($A1) - FIND("gi|",$A1,FIND("gi|", $A1)+1) - 2)) 
-1)),LEFT(RIGHT($A1,LEN($A1) - FIND("gi|",$A1) - 2), 
FIND("|",RIGHT($A1,LEN($A1) - FIND("gi|",$A1) - 2)) -1)) 

Làm thế nào của đó cho xấu xí? Một giải pháp VBA có thể tốt hơn cho bạn, nhưng tôi sẽ để nó ở đây cho bạn.

Để tối đa 5 con số, hãy nghiên cứu mẫu và sử dụng lại thủ công trong công thức. CNTT sẽ lâu!

+0

Haha đã làm việc tuyệt vời. Cảm ơn bạn đã giúp đỡ. Bạn nói đúng, điều này sẽ nhanh chóng xấu đi. Có lẽ tôi nên gắn bó với VBA sau đó? Tôi không thực sự nhớ tôi chỉ nghĩ rằng mọi người có thể tìm thấy câu trả lời VBA là quá cồng kềnh: P Thành thật mà nói, tôi không chắc tôi có bất kỳ đầu mối những gì đang xảy ra trong mã mà bạn đã bao gồm! Tôi không chắc chắn nơi tôi sẽ cần phải thực hiện chỉnh cho nó để đi lên đến 5 hoặc 7 con số. – Brandon

+0

Nếu bạn chọn phương pháp này, hãy sử dụng hàm MID() thay vì LEFT và RIGHT. Điều đó sẽ làm cho mã dễ đọc hơn. –

2

Tôi có thể chia dữ liệu trước tiên trên dấu phân tách | bằng thuật sĩ chuyển đổi văn bản thành cột. Trong Excel 2007 có trên tab Dữ liệu, Công cụ dữ liệu nhóm và sau đó chọn Văn bản thành cột. Chỉ định Khác:| làm dấu phân tách.

Từ dữ liệu mẫu bạn đã đăng, có vẻ như sau khi bạn thực hiện điều này, tất cả các số sẽ nằm trong cùng một cột để bạn có thể xóa các cột mà bạn không muốn.

+0

Tôi thực sự ban đầu nghĩ rằng điều này, nhưng tôi nên đề cập đến rằng có những lúc mà sau khi cột gb có số là tốt. Vì vậy, trong chuỗi ví dụ tôi liệt kê, bạn cũng có thể nhận được một cái gì đó như "randomrandomrandom gb | 13151414 |" Tôi vừa thay đổi bài đăng gốc để phản ánh điều đó. – Brandon

2

Khi những người khác trình bày giải pháp không có VBA ... Tôi sẽ trình bày giải pháp không sử dụng. Bây giờ, là cuộc gọi của bạn để sử dụng nó hoặc không.

Chỉ cần thấy rằng @Issun đã trình bày giải pháp với regex, rất hay! Dù bằng cách nào, sẽ trình bày một giải pháp 'khiêm tốn' cho câu hỏi, chỉ sử dụng VBA 'đơn giản'.

Option Explicit 
Option Base 0 

Sub findGi() 

    Dim oCell As Excel.Range 
    Set oCell = Sheets(1).Range("A1") 

    'Loops through every row until empty cell 
    While Not oCell.Value = "" 

     oCell.Offset(0, 1).Value2 = GetGi(oCell.Value) 
     Set oCell = oCell.Offset(1, 0) 

    Wend 

End Sub 

Private Function GetGi(ByVal sValue As String) As String 

    Dim sResult As String 
    Dim vArray As Variant 
    Dim vItem As Variant 
    Dim iCount As Integer 

    vArray = Split(sValue, "|") 
    iCount = 0 

    'Loops through the array... 
    For Each vItem In vArray 

     'Searches for the 'Gi' factor... 
     If vItem Like "*gi" And UBound(vArray) > iCount + 1 Then 

      'Concatenates the results... 
      sResult = sResult & vArray(iCount + 1) & "," 

     End If 

     iCount = iCount + 1 

    Next vItem 

    'And removes trail comma 
    If Len(sResult) > 0 Then 

     sResult = Left(sResult, Len(sResult) - 1) 

    End If 

    GetGi = sResult 

End Function 
+0

Ah hah đây cũng là một điều tuyệt vời. Tôi thấy rằng VBA có thể là một cách tiếp cận thực sự suôn sẻ với điều này sau đó, tôi đã không nhận ra điều đó. Cảm ơn một lần nữa vì sự giúp đỡ của bạn! – Brandon

Các vấn đề liên quan