You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
2.9 KiB
75 lines
2.9 KiB
# -*- coding: utf-8 -*-
|
|
# Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""A collection of classes/functions to manipulate strings."""
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
|
|
import bisect
|
|
|
|
|
|
class StringTooLongError(Exception):
|
|
"""Raised when string is too long to manipulate."""
|
|
|
|
|
|
def join_longest_with_length_limit(string_list, length_limit, separator='',
|
|
do_join=True):
|
|
"""Join strings to meet length limit and yield results.
|
|
|
|
Join strings from |string_list| using |separator| and yield the results.
|
|
Each result string should be as long as possible while still shorter than
|
|
|length_limit|. In other words, this function yields minimum number of
|
|
result strings.
|
|
|
|
An error will be raised when any string in |string_list| is longer than
|
|
|length_limit| because the result string joined must be longer than
|
|
|length_limit| in any case.
|
|
|
|
@param string_list: A list of strings to be joined.
|
|
@param length_limit: The maximum length of the result string.
|
|
@param separator: The separator to join strings.
|
|
@param do_join: join the result list to string if True, else just return the
|
|
result list.
|
|
|
|
@yield The result string.
|
|
@throws StringTooLongError when any string in |string_list| is longer than
|
|
|length_limit|."""
|
|
# The basic idea is, always select longest string which shorter than length
|
|
# limit, then update the limit with subtracting the length of selected
|
|
# string plus separator. Repeat the process until no more strings shorter
|
|
# than the updated limit. Then yield the result and start next loop.
|
|
|
|
string_list = sorted(string_list, key=len)
|
|
# The length of longest string should shorter than the limit.
|
|
if len(string_list[-1]) > length_limit:
|
|
raise StringTooLongError('At least one string is longer than length '
|
|
'limit: %s' % length_limit)
|
|
|
|
length_list = [len(s) for s in string_list]
|
|
len_sep = len(separator)
|
|
length_limit += len_sep
|
|
# Call str.join directly when possible.
|
|
if sum(length_list) + len_sep * len(string_list) <= length_limit:
|
|
yield separator.join(string_list) if do_join else string_list
|
|
return
|
|
|
|
result = []
|
|
new_length_limit = length_limit
|
|
while string_list:
|
|
index = bisect.bisect_right(length_list,
|
|
new_length_limit - len_sep) - 1
|
|
if index < 0: # All available strings are longer than the limit.
|
|
yield separator.join(result) if do_join else result
|
|
result = []
|
|
new_length_limit = length_limit
|
|
continue
|
|
|
|
result.append(string_list.pop(index))
|
|
new_length_limit -= length_list.pop(index) + len_sep
|
|
|
|
if result:
|
|
yield separator.join(result) if do_join else result
|