Разделение адреса с различными разделителями на улицу, город, штат, почтовый индекс и страну

Мои данные в Excel. У меня есть несколько листов данных, где адрес всегда находится в одном столбце на каждом листе. Примеры форматов адресов включают:

1155 15th Street NW Suite 600 Вашингтон, округ Колумбия 20005 США
4600 Emperor Blvd # 200 Durham, NC 27703-8577 США
200 Stevens Drive Philadelphia, PA 19113 США
505 City Parkway West Orange, CA 92868 США
550 S Caldwell St, Charlotte, NC 28202-2633 US
1643 NW 136th Ave Ste H200 Sunrise, FL 33323-2857 США

Я пробовал приведенный ниже код, но в этот момент получаю ошибку в коде sCity = Trim (Mid $ (rCell.Value, Len (sAddress) + 1, lStatePos - Len (sAddress) - 1))

Может ли кто-нибудь помочь мне понять, как решить эту проблему?

Sub SplitAddresses()

    Dim vaStates As Variant
    Dim vaStreets As Variant
    Dim i As Long
    Dim rCell As Range
    Dim sAddress As String
    Dim sCity As String, sState As String
    Dim sZip As String
    Dim lStreetPos As Long, lStatePos As Long

    vaStates = Array(“ AL “, “ AK “, “ AZ “, “ AR “, “ CA “, “ CO “, “ CT “, “ DE “, “ DC “, “ FL “, “ GA “, “ HI “, “ ID “, “ IL “, “ IN “, “ IA “, “ KS “, “ KY “, “ LA “, “ ME “, “ MD “, “ MA “, “ MI “, “ MN “, “ MS “, “ MO “, “ MT “, “ NE “, “ NV “, “ NH “, “ NJ “, “ NM “, “ NY “, “ NC “, “ ND “, “ OH “, “ OK “, “ OR “, “ PA “, “ RI “, “ SC “, “ SD “, “ TN “, “ TX “, “ UT “, “ VT “, “ VA “, “ WA “, “ WV “, “ WI “, “ WY “, “ GU “, “ PR “)
    vaStreets = Array(" CR ", " BLVD ", " RD ", " ST ", " AVE ", " CT ")

    For Each rCell In Sheet1.Range("A1:A5").Cells
        sAddress = "": sCity = "": sZip = "": sState = ""
        For i = LBound(vaStreets) To UBound(vaStreets)
            lStreetPos = InStr(1, rCell.Value, vaStreets(i))
            If lStreetPos > 0 Then
                sAddress = Trim(Left$(rCell.Value, lStreetPos + Len(vaStreets(i)) - 1))
                Exit For
            End If
        Next i

        For i = LBound(vaStates) To UBound(vaStates)
            lStatePos = InStr(1, rCell.Value, vaStates(i))
            If lStatePos > 0 Then
                sCity = Trim(Mid$(rCell.Value, Len(sAddress) + 1, lStatePos - Len(sAddress) - 1))
                sState = Trim(Mid$(rCell.Value, lStatePos + 1, Len(vaStates(i)) - 1))
                sZip = Trim(Mid$(rCell.Value, lStatePos + Len(vaStates(i)), Len(rCell.Value)))
                Exit For
            End If
        Next i

        rCell.Offset(0, 1).Value = "'" & sAddress
        rCell.Offset(0, 2).Value = "'" & sCity
        rCell.Offset(0, 3).Value = "'" & sState
        rCell.Offset(0, 4).Value = "'" & sZip

    Next rCell

End Sub

Я получаю следующую ошибку: error_image


person Kristan    schedule 13.10.2020    source источник
comment
может ошибка исходит из середины $? Разве это не должно быть просто Мид? или, если это формула, возможно, вы захотите заключить их в двойные кавычки. EX: = ОБРЕЗАТЬ (...) -1))   -  person victor song    schedule 13.10.2020
comment
Mid $ - это типизированная функция, и это нормально. Вам нужно изменить кавычки на правильный тип, так как они не должны быть «»   -  person QHarr    schedule 13.10.2020
comment
С приведенными вами примерами и изменением кавычек код работает нормально, но не похоже, что логика верна.   -  person QHarr    schedule 13.10.2020
comment
Я бы не стал пробовать это самостоятельно. Скорее склонился бы к сторонней библиотеке. На этом сайте есть ряд вопросов по этой теме.   -  person Brian M Stafford    schedule 13.10.2020
comment
Я опубликовал сообщение об ошибке, которое появляется после исправления цитат.   -  person Kristan    schedule 13.10.2020


Ответы (2)


С вашим комментарием о том, что существует символ возврата для отделения адреса от города и обычный формат адресов: street|City, State Zip Country алгоритм становится намного проще, поскольку ряд Split функций может разделять части адреса.

Я также использовал оператор Type - не обязательно, но делает код более понятным, IMO. В зависимости от форматирования некоторые из операторов Trim могут не понадобиться, но они не повредят.

Обратите внимание, что вы можете изменить диапазоны / листы вашего источника данных и расположения результатов в соответствии с вашими конкретными требованиями.

РЕДАКТИРОВАТЬ: Я только что прочитал ваш комментарий о том, что может быть несколько returns перед обратным отправлением из города с почтового адреса.

Код для .street изменен соответственно

Option Explicit
Type Address
    street As String
    city As String
    state As String
    zip As String
    country As String
End Type
Sub splitAddresses()
    Dim wsSrc As Worksheet, wsRes As Worksheet, rRes As Range
    Dim vSrc As Variant, vRes As Variant
    Dim myAdr As Address
    Dim v, w, x, y
    Dim I As Long
    
Set wsSrc = Worksheets("sheet1")

'read into vba array for faster processing
With wsSrc
    vSrc = .Range(.Cells(2, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With

Set wsRes = Worksheets("Sheet1")
    Set rRes = wsRes.Cells(1, 3)


ReDim vRes(0 To UBound(vSrc), 1 To 5)

'Headers
    vRes(0, 1) = "Street"
    vRes(0, 2) = "City"
    vRes(0, 3) = "State"
    vRes(0, 4) = "Zip"
    vRes(0, 5) = "Country"
    
For I = 1 To UBound(vSrc)
    v = Split(vSrc(I, 1), vbLf)
    With myAdr
        y = v
        ReDim Preserve y(UBound(y) - 1)
        .street = WorksheetFunction.Trim(Join(y, " "))

    w = Split(Trim(v(UBound(v))), ",")
        .city = w(0)
    
    x = Split(Trim(w(1)))
        .state = Trim(x(0))
        .zip = Trim(x(1))
        .country = Trim(x(2))
    
    vRes(I, 1) = .street
    vRes(I, 2) = .city
    vRes(I, 3) = .state
    vRes(I, 4) = .zip
    vRes(I, 5) = .country
End With

Set rRes = rRes.Resize(rowsize:=UBound(vRes, 1) + 1, columnsize:=UBound(vRes, 2))
With rRes
    .EntireColumn.Clear
    .Value = vRes
    .Rows(1).Font.Bold = True
    .Columns(4).NumberFormat = "@"
    .EntireColumn.AutoFit
End With
    
Next I

End Sub

введите описание изображения здесь

person Ron Rosenfeld    schedule 16.10.2020
comment
См. Недавнее изменение, чтобы исключить вероятность того, что до того, как улица отделяется от города, будет несколько возвратов. - person Ron Rosenfeld; 16.10.2020
comment
Похоже, это будет полезно. Однако я получаю сообщение об ошибке при первом наборе. Мои данные находятся в столбце G, начиная с ячейки G5. То же самое на всех листах в книге. @ Рон Розенфельд - person Kristan; 19.10.2020
comment
@Kristan Обратите внимание, что вы можете изменить диапазоны / листы вашего источника данных и расположения результатов в соответствии с вашими конкретными требованиями. Просмотрите изменения, которые вы внесли в код, чтобы адаптировать его к вашей собственной настройке. - person Ron Rosenfeld; 19.10.2020
comment
Мои извинения. Я говорю, что не знаю, как настроить код, чтобы он применялся ко всем листам для диапазона столбца G. Вы можете мне с этим помочь? - person Kristan; 19.10.2020
comment
Нет. В рабочей тетради 23 рабочих листа. Все данные находятся в столбце G и начинаются в строке 5. - person Kristan; 20.10.2020
comment
@Kristan Сначала заставьте его работать на одном листе, установив wsSrc, vSrc и rRes на правильные листы и диапазоны. - person Ron Rosenfeld; 20.10.2020
comment
Мне жаль. Я новичок в VBA. Я попытался ввести свои значения в свой первый рабочий лист «Поведенческое здоровье» и свой диапазон G5: G, и ни один из них у меня не работает правильно. - person Kristan; 20.10.2020
comment
@Kristan Хммм. Предоставленный вами код выглядит так, как будто его написал не новичок. Вам нужно изменить имена рабочих листов и адреса диапазона в коде. Имя рабочего листа выглядит как sheet1, а диапазоны определяются кодом, который выглядит как .Cell(rowNum,colNum). Таким образом, вы должны заменить sheet1 именем своего рабочего листа, а также изменить обозначение строки и столбца в свойстве Cells, чтобы отразить ваше местоположение. Взгляните на справку VBA для объекта Range и свойства Cells, чтобы лучше понять. - person Ron Rosenfeld; 20.10.2020
comment
Я получаю ошибки при обновлении кода следующим образом: Set wsSrc = Worksheets("Behavioral Health") Set rRes = Range("G5:G") 'read into vba array for faster processing With wsSrc vSrc = .Range(.Cells(2, 1), .Cells(.Rows.Count, 1).End(xlUp)) End With Set wsRes = Worksheets("Behavioral Health") Set rRes = wsRes.Cells(1, 3) - person Kristan; 23.10.2020
comment
@Kristan wsSrc & wsRes правы. Поскольку ваши данные начинаются с G5, диапазон, который вам нужно прочитать в vSrc, должен начинаться с G5 (который может быть представлен как Cells(5,7). Итак, vSrc = .Range(.Cells(5, 7), .Cells(.Rows.Count, 7).End(xlUp)). И, поскольку вы не хотите перезаписывать свои данные своими результатами, если вы записывая на том же листе, вам нужно установить rRes в соответствующее место. например: Set rRes = wsRes.Cells(1, 1) запустит вывод результатов в A1. Если вы записали его на другой лист, вы бы изменили wsRes, чтобы отразить имя рабочего листа результатов. - person Ron Rosenfeld; 23.10.2020
comment
Я получаю сообщение об ошибке Type Address с обновлением кода: Sub splitAddresses() Dim wsSrc As Worksheet, wsRes As Worksheet, rRes As Range Dim vSrc As Variant, vRes As Variant Dim myAdr As Address Dim v, w, x, y Dim I As Long Set wsSrc = Worksheets("Behavioral Health") Set rRes = Range("G5:G") 'read into vba array for faster processing With wsSrc vSrc = .Range(.Cells(5, 7), .Cells(.Rows.Count, 7).End(xlUp)) End With Set wsRes = Worksheets("Behavioral Health") Set rRes = wsRes.Cells(5, 8) ReDim vRes(0 To UBound(vSrc), 1 To 5) - person Kristan; 23.10.2020
comment
@Kristan I get an error - довольно бесполезная информация. Есть ли причина, по которой вы не хотите указывать, в чем заключается ошибка? Кроме того, я не понимаю, почему у вас Set rRes = Range("G5:G"). Чего вы ожидаете от этой линии? - person Ron Rosenfeld; 24.10.2020
comment
Я получаю ошибку компиляции: не могу определить общедоступный тип, определяемый пользовательской ошибкой, в объектном модуле в Type Address. Я просто удалил Set rRes = Range("G5:G") из кода и получаю ту же ошибку, что и раньше. - person Kristan; 26.10.2020
comment
@Kristan Похоже, ваш код находится не в том месте. Обычный код VBA должен входить в обычные модули. Модули Worksheet, Workbook и Class обычно зарезервированы для определенных целей - и это НЕ одно из них. - person Ron Rosenfeld; 26.10.2020

В вашей логике разделения есть некоторые несоответствия, не считая того, что вам придется сравнивать массив улиц в верхнем регистре также со строковыми значениями Ucase().

Однако хорошие новости - поскольку вы, кажется, применяете последовательную логику адресации, то есть группируете город, штат + почтовый индекс вокруг последнего разделителя двоеточия, вы можете попробовать следующий код:

Option Explicit             ' declaration head of code module
Enum c                      ' define column constants
    [_Start] = 0
    add1
    City
    State
    Zip
End Enum

Sub SplitAddresses()
With Sheet1
    'define dataset
    Dim lastRow As Long: lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
    Dim rng As Range: Set rng = .Range("A2:A" & lastRow)
    'assign to variant datafield array (provide for 4 columns: Add+City+State+ZIP)
    Dim v: v = rng.Resize(columnsize:=4).Value2
    'split data
    doSplit v
    'write split results to any target, e.g. B:B
    .Range("B2").Resize(UBound(v), 4) = v
End With
End Sub

Справочная процедура doSplit

Sub doSplit(data)
Dim i As Long
For i = LBound(data) To UBound(data)
    Dim curAddress As String: curAddress = data(i, c.add1)
    
    Dim tokens, tmp
    tokens = Split(curAddress, ",")
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    'a) analyze string part after last ","
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    tmp = Split(Trim(tokens(UBound(tokens))) & " ", " ", 2)
    'aa) add State + Zip (to columns 3..4)
    data(i, c.State) = tmp(0): data(i, c.Zip) = tmp(1)
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    'b) analyze first string part
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    tmp = Split(tokens(UBound(tokens) - 1), " ")
    'data(i, c.City) = tmp(UBound(tmp))   '<< only for 1-word city names
     data(i, c.City) = getCity(tmp)       '<< see edit below
    'bb) add City + Address
    data(i, c.add1) = Split(curAddress, data(i, c.City), 2)(0)
    data(i, c.add1) = Replace(data(i, c.add1), ",", "")
Next i
End Sub

Функция справки // Редактировать из-за комментария @ RonRosenfeld

Так как названия городов будут состоять из составных слов, присвоение строки города в приведенном выше подпункте должно быть изменено с data(r, c.City) = tmp(UBound(tmp)) на

    data(r, c.City) = getCity(tmp)  ' << function call

Функция getCity()

Включает проверки для common первых частей как «Север», «Запад» или «Новый», чтобы по крайней мере не проверять исчерпывающий список с составными названиями городов. Все остальные необходимые названия городов, содержащие более одного слова, необходимо указать в дополнительном списке cities:

Function getCity(tmp) As String
'Purp.: return valid city names of either one or two parts
'[1]Definitions
    'a) List common first parts of city names like "West" in "West Orange"
        Dim common$: common = "North,West,South,East,Grand,New"
    'b) List all other needed cities consisting of compound words
        Dim cities$: cities = "Sterling Heights,Ann Arbor"
'[2]Get potential city name
    'a) Define tmp indices of potential city tokens
        Dim first&: first = UBound(tmp) - 1
        Dim secnd&: secnd = UBound(tmp)
    'b) Build city name as compound string of tmp tokens
        Dim City As String
        City = Trim(IIf(first < 0, "", tmp(first) & " ") & tmp(secnd))
'[3]Check common first parts plus additional cities list
    'a) Check for common name parts like e.g. "West" in "West Orange"
        If InStr(common & ",", tmp(first) & ",") Then getCity = City: Exit Function
    'b) Check rest in listed cities and return function result
        getCity = IIf(InStr(cities, City) > 0, City, tmp(secnd))
End Function

включает названия городов, состоящие из двух слов

person T.M.    schedule 13.10.2020
comment
У меня проблема с названиями городов, которые состоят более чем из одного слова. Например, без списка, как определить, является ли город в строке 5 Orange или West Orange - person Ron Rosenfeld; 13.10.2020
comment
Цените вашу подсказку, пожалуйста, смотрите редактирование с дополнительной функцией. - @RonRosenfeld - person T.M.; 14.10.2020
comment
@ T.M. Это определенно выглядит многообещающим. Я получаю сообщение об ошибке Нижний индекс вне допустимого диапазона (ошибка 9) в этой строке: `` tmp = Split (Trim (tokens (UBound (tokens))) &,, 2) `` - person Kristan; 14.10.2020
comment
@Kristan Можете ли вы сказать мне текущую строку, которая вызывает проблему? - Возможно, у вас есть пустые ячейки между первой и последней строкой. Если это так, заключите все блоки a) и b) в процедуру doSplit() с условием If UBound(tokens) > 0 Then ... End If (т.е. строка End If непосредственно перед Next i) - person T.M.; 14.10.2020
comment
@ T.M. Это исправление работает. Я больше не получаю ошибку. Однако такие города, как Нью-Йорк, Батон-Руж, Солт-Лейк-Сити и т. Д., Возвращаются разделенными после первого слова. Фактически, данные имеют жесткий возврат перед каждым названием города. В ячейке есть и другие жесткие возвраты, поэтому нам нужно будет использовать окончательный жесткий возврат, чтобы выделить адрес улицы от города. Приветствуются любые обновления кода! - person Kristan; 16.10.2020
comment
Поскольку кажется, что включены символы возврата, см. Ответ @RonRosenfeld. Конечно, вы могли бы развить и мою, добавив заменители. - person T.M.; 16.10.2020