[string] 문자열에서 사용 가능한 주소, 도시, 주, 우편 번호를 파싱합니다.

문제 : Sql Server 2005로 변환 된 Access 데이터베이스의 주소 필드가 있습니다.이 필드는 모두 하나의 필드에 있습니다. 주소의 개별 섹션을 정규화 된 테이블의 해당 필드로 구문 분석해야합니다. 약 4,000 개의 레코드에 대해이 작업을 수행해야하며 반복 가능해야합니다.

가정 :

  1. 미국에서 주소를 가정합니다 (현재)

  2. 입력 문자열에 때때로 수취인 (주소 지정된 사람) 및 / 또는 두 번째 주소 (예 : 스위트 B)가 포함되어 있다고 가정

  3. 상태는 축약 될 수있다

  4. 우편 번호는 표준 5 자리 또는 zip + 4 일 수 있습니다.

  5. 어떤 경우에는 오타가 있습니다

업데이트 : 제기 된 질문에 대한 응답으로 표준이 보편적으로 준수되지 않았으므로 지오 코드뿐만 아니라 개별 값을 저장해야하며 오류는 오타를 의미합니다 (위에서 수정 됨)

샘플 데이터 :

  • AP Croll & Son 2299 Lewes-Georgetown Hwy, 조지 타운, DE 19947

  • 11522 Shawnee Road, 그린 우드 DE 19950

  • 144 Kings Highway, SW 도버, DE 19901

  • 통합 된 Const. 서비스 2 Penns Way Suite 405 New Castle, DE 19720

  • Humes Realty 33 Bridle Ridge Court, 루이스, DE 19958

  • 니콜스 발굴 2742 Pulaski Hwy Newark, DE 19711

  • 2284 Bryn Zion Road, 서머나, DE 19904

  • VEI Dover Crossroads, LLC 1500 서 펜타 인로드, 스위트 100 볼티모어 MD 21

  • 580 노스 듀폰 고속도로 도버, DE 19901

  • PO Box 778 Dover, DE 19903



답변

나는 이런 종류의 파싱에 많은 노력을 기울였다. 오류가 있기 때문에 100 % 정확도는 얻지 못하지만 대부분의 방법으로 시각적 BS 테스트를 수행 할 수있는 몇 가지 방법이 있습니다. 여기에 일반적인 방법이 있습니다. 코드가 아닙니다. 코드를 작성하는 것은 꽤 학문적이기 때문에 이상한 점이 없으며 많은 문자열 처리가 가능합니다.

(이제 샘플 데이터를 게시 했으므로 약간 변경했습니다.)

  1. 뒤로 일하십시오. 우편 번호는 끝 부분에 있으며 XXXXX 또는 XXXXX-XXXX라는 두 가지 알려진 형식 중 하나로 시작합니다. 이 메시지가 나타나지 않으면 아래 도시의 주에 있다고 가정 할 수 있습니다.
  2. 지퍼 앞의 다음 것은 상태가 될 것이며 두 글자 형식이거나 단어로 표시됩니다. 이것들도 무엇인지 알고 있습니다. 50 개 밖에 없습니다. 또한 철자 오류를 보완하는 데 도움이되는 단어를 발음 할 수 있습니다.
  3. 그 전에 도시이며, 아마도 주와 같은 노선에 있을 것 입니다. 우편 번호 데이터베이스 를 사용하여 우편 번호를 기준으로 도시 및 주를 확인하거나 적어도 BS 감지기로 사용할 수 있습니다.
  4. 주소는 일반적으로 한두 줄입니다. 두 번째 줄은 일반적으로 제품군 번호가 있으면 제품군 번호이지만 PO 상자 일 수도 있습니다.
  5. 첫 번째 또는 두 번째 줄에서 이름을 감지하는 것은 거의 불가능하지만 숫자 앞에 접두사가 없으면 (또는 “attn :”또는 “attention to :”접두사가 있으면 힌트를 줄 수 있습니다. 이름 또는 주소 줄인지

이것이 다소 도움이되기를 바랍니다.


답변

문제를 아웃소싱하는 것이 최선의 방법이라고 생각합니다. Google (또는 Yahoo) 지오 코더로 보내십시오. 지오 코더는 위도 / 경도 (여기서는 중요하지 않음)뿐만 아니라 보내지 않은 필드 (ZIP + 4 및 카운티 포함)로 채워진 풍부한 주소 구문 분석도 반환합니다.

예를 들어 “1600 원형 극장 파크 웨이, 캘리포니아 주 마운틴 뷰”구문 분석

{
  "name": "1600 Amphitheatre Parkway, Mountain View, CA, USA",
  "Status": {
    "code": 200,
    "request": "geocode"
  },
  "Placemark": [
    {
      "address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
      "AddressDetails": {
        "Country": {
          "CountryNameCode": "US",
          "AdministrativeArea": {
            "AdministrativeAreaName": "CA",
            "SubAdministrativeArea": {
              "SubAdministrativeAreaName": "Santa Clara",
              "Locality": {
                "LocalityName": "Mountain View",
                "Thoroughfare": {
                  "ThoroughfareName": "1600 Amphitheatre Pkwy"
                },
                "PostalCode": {
                  "PostalCodeNumber": "94043"
                }
              }
            }
          }
        },
        "Accuracy": 8
      },
      "Point": {
        "coordinates": [-122.083739, 37.423021, 0]
      }
    }
  ]
}

이제 그건 구문 분석!


답변

원래 포스터는 오래 전부터 사용되었을 가능성이 있지만 geocoder.us 에서 사용 하는 Perl Geo :: StreetAddress : US 모듈 을 C #으로 포팅하는 데 찔린 후 CodePlex에 덤프하여 향후이 문제를 겪고있는 사람들이 유용하다고 생각하십시오 :

미국 주소 파서

프로젝트 홈 페이지에서 (실제로) 제한 사항에 대해 이야기하려고합니다. 유효한 거리 주소의 USPS 데이터베이스가 지원하지 않기 때문에 구문 분석이 모호 할 수 있으며 주어진 주소의 유효성을 확인하거나 거부 할 수 없습니다. 문자열에서 데이터를 꺼내려고 할 수 있습니다.

주로 올바른 필드에 데이터 세트를 가져 오거나 데이터 입력 바로 가기를 제공하려는 경우 (사용자가 여러 필드를 탭하지 않고 텍스트 상자에 주소를 붙여 넣을 수 있도록 함)를위한 것입니다. 주소의 전달 가능성을 검증하기위한 것은 아닙니다 .

거리 위의 내용을 파싱하려고 시도하지는 않지만 정규 표현식을 사용하여 합리적으로 가까운 것을 얻을 수 있습니다. 아마도 집 번호에서 깰 수 있습니다.


답변

SmartyStreets에는 임의의 입력 문자열에서 주소를 추출하는 새로운 기능이 있습니다. (참고 : SmartyStreets에서 근무하지 않습니다.)

위의 질문에 제공된 샘플 입력에서 모든 주소를 성공적으로 추출했습니다. (그런데, 그 10 개의 주소 중 9 개만 유효합니다.)

출력 결과는 다음과 같습니다.여기에 이미지 설명을 입력하십시오

다음은 동일한 요청의 CSV 형식 출력입니다.

ID,Start,End,Segment,Verified,Candidate,Firm,FirstLine,SecondLine,LastLine,City,State,ZIPCode,County,DpvFootnotes,DeliveryPointBarcode,Active,Vacant,CMRA,MatchCode,Latitude,Longitude,Precision,RDI,RecordType,BuildingDefaultIndicator,CongressionalDistrict,Footnotes
1,32,79,"2299 Lewes-Georgetown Hwy, Georgetown, DE 19947",N,,,,,,,,,,,,,,,,,,,,,,
2,81,119,"11522 Shawnee Road, Greenwood DE 19950",Y,0,,11522 Shawnee Rd,,Greenwood DE 19950-5209,Greenwood,DE,19950,Sussex,AABB,199505209226,Y,N,N,Y,38.82865,-75.54907,Zip9,Residential,S,,AL,N#
3,121,160,"144 Kings Highway, S.W. Dover, DE 19901",Y,0,,144 Kings Hwy,,Dover DE 19901-7308,Dover,DE,19901,Kent,AABB,199017308444,Y,N,N,Y,39.16081,-75.52377,Zip9,Commercial,S,,AL,L#
4,190,232,"2 Penns Way Suite 405 New Castle, DE 19720",Y,0,,2 Penns Way Ste 405,,New Castle DE 19720-2407,New Castle,DE,19720,New Castle,AABB,197202407053,Y,N,N,Y,39.68332,-75.61043,Zip9,Commercial,H,,AL,N#
5,247,285,"33 Bridle Ridge Court, Lewes, DE 19958",Y,0,,33 Bridle Ridge Cir,,Lewes DE 19958-8961,Lewes,DE,19958,Sussex,AABB,199588961338,Y,N,N,Y,38.72749,-75.17055,Zip7,Residential,S,,AL,L#
6,306,339,"2742 Pulaski Hwy Newark, DE 19711",Y,0,,2742 Pulaski Hwy,,Newark DE 19702-3911,Newark,DE,19702,New Castle,AABB,197023911421,Y,N,N,Y,39.60328,-75.75869,Zip9,Commercial,S,,AL,A#
7,341,378,"2284 Bryn Zion Road, Smyrna, DE 19904",Y,0,,2284 Bryn Zion Rd,,Smyrna DE 19977-3895,Smyrna,DE,19977,Kent,AABB,199773895840,Y,N,N,Y,39.23937,-75.64065,Zip7,Residential,S,,AL,A#N#
8,406,450,"1500 Serpentine Road, Suite 100 Baltimore MD",Y,0,,1500 Serpentine Rd Ste 100,,Baltimore MD 21209-2034,Baltimore,MD,21209,Baltimore,AABB,212092034250,Y,N,N,Y,39.38194,-76.65856,Zip9,Commercial,H,,03,N#
9,455,495,"580 North Dupont Highway Dover, DE 19901",Y,0,,580 N DuPont Hwy,,Dover DE 19901-3961,Dover,DE,19901,Kent,AABB,199013961803,Y,N,N,Y,39.17576,-75.5241,Zip9,Commercial,S,,AL,N#
10,497,525,"P.O. Box 778 Dover, DE 19903",Y,0,,PO Box 778,,Dover DE 19903-0778,Dover,DE,19903,Kent,AABB,199030778781,Y,N,N,Y,39.20946,-75.57012,Zip5,Residential,P,,AL,

나는 원래 서비스를 작성한 개발자였습니다. 우리가 구현 한 알고리즘은 여기의 특정 답변과 약간 다르지만 추출 된 각 주소는 주소 조회 API에 대해 확인되므로 유효한지 확인할 수 있습니다. 검증 된 각 결과는 보장되지만 이 스레드에서 명확하게 알 수 있듯이 때로는 인간에게도 주소를 예측할 수 없기 때문에 다른 결과가 완벽하지 않다는 것을 알고 있습니다 .


답변

나는 과거에 이것을했다.

수동으로 (사용자가 신속하게 수행하는 데 도움이되는 멋진 GUI를 작성) 자동화하거나 최근 주소 데이터베이스를 확인하고 (구입해야 함) 오류를 수동으로 처리하십시오.

수동 처리에는 각각 약 10 초가 걸리므로 시간당 3600/10 = 360을 수행 할 수 있으므로 4000은 약 11-12 시간이 걸립니다. 이를 통해 높은 정확도를 얻을 수 있습니다.

자동화를 위해서는 최근 미국 주소 데이터베이스 가 필요 하며 이에 대한 규칙을 조정하십시오. 나는 정규 표현식을 좋아하지 않는 것이 좋습니다 (장기적으로 많은 예외를 유지하기가 어렵습니다). 데이터베이스와 90 % 일치하고 나머지는 수동으로 수행하십시오.

http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf 에서 USPS (우편 주소 표준) 사본을 구하여 길이가 130 페이지 이상임을 확인 하십시오 . 그것을 구현하는 정규식은 견과류 일 것입니다.

국제 주소의 경우 모든 베팅이 해제됩니다. 미국에 기반을 둔 노동자들은 검증 할 수 없었습니다.

또는 데이터 서비스를 사용하십시오. 그러나 권장 사항이 없습니다.

또한, 우편물을 보낼 때 (즉, 맞습니까?) 봉투 (올바른 위치)에 “주소 수정 요청”을 입력 하고 데이터베이스를 업데이트 하십시오. (우리는 프론트 데스크 사람이 할 수있는 간단한 GUI를 만들었습니다. 실제로 메일을 분류하는 사람)

마지막으로 데이터를 문지른 경우 중복 항목을 찾으십시오.


답변

여기에 조언을 한 후 VB에서 다음과 같은 기능을 고안했습니다.이 기능은 항상 완벽하지는 않지만 (회사 이름과 스위트 라인이 제공되면 스위트와 도시를 결합하여) 사용 가능한 데이터를 생성합니다. 본인의 규칙 중 하나를 위반 한 것에 대해 자유롭게 의견을 말하거나 리팩터링하거나 소리 지르십시오.

Public Function parseAddress(ByVal input As String) As Collection
    input = input.Replace(",", "")
    input = input.Replace("  ", " ")
    Dim splitString() As String = Split(input)
    Dim streetMarker() As String = New String() {"street", "st", "st.", "avenue", "ave", "ave.", "blvd", "blvd.", "highway", "hwy", "hwy.", "box", "road", "rd", "rd.", "lane", "ln", "ln.", "circle", "circ", "circ.", "court", "ct", "ct."}
    Dim address1 As String
    Dim address2 As String = ""
    Dim city As String
    Dim state As String
    Dim zip As String
    Dim streetMarkerIndex As Integer

    zip = splitString(splitString.Length - 1).ToString()
    state = splitString(splitString.Length - 2).ToString()
    streetMarkerIndex = getLastIndexOf(splitString, streetMarker) + 1
    Dim sb As New StringBuilder

    For counter As Integer = streetMarkerIndex To splitString.Length - 3
        sb.Append(splitString(counter) + " ")
    Next counter
    city = RTrim(sb.ToString())
    Dim addressIndex As Integer = 0

    For counter As Integer = 0 To streetMarkerIndex
        If IsNumeric(splitString(counter)) _
            Or splitString(counter).ToString.ToLower = "po" _
            Or splitString(counter).ToString().ToLower().Replace(".", "") = "po" Then
                addressIndex = counter
            Exit For
        End If
    Next counter

    sb = New StringBuilder
    For counter As Integer = addressIndex To streetMarkerIndex - 1
        sb.Append(splitString(counter) + " ")
    Next counter

    address1 = RTrim(sb.ToString())

    sb = New StringBuilder

    If addressIndex = 0 Then
        If splitString(splitString.Length - 2).ToString() <> splitString(streetMarkerIndex + 1) Then
            For counter As Integer = streetMarkerIndex To splitString.Length - 2
                sb.Append(splitString(counter) + " ")
            Next counter
        End If
    Else
        For counter As Integer = 0 To addressIndex - 1
            sb.Append(splitString(counter) + " ")
        Next counter
    End If
    address2 = RTrim(sb.ToString())

    Dim output As New Collection
    output.Add(address1, "Address1")
    output.Add(address2, "Address2")
    output.Add(city, "City")
    output.Add(state, "State")
    output.Add(zip, "Zip")
    Return output
End Function

Private Function getLastIndexOf(ByVal sArray As String(), ByVal checkArray As String()) As Integer
    Dim sourceIndex As Integer = 0
    Dim outputIndex As Integer = 0
    For Each item As String In checkArray
        For Each source As String In sArray
            If source.ToLower = item.ToLower Then
                outputIndex = sourceIndex
                If item.ToLower = "box" Then
                    outputIndex = outputIndex + 1
                End If
            End If
            sourceIndex = sourceIndex + 1
        Next
        sourceIndex = 0
    Next
    Return outputIndex
End Function

parseAddress“AP Croll & Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947″기능을 전달하면 다음이 반환됩니다.

2299 Lewes-Georgetown Hwy
A. P. Croll & Son
Georgetown
DE
19947

답변

저는 약 5 년 동안 주소 처리 도메인에서 일해 왔으며 실제로은 총알이 없습니다. 올바른 솔루션은 데이터의 가치에 달려 있습니다. 그다지 가치가 없다면 다른 답변에서 제안한대로 파서를 통해 던져보십시오. 다소 가치가 있다면 파서의 모든 결과를 인간이 평가 / 수정해야합니다. 완전 자동화되고 반복 가능한 솔루션을 찾고 있다면 Group1 또는 Trillium과 같은 주소 수정 공급 업체에 문의하십시오.