이름이 지정된 범위 사용

Google Docs API에서는 이름이 지정된 범위를 사용하여 일부 수정 작업을 간소화할 수 있습니다.

이름이 지정된 범위를 만들 때는 나중에 참조할 수 있는 문서의 섹션을 식별합니다. 이름이 지정된 범위의 색인은 문서에 콘텐츠가 추가되거나 삭제될 때 자동으로 업데이트됩니다. 이렇게 하면 수정 변경사항을 추적하거나 문서를 검색할 필요가 없으므로 향후 업데이트할 텍스트를 찾는 방법이 간소화됩니다. 대신 이름이 지정된 범위를 읽고 색인을 사용하여 편집 위치를 가져올 수 있습니다.

예를 들어 이름이 지정된 범위를 만들고 여기에 'product description' 문자열이 문서에 표시되어야 한다고 가정해 보겠습니다.

이렇게 하면 설명을 바꿀 수 있습니다. 이름이 지정된 범위의 시작 색인과 끝 색인을 가져온 후 색인 사이의 텍스트를 새 콘텐츠로 업데이트하면 됩니다.

다음 예시 코드는 이름이 지정된 범위의 콘텐츠를 대체하는 도우미 함수를 구현하는 방법을 보여줍니다. 여기서 이름이 지정된 범위는 이전에 CreateNamedRangeRequest를 사용하여 만든 것입니다.

Java

  /** Replaces the text in existing named ranges. */
  static void replaceNamedRange(Docs service, String documentId, String rangeName, String newText)
      throws IOException {
    // Fetch the document to determine the current indexes of the named ranges.
    Document document = service.documents().get(documentId).execute();

    // Find the matching named ranges.
    NamedRanges namedRangeList = document.getNamedRanges().get(rangeName);
    if (namedRangeList == null) {
      throw new IllegalArgumentException("The named range is no longer present in the document.");
    }

    // Determine all the ranges of text to be removed, and at which indexes the replacement text
    // should be inserted.
    List<Range> allRanges = new ArrayList<>();
    Set<Integer> insertIndexes = new HashSet<>();
    for (NamedRange namedRange : namedRangeList.getNamedRanges()) {
      allRanges.addAll(namedRange.getRanges());
      insertIndexes.add(namedRange.getRanges().get(0).getStartIndex());
    }

    // Sort the list of ranges by startIndex, in descending order.
    allRanges.sort(Comparator.comparing(Range::getStartIndex).reversed());

    // Create a sequence of requests for each range.
    List<Request> requests = new ArrayList<>();
    for (Range range : allRanges) {
      // Delete all the content in the existing range.
      requests.add(
          new Request().setDeleteContentRange(new DeleteContentRangeRequest().setRange(range)));

      if (insertIndexes.contains(range.getStartIndex())) {
        // Insert the replacement text.
        requests.add(
            new Request()
                .setInsertText(
                    new InsertTextRequest()
                        .setLocation(
                            new Location()
                                .setSegmentId(range.getSegmentId())
                                .setIndex(range.getStartIndex()))
                        .setText(newText)));

        // Re-create the named range on the new text.
        requests.add(
            new Request()
                .setCreateNamedRange(
                    new CreateNamedRangeRequest()
                        .setName(rangeName)
                        .setRange(
                            new Range()
                                .setSegmentId(range.getSegmentId())
                                .setStartIndex(range.getStartIndex())
                                .setEndIndex(range.getStartIndex() + newText.length()))));
      }
    }

    // Make a batchUpdate request to apply the changes, ensuring the document hasn't changed since
    // we fetched it.
    BatchUpdateDocumentRequest batchUpdateRequest =
        new BatchUpdateDocumentRequest()
            .setRequests(requests)
            .setWriteControl(new WriteControl().setRequiredRevisionId(document.getRevisionId()));
    service.documents().batchUpdate(documentId, batchUpdateRequest).execute();
  }

Python

def replace_named_range(service, document_id, range_name, new_text):
    """Replaces the text in existing named ranges."""

    # Determine the length of the replacement text, as UTF-16 code units.
    # https://developers.google.com/docs/api/concepts/structure#start_and_end_index
    new_text_len = len(new_text.encode('utf-16-le')) / 2

    # Fetch the document to determine the current indexes of the named ranges.
    document = service.documents().get(documentId=document_id).execute()

    # Find the matching named ranges.
    named_range_list = document.get('namedRanges', {}).get(range_name)
    if not named_range_list:
        raise Exception('The named range is no longer present in the document.')

    # Determine all the ranges of text to be removed, and at which indices the
    # replacement text should be inserted.
    all_ranges = []
    insert_at = {}
    for named_range in named_range_list.get('namedRanges'):
        ranges = named_range.get('ranges')
        all_ranges.extend(ranges)
        # Most named ranges only contain one range of text, but it's possible
        # for it to be split into multiple ranges by user edits in the document.
        # The replacement text should only be inserted at the start of the first
        # range.
        insert_at[ranges[0].get('startIndex')] = True

    # Sort the list of ranges by startIndex, in descending order.
    all_ranges.sort(key=lambda r: r.get('startIndex'), reverse=True)

    # Create a sequence of requests for each range.
    requests = []
    for r in all_ranges:
        # Delete all the content in the existing range.
        requests.append({
            'deleteContentRange': {
                'range': r
            }
        })

        segment_id = r.get('segmentId')
        start = r.get('startIndex')
        if insert_at[start]:
            # Insert the replacement text.
            requests.append({
                'insertText': {
                    'location': {
                        'segmentId': segment_id,
                        'index': start
                    },
                    'text': new_text
                }
            })
            # Re-create the named range on the new text.
            requests.append({
                'createNamedRange': {
                    'name': range_name,
                    'range': {
                        'segmentId': segment_id,
                        'startIndex': start,
                        'endIndex': start + new_text_len
                    }
                }
            })

    # Make a batchUpdate request to apply the changes, ensuring the document
    # hasn't changed since we fetched it.
    body = {
        'requests': requests,
        'writeControl': {
            'requiredRevisionId': document.get('revisionId')
        }
    }
    service.documents().batchUpdate(documentId=document_id, body=body).execute()

이름이 지정된 범위는 문서 콘텐츠의 범위를 지정하지만 해당 콘텐츠의 일부는 아닙니다. 이름이 지정된 범위가 포함된 콘텐츠를 추출한 후 다른 위치에 삽입하면 이름이 지정된 범위는 원본 콘텐츠만 가리키고 중복 섹션은 가리키지 않습니다.