mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 16:56:42 +08:00
799 lines
34 KiB
Markdown
799 lines
34 KiB
Markdown
### Introduction
|
|
|
|
A string is actually a collection of characters. In the C language, a string is an array of the `char` type with a terminating null character `\0` at the end. Without the terminating character, it's just a character array. In C, there isn't a dedicated string variable. However, the `str` in varch encapsulates array strings, making it a string variable where you don't need to worry about the underlying storage structure. Although `str` still stores character sets in contiguous address spaces, the work related to storage is handled by the methods provided by `str`, and it also offers methods that are in line with string operations, facilitating flexible manipulation of strings. For convenience, traditional C language strings will be referred to as array strings later, and the ones provided by varch will be called `str` strings.
|
|
|
|
### Interface
|
|
|
|
#### Creation and Deletion of str String Objects
|
|
```c
|
|
str_t str_create(void *string);
|
|
void str_delete(str_t str);
|
|
#define str(str) // For more convenient use, a macro definition is wrapped around str_create
|
|
#define _str(str) // A macro definition is wrapped around str_delete, and the str is set to NULL after deletion
|
|
```
|
|
Here, **str_t** is the structure of `str`. The creation method will return an `str` object, and it will return `NULL` if the creation fails. The `string` parameter is used to pass in the initializing string. This `string` is defined as the `void *` type, so it can support passing both array strings and `str` strings for initialization (all subsequent mentions of `void *string` can support these two types of strings for initialization). The deletion method is used to delete the passed-in `str` object. The creation method and the deletion method should be used in pairs. Once created, the `str` object should be deleted when it's no longer in use.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("Hello str!"); // Use an array string for assignment construction
|
|
str_t copy = str(ss); // Use an str string for copy construction
|
|
_str(ss); // Delete str
|
|
_str(copy); // Delete str
|
|
}
|
|
```
|
|
|
|
#### str String's Array String
|
|
```c
|
|
#define str_c_str(str)
|
|
#define _S(str)
|
|
```
|
|
`str` stores strings in contiguous address spaces, and by obtaining this address, you can access the actually stored string. However, it's not recommended to modify the string as an array string in this way during use. There's also a method `char* str_data(str_t str, int pos);` that has a similar function. `str_c_str()` is implemented by using `str_data`, and it's not recommended to replace `str_c_str()` with this method. And `_S()` is a shorter version.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("Hello str!"); // Use an array string for assignment construction
|
|
str_t copy = str(ss); // Use an str string for copy construction
|
|
printf("ss: %s\r\n", _S(ss));
|
|
printf("copy: %s\r\n", _S(copy));
|
|
_str(ss); // Delete str
|
|
_str(copy); // Delete str
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss: Hello str!
|
|
copy: Hello str!
|
|
```
|
|
|
|
#### str Assignment
|
|
```c
|
|
str_t str_assign(str_t str, void *string);
|
|
```
|
|
`str` abstracts the string as a class, and the `str_assign` method performs an assignment operation similar to the `=` operator. As mentioned before, `void *string` can support both array strings and `str` strings.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str(""); // Initialize as an empty string
|
|
printf("ss: %s\r\n", _S(ss));
|
|
str_assign(ss, "Hello str!"); // Assign the value "Hello str!"
|
|
printf("ss: %s\r\n", _S(ss));
|
|
str_assign(ss, "This is the assignment method!"); // Reassign the value
|
|
printf("ss: %s\r\n", _S(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss:
|
|
ss: Hello str!
|
|
ss: This is the assignment method!
|
|
```
|
|
From this, we can see that `str` doesn't require you to worry about the size of the defined space like array strings do. Instead, `str` internally adapts to the data.
|
|
|
|
#### str Concatenation
|
|
```c
|
|
#define str_append(str,...)
|
|
```
|
|
Besides having an assignment operation similar to the `=` operator, `str` also has a concatenation operation similar to the `+=` operator. The method has variable arguments after the formal parameter, which means it can concatenate at least one string of any quantity. Again, these can be either array strings or `str` strings. The prototype of the `str_append` method is `str_t str_append_series(str_t str,...);`. The prototype function with variable arguments requires ending with `NULL`, and it's not recommended to use the prototype method.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t name = str("123456789");
|
|
str_t ident = str("qq");
|
|
str_t email = str("");
|
|
|
|
str_append(email, name, "@", ident, ".com"); // Concatenate several strings
|
|
printf("%s\r\n", str_c_str(email));
|
|
|
|
_str(name);
|
|
_str(ident);
|
|
_str(email);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
123456789@qq.com
|
|
```
|
|
|
|
#### str Insertion
|
|
```c
|
|
str_t str_insert(str_t str, int pos, void *string);
|
|
```
|
|
This method inserts a string into `str` at the specified position and returns `str` itself. `pos` is the insertion position, and `string` is the source string to be inserted.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456");
|
|
str_insert(ss, 3, "|insert|"); // Insert the string at position 3
|
|
printf("ss: %s\r\n", str_c_str(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss: 012|insert|3456
|
|
```
|
|
|
|
#### str Removal
|
|
```c
|
|
str_t str_erase(str_t str, int pos, int len);
|
|
```
|
|
This method removes a segment of characters from `str` and returns `str` itself. `pos` is the removal position, and `len` is the length of the segment to be removed.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
str_erase(ss, 3, 3); // Remove three characters starting from position 3, that is, remove "345"
|
|
printf("ss: %s\r\n", str_c_str(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss: 0126789
|
|
```
|
|
|
|
#### str Push Back and Pop Back
|
|
```c
|
|
int str_push_back(str_t str, char c);
|
|
char str_pop_back(str_t str);
|
|
```
|
|
These two methods are used to push and pop a character from the end of the `str` string respectively. The `push_back` method returns 1 for success and 0 for failure, and the `pop_back` method returns the popped character. The storage structure of `str` is similar to that of `vector`, and operations at the end are relatively efficient.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
str_push_back(ss, 'A');
|
|
printf("ss: %s\r\n", str_c_str(ss));
|
|
printf("pop %c\r\n", str_pop_back(ss));
|
|
printf("ss: %s\r\n", str_c_str(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss: 0123456789A
|
|
pop A
|
|
ss: 0123456789
|
|
```
|
|
|
|
#### str String Comparison
|
|
```c
|
|
int str_compare(str_t str, void *string);
|
|
```
|
|
This method compares `str` with another string. A return value of 0 means they are equal, -1 means `str` is less than the other string, and 1 means `str` is greater than the other string.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("01234");
|
|
str_t copy = str(ss);
|
|
printf("compare: %d\r\n", str_compare(ss, copy));
|
|
printf("compare: %d\r\n", str_compare(ss, "01233"));
|
|
printf("compare: %d\r\n", str_compare(ss, "012345"));
|
|
_str(ss);
|
|
_str(copy);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
compare: 0
|
|
compare: 1
|
|
compare: -1
|
|
```
|
|
|
|
#### str Creating a Substring
|
|
```c
|
|
str_t str_substr(str_t str, int pos, int len);
|
|
```
|
|
This method creates a new substring from `str`. Remember to call the `_str(str)` method to delete it after you finish using it. `pos` is used to pass in the starting position, and `len` is used to pass in the length of the substring to be created.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
str_t sub = str_substr(ss, 4, 2);
|
|
printf("ss: %s\r\n", str_c_str(ss));
|
|
printf("sub: %s\r\n", str_c_str(sub));
|
|
_str(ss);
|
|
_str(sub);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss: 0123456789
|
|
sub: 45
|
|
```
|
|
|
|
#### str String Length
|
|
```c
|
|
int str_length(str_t str);
|
|
```
|
|
This is the standard method for obtaining the length of an `str` string. Of course, you can also use the `strlen` method, but for `str` objects, it's not necessary because this standard method doesn't take time to measure the length as it already has the recorded length available.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
printf("length: %d\r\n", str_length(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
length: 10
|
|
```
|
|
|
|
#### str Character Reading and Writing
|
|
```c
|
|
#define str_at(str, i)
|
|
```
|
|
`str` provides operations for reading and writing characters within it, and you can use them just like elements of an array. However, remember not to modify a character to `\0`.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
printf("ss[3] = %c\r\n", str_at(ss, 3));
|
|
str_at(ss, 3) = 'A';
|
|
printf("ss: %s\r\n", _S(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss[3] = 3
|
|
ss: 012A456789
|
|
```
|
|
|
|
#### str String Searching
|
|
```c
|
|
int str_find(str_t str, void *string, int pos);
|
|
int str_rfind(str_t str, void *string, int pos);
|
|
```
|
|
These two methods are used to search for a character substring in the forward and reverse directions respectively. `string` is used to pass in the string to be searched for, and `pos` is used to pass in the starting index for the search. The return value is the position index of the substring if found, and `str_npos` is returned if not found.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
printf("find = %d\r\n", str_find(ss, "3456", 0)); // Start searching from position 0
|
|
printf("rfind = %d\r\n", str_rfind(ss, "23", str_length(ss) - 1)); // Start searching backward from the end
|
|
printf("find = %d\r\n", str_find(ss, "0000", 0)); // Search for a non-existent string
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
find = 3
|
|
rfind = 2
|
|
find = 2147483647
|
|
```
|
|
|
|
#### str Searching for Matching Character Sets
|
|
```c
|
|
int str_find_first_of(str_t str, void *string, int pos);
|
|
int str_find_first_not_of(str_t str, void *string, int pos);
|
|
int str_find_last_of(str_t str, void *string, int pos);
|
|
int str_find_last_not_of(str_t str, void *string, int pos);
|
|
```
|
|
These four methods are different from `str_find` and `str_rfind`. The `find` and `rfind` methods require a complete match of all characters, while these four methods only need to match any one of the characters in the character set to be considered a match. Similarly, `string` is used to pass in the string (the matching character set), `pos` is used to pass in the starting index for the search, and the return value is the position index of the substring if found, and `str_npos` is returned if not found. These four functions are used to find the **"first occurrence of any character in the set"**, **"first occurrence of any character not in the set"**, **"last occurrence of any character in the set"**, and **"last occurrence of any character not in the set"** respectively.
|
|
Example: To obtain the file name and the drive letter from a file path in a practical application.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("C:/workspace/project/C/00 - vlib/Project/main.c"); // Given a file path first
|
|
str_t pan = NULL; // Initialize to NULL, not constructed
|
|
str_t filename = NULL; // Initialize to NULL, not constructed
|
|
pan = str_substr(ss, 0, str_find_first_of(ss, ":", 0)); // First find the first occurrence of ":"
|
|
filename = str_substr(ss, str_find_last_of(ss, "\\/", str_length(ss)-1) + 1, str_length(ss)); // Find the last occurrence of "\\" or "/" as the path separator
|
|
printf("pan: %s\r\n", str_c_str(pan));
|
|
printf("filename: %s\r\n", str_c_str(filename));
|
|
_str(ss);
|
|
_str(pan);
|
|
_str(filename);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
pan: C
|
|
filename: main.c
|
|
```
|
|
|
|
#### str String Reversal
|
|
```c
|
|
str_t str_reverse(str_t str, int begin, int end);
|
|
```
|
|
This method reverses the entire string within the specified interval of `str`. `begin` is the starting position of the interval, and `end` is the ending position of the interval.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
str_reverse(ss, 2, 8);
|
|
printf("ss = %s\r\n", str_c_str(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss = 0187654329
|
|
```
|
|
|
|
#### str Character Replacement
|
|
```c
|
|
str_t str_replace(str_t str, int pos, int len, void *string);
|
|
```
|
|
This method replaces the characters at the specified position with a specified length by a given string.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("My name is ZhangSan!");
|
|
printf("ss = %s\r\n", str_c_str(ss));
|
|
str_replace(ss, 11, 8, "Lisi"); // Replace "ZhangSan" with "Lisi"
|
|
printf("ss = %s\r\n", str_c_str(ss));
|
|
_str(ss);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
ss = My name is ZhangSan!
|
|
ss = My name is Lisi!
|
|
```
|
|
|
|
#### str String Swap
|
|
```c
|
|
void str_swap(str_t str, str_t swap);
|
|
```
|
|
This method swaps two strings.
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t s1 = str("This s1!");
|
|
str_t s2 = str("This s2!");
|
|
str_swap(s1, s2);
|
|
printf("s1 = %s\r\n", str_c_str(s1));
|
|
printf("s2 = %s\r\n", str_c_str(s2));
|
|
_str(s1);
|
|
_str(s2);
|
|
}
|
|
```
|
|
**Results**:
|
|
```
|
|
s1 = This s2!
|
|
s2 = This s1!
|
|
```
|
|
|
|
### Copying str String to an Array
|
|
- **Function Functionality and Parameter Introduction**:
|
|
- The function `int str_copy(str_t str, int pos, int len, char *buf);` is used to copy the characters at the specified position in the `str` string to the specified array `buf`. Among them, the parameter `pos` indicates the starting position of the copy, `len` represents the maximum length of the copy, and `buf` is the array used to receive the copied data. The function will finally return the actual length of the copied content.
|
|
- For example, in the following test code:
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("0123456789");
|
|
char array[32] = { 0 };
|
|
int length = 0;
|
|
length = str_copy(ss, 5, sizeof(array), array); // Start copying from the position, and the maximum length of the copy is the length of the array
|
|
array[length] = '\0'; // Add a terminator at the end for easy printing
|
|
printf("length = %d, copy: %s\r\n", length, array);
|
|
_str(ss);
|
|
}
|
|
```
|
|
The running result is:
|
|
```
|
|
length = 5, copy: 56789
|
|
```
|
|
From the code execution process, it starts copying from the 5th position of the `str` object `ss`. Since the set maximum copy length is the length of the `array` array (which is long enough here), 5 characters "56789" are actually copied to the `array` array. Then, a terminator `\0` is added and the result is printed. Finally, the `str` object is correctly released.
|
|
|
|
### String Formatting of str
|
|
- **Function Functionality and Features**:
|
|
- The function `str_t str_format(str_t str, char *format,...);` realizes the string formatting operation of `str`. It borrows from and rewrites the `sprintf` function. Its usage is basically the same as that of `sprintf`, but it also has unique features. It adds the function that the format specifier `%s` can support the `str_t` type. This means that when formatting a string, `%s` can not only support the `char*` type string as traditionally, but also support the `str_t` object, achieving compatibility between the two string types. In addition, the `str_format` function will perform segmented dynamic expansion during the formatting process. Users don't need to worry about whether the length of the `buf` exceeds the limit as when using the `sprintf` function, making the operation more convenient.
|
|
- Here is a test example:
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t ss = str("");
|
|
str_t pp = str("format");
|
|
str_format(ss, "Hello str! %s %s! int:%d, float:%.2f, char:%c", pp, "function", 18, 175.5, 'A');
|
|
printf("ss: %s\r\n", str_c_str(ss));
|
|
_str(ss);
|
|
_str(pp);
|
|
}
|
|
```
|
|
The output result is:
|
|
```
|
|
ss: Hello str! format function! int:18, float:175.50, char:A
|
|
```
|
|
From this example, it can be clearly seen that when using the `str_format` function, there is no need to consider the space size required for the content to be formatted in advance. As long as the parameters are passed in according to the format requirements, the formatting operation can be successfully completed, which is very convenient.
|
|
|
|
## Reference Examples
|
|
### Example 1: Judging Palindrome Strings
|
|
- **Code Logic and Implementation Steps**:
|
|
- The following is the code for judging whether the input string is a palindrome:
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t s = str("");
|
|
str_t r = str("");
|
|
char buf[64] = {0};
|
|
while (1)
|
|
{
|
|
scanf("%s", buf); // Input a string
|
|
str_assign(s, buf); // Assign the input string to s
|
|
str_assign(r, s); // Assign s to r
|
|
str_reverse(r, 0, str_length(r) - 1); // Reverse r
|
|
if (str_compare(s, r) == 0) // Compare whether s and r are the same
|
|
{
|
|
printf("This is a palindrome string!\r\n");
|
|
}
|
|
else
|
|
{
|
|
printf("This is not a palindrome string!\r\n");
|
|
}
|
|
}
|
|
_str(s);
|
|
_str(r);
|
|
}
|
|
```
|
|
- This code continuously receives the strings input by the user in a loop. First, it assigns the input string to `s` through the `str_assign` function, then assigns `s` to `r`. Next, it uses the `str_reverse` function to reverse the `r` string. Finally, it compares whether `s` and the reversed `r` are the same through the `str_compare` function. If they are the same, it outputs that it is a palindrome string; if not, it outputs that it is not a palindrome string. For example, when inputting "qwewq", after the above operations, the comparison result is equal, and it will output "This is a palindrome string!". When inputting "zxcvfd", the comparison result is not equal, and it will output "This is not a palindrome string!".
|
|
|
|
### Example 2: Deleting Repeated Characters in a String
|
|
- **Code Logic and Implementation Idea**:
|
|
- The following is the code for deleting repeated characters in a string:
|
|
```c
|
|
void test(void)
|
|
{
|
|
str_t s = str("");
|
|
str_t r = str("");
|
|
char buf[64] = {0};
|
|
|
|
while (1)
|
|
{
|
|
scanf("%s", buf); // Input a string
|
|
str_assign(s, buf); // Assign the input string to s
|
|
str_assign(r, ""); // Set r to an empty string
|
|
for (int i = 0, n = str_length(s); i < n; i++) // Traverse s
|
|
{
|
|
char t[2] = {str_at(s, i),0}; // Take out the character of s and form a short array string
|
|
if (str_find(r, t, 0) == str_npos) // Judge whether this array string is in r, that is, judge whether the character of s is in r
|
|
{
|
|
str_append(r, t); // If it is not in r, append it to r
|
|
}
|
|
}
|
|
printf("%s\r\n", _S(r));
|
|
}
|
|
|
|
_str(s);
|
|
_str(r);
|
|
}
|
|
```
|
|
- The code first receives the string input by the user and assigns it to `s`, and initializes `r` as an empty string. Then, it traverses each character of the `s` string. Each time, it takes out a character to form a short array string `t`, and then uses the `str_find` function to judge whether `t` has appeared in `r`. If it has not appeared (that is, returns `str_npos`), it uses the `str_append` function to append the character to `r`. Finally, it outputs the processed `r` string, thus realizing the function of removing repeated characters. For example, when inputting "16783679816488742135468794", after processing, it will output "167839425", and the repeated characters are removed.
|
|
|
|
## Source Code Analysis
|
|
### str Structure
|
|
- **Purpose of Structure Design and Meaning of Members**:
|
|
- The structures of the `str` container are implicit. Such a design ensures the independence and security of the module, avoids external arbitrary access and modification of structure members, and thus prevents the destruction of the `str` storage structure. Its type declaration is `typedef struct STR *str_t;`. When using it, `str_t` can be used to represent this structure type.
|
|
- The specific definition of the `STR` structure is as follows:
|
|
```c
|
|
typedef struct STR
|
|
{
|
|
char ident; /* ident of str */
|
|
char* base; /* base address of string */
|
|
int length; /* length of str */
|
|
int capacity; /* capacity of str */
|
|
} STR;
|
|
```
|
|
- The `ident` member in the structure is the basis for distinguishing between array strings and `str` strings. The `base` member points to the base address where the string is actually stored. The `length` represents the size of `str`, that is, the length of the string. The `capacity` is the space capacity allocated for actually storing the string. However, in general usage scenarios, there is no need to pay special attention to capacity-related matters.
|
|
|
|
### Distinguishing between Array Strings and str Strings
|
|
- **Distinguishing Mechanism and Code Implementation**:
|
|
- Array strings and `str` strings are distinguished by the `ident` member in the `STR` structure. The distinguishing code is as follows:
|
|
```c
|
|
typedef struct
|
|
{
|
|
char *base;
|
|
int length;
|
|
} str_info_t;
|
|
|
|
static str_info_t string_info(void *string)
|
|
{
|
|
str_info_t info;
|
|
|
|
if (((char *)string)[0] == 0) /* empty array string */
|
|
{
|
|
info.base = (char *)string;
|
|
info.length = 0;
|
|
}
|
|
else if (((str_t)string)->ident == ident()) /* str type */
|
|
{
|
|
info.base = ((str_t)string)->base;
|
|
info.length = ((str_t)string)->length;
|
|
}
|
|
else /* array string type */
|
|
{
|
|
info.base = (char *)string;
|
|
info.length = strlen((char *)string);
|
|
}
|
|
|
|
return info;
|
|
}
|
|
```
|
|
- Because `ident` is in the first position of the structure, when `void *string` is passed in, the content of the first byte of its address is first judged. If it is `\0`, it is an empty array string. If it is `ident()` (which is defined as `-1`), it represents an `str` string. Other values represent array strings. The reason for choosing `-1` to distinguish is that in common ASCII encoding (the character range is `0 ~ 127`), Unicode encoding (the range is `0x0000 ~ 0x10FFFF`), and UTF-8 encoding, there will be no situation where it starts with `-1` (the corresponding unsigned value is `0xFF`). So it is feasible to use it as a distinguishing mark in these common encoding scenarios. In the APIs related to `str`, whenever it is necessary to distinguish the string type, the `string_info` function will be called to obtain the `str_info_t` type information containing the base address and length of the string.
|
|
|
|
### str Self-Adapting Length
|
|
- **Implementation Principle Taking the Assignment Function as an Example**:
|
|
- Looking at the self-adapting length mechanism of `str` from the `str_assign` assignment function, the code is as follows:
|
|
```c
|
|
str_t str_assign(str_t str, void *string)
|
|
{
|
|
str_info_t info;
|
|
if (!str) return NULL;
|
|
if (!string) return NULL;;
|
|
info = string_info(string); // Obtain the information (base address and length) of the string
|
|
if (!str_alter_capacity(str, info.length)) return NULL; // Adjust the capacity according to the length
|
|
strcpy(str->base, info.base); // Copy the string to the str space
|
|
str->length = info.length; // Update the length
|
|
return str;
|
|
}
|
|
```
|
|
- First, it will obtain the relevant information (base address and length) of the string to be assigned through the `string_info` function. Then, it calls the `str_alter_capacity` function to adjust the capacity of the target `str` object according to the obtained length. If the capacity adjustment is successful, it copies the source string to the space of the target `str` object and updates its length. Finally, it returns the adjusted `str` object. If the capacity adjustment fails, it returns `NULL`. Inside the `str_alter_capacity` function, it will first judge whether the capacity needs to be changed. If the `str` object has not been allocated space originally, it will allocate new space through `malloc`. If there is already space, it will reallocate space through `realloc` and finally update the capacity value and return the corresponding result.
|
|
|
|
### str Obtaining the Actual Stored Data
|
|
- **Function Implementation and Error Prevention Mechanism**:
|
|
- The function `char* str_data(str_t str, int pos)` is used to obtain the address of the character at the specified index position in the `str` object. Functions such as `str_at`, `str_c_str`, and `_S` are all implemented based on it. The code is as follows:
|
|
```c
|
|
char* str_data(str_t str, int pos)
|
|
{
|
|
if (!str || pos < 0 || pos >= str->length)
|
|
{
|
|
error = 0; /* reset error area */
|
|
return &error;
|
|
}
|
|
return &str->base[pos];
|
|
}
|
|
```
|
|
- The function will first perform parameter validity judgment. If the passed-in `str` object is empty or the specified position `pos` is illegal (less than 0 or exceeding the string length range), it will reset a static variable `error` defined in the module and return the address of `error`. This is to prevent the situation that returning `NULL` causes the program to crash due to subsequent operations on the null address. If the parameters are legal, it will return the address after offsetting the address pointed to by `base` in the `str` structure by `pos` positions, that is, the address of the character at the corresponding index position.
|
|
|
|
### str String Replacement Principle
|
|
- **Key Steps and Logical Processing in the Replacement Operation**:
|
|
- The function `str_t str_replace(str_t str`, which involves the following key aspects in the replacement operation:
|
|
```c
|
|
str_t str_replace(str_t str, int pos, int len, void *string)
|
|
{
|
|
str_info_t info;
|
|
char *overlap = NULL;
|
|
if (!str) return NULL;
|
|
if (!string) return NULL;
|
|
if (pos < 0 || pos > str->length) return NULL;
|
|
if (len > str->length - pos) len = str->length - pos;
|
|
info = string_info(string);
|
|
|
|
/* Check if addresses overlap
|
|
This step is to check whether the addresses overlap, that is, whether the address of the string information overlaps with the original address of str.
|
|
The processing mechanism for overlap is to allocate a piece of space, copy the content to be replaced,
|
|
and then replace the copied content later.
|
|
*/
|
|
if (str->base <= info.base && info.base <= str->base + str->length && pos < str->length)
|
|
{
|
|
overlap = (char *)malloc(info.length + 1);
|
|
if (!overlap) return NULL;
|
|
strcpy(overlap, info.base);
|
|
info.base = overlap;
|
|
}
|
|
|
|
/*
|
|
This step judges the length relationship between the string to be replaced and the replacing string, that is, judges whether the capacity needs to be adjusted.
|
|
*/
|
|
if (info.length > len) // The replacing string is longer and needs to expand the capacity
|
|
{
|
|
/* First expand the capacity, and the expanded capacity is the length difference */
|
|
if (str_alter_capacity(str, str->length + (info.length - len)) == 0)
|
|
{
|
|
if (overlap) free(overlap);
|
|
return NULL;
|
|
}
|
|
/* Move the latter part of the data backward to make room for storing the longer replacing string */
|
|
memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len));
|
|
/* Copy the replacing string into the vacated space */
|
|
memcpy(&str->base[pos], info.base, info.length);
|
|
}
|
|
else if (info.length < len) /* The length becomes smaller */
|
|
{
|
|
/* Move the latter data forward, leaving the length of info for the replacing string */
|
|
memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len));
|
|
/* Copy the replacing string into the vacated space */
|
|
memcpy(&str->base[pos], info.base, info.length);
|
|
/* For shrinking, adjust the capacity later */
|
|
str_alter_capacity(str, str->length + (info.length - len));
|
|
}
|
|
else /* The length has not changed */
|
|
{
|
|
/* Directly overwrite */
|
|
memcpy(&str->base[pos], info.base, info.length);
|
|
}
|
|
str->length += (info.length - len);
|
|
str->base[str->length] = 0;
|
|
if (overlap) free(overlap);
|
|
|
|
return str;
|
|
}
|
|
```
|
|
The function first performs some basic parameter validity checks and obtains information about the string to be inserted through the `string_info` function. Then, it checks whether the addresses of the original string and the string to be inserted overlap. If there is an overlap, it will allocate additional space to handle it properly. Next, it compares the lengths of the two strings to determine whether to expand, shrink, or directly overwrite the capacity of the original string. Finally, it updates the length of the string in the `str` object, adds a terminator, and releases the temporarily allocated space if necessary, and then returns the modified `str` object. And the functions `str_insert` and `str_erase` are implemented based on the `str_replace` function. For example, `str_insert` is implemented by calling `str_replace` with a length of 0 for the replacement part, and `str_erase` is implemented by calling `str_replace` with an empty string for the replacement part.
|
|
|
|
### Principle of str Concatenation
|
|
The actual operation is to call the `str_append_series` function for parameter passing, and append a `NULL` at the end of the variable arguments.
|
|
|
|
Inside the `str_append_series` function, it processes the variable arguments. It inserts each segment of strings to the end of the `str` one by one through the `str_insert` function to complete the concatenation. The specific code is as follows:
|
|
```c
|
|
#define str_append(str,...) str_append_series((str), ##__VA_ARGS__, NULL)
|
|
str_t str_append_series(str_t str,...)
|
|
{
|
|
va_list args;
|
|
void* s = NULL;
|
|
if (!str) return NULL;
|
|
va_start(args, str);
|
|
s = va_arg(args, void*);
|
|
while (s)
|
|
{
|
|
/* Insert at the end */
|
|
if (!str_insert(str, str->length, s))
|
|
{
|
|
va_end(args);
|
|
return NULL;
|
|
}
|
|
s = va_arg(args, void*);
|
|
}
|
|
va_end(args);
|
|
return str;
|
|
}
|
|
```
|
|
|
|
### Source Code of str Formatting
|
|
The source code of the `str_format` function is relatively long. The general process is to traverse the `format` string. When encountering the `%` symbol, specific formatting operations will be carried out. Taking the `%` symbol as the delimiter, the entire `format` string is divided into segments for dynamic expansion and assignment. The detailed code is shown below:
|
|
```c
|
|
str_t str_format(str_t str, char *format,...)
|
|
{
|
|
va_list args;
|
|
int i = 0;
|
|
int len = 0; /* length of sub string */
|
|
char *s = NULL; /* temp string */
|
|
str_info_t info; /* str info */
|
|
double dbl = 0.0; /* double precision floating point */
|
|
char *begin = NULL; /* begin of format */
|
|
char qualifier = 0; /* conversion qualifier for integer as 'h','l','L' */
|
|
int width = 0; /* transition field width */
|
|
int precision = 0; /* minimum digits of integers and maximum digits of strings */
|
|
char tfmt[16] = "%"; /* temporary format */
|
|
|
|
if (!str) return NULL;
|
|
str_assign(str, "");
|
|
va_start(args, format);
|
|
for (; *format; format++)
|
|
{
|
|
/* When it is an ordinary character */
|
|
if (*format!= '%')
|
|
{
|
|
if (!begin) begin = format; // Record the starting position of the ordinary character segment
|
|
continue; // Skip this loop iteration and move to the next one
|
|
}
|
|
|
|
/* If the starting position of the ordinary character has been recorded, it means the previous part of the current position is ordinary characters */
|
|
if (begin)
|
|
{
|
|
len = format - begin; // Record the length for later expansion
|
|
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
|
|
while (len--) str->base[str->length++] = *begin++; // Copy the ordinary characters in
|
|
begin = NULL;
|
|
}
|
|
|
|
/* Start processing the formatted data segment from here */
|
|
begin = format;
|
|
while (1) // Record the prefix symbol flag
|
|
{
|
|
/* Also skips the first '%' */
|
|
format++;
|
|
if ((*format!= '0') &&
|
|
(*format!= ' ') &&
|
|
(*format!= '+') &&
|
|
(*format!= '-') &&
|
|
(*format!= '#')) break;
|
|
}
|
|
|
|
/* Get field width */
|
|
width = -1;
|
|
if (is_digit(*format))
|
|
{
|
|
format += get_digit(format, &width);
|
|
}
|
|
else if (*format == '*')
|
|
{
|
|
format++;
|
|
width = va_arg(args, int);
|
|
if (width < 0) width = -width;
|
|
}
|
|
|
|
/* Get the precision */
|
|
precision = -1;
|
|
if (*format == '.')
|
|
{
|
|
format++;
|
|
if (is_digit(*format)) format += get_digit(format, &precision);
|
|
else if (*format == '*')
|
|
{
|
|
format++;
|
|
precision = va_arg(args, int);
|
|
}
|
|
if (precision < 0) precision = 0;
|
|
}
|
|
|
|
/* Get the conversion qualifier */
|
|
qualifier = 0;
|
|
if (*format == 'h' || *format == 'l' || *format == 'L')
|
|
{
|
|
qualifier = *format;
|
|
format++;
|
|
if (qualifier == 'l' && *format == 'l')
|
|
{
|
|
qualifier = 'L';
|
|
format++;
|
|
}
|
|
}
|
|
|
|
/* Format distribution */
|
|
switch (*format)
|
|
{
|
|
case 'c':
|
|
// Estimate how much space is needed
|
|
// Call sprintf to append a character at the end
|
|
...
|
|
case 's':
|
|
// Estimate how much space is needed
|
|
// Call sprintf to append a segment of string at the end
|
|
...
|
|
case 'p':
|
|
// Estimate how much space is needed
|
|
// Call sprintf to append a pointer at the end
|
|
...
|
|
case 'a':
|
|
case 'A':
|
|
case 'e':
|
|
case 'E':
|
|
case 'g':
|
|
case 'G':
|
|
case 'f':
|
|
// Estimate how much space is needed
|
|
// Call sprintf to append a floating-point number at the end
|
|
...
|
|
case 'o':
|
|
case 'X':
|
|
case 'x':
|
|
case 'd':
|
|
case 'i':
|
|
case 'u':
|
|
// Estimate how much space is needed
|
|
// Call sprintf to append an integer at the end
|
|
...
|
|
case '%':
|
|
// Push in %
|
|
...
|
|
default:
|
|
// Push in %
|
|
...
|
|
}
|
|
}
|
|
|
|
/* Copy the tail string to str */
|
|
if (begin)
|
|
{
|
|
len = format - begin;
|
|
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
|
|
while (len--) str->base[str->length++] = *begin++;
|
|
}
|
|
|
|
if (!str_alter_capacity(str, str->length)) goto FAIL_CAPACITY;
|
|
str->base[str->length] = '\0';
|
|
va_end(args);
|
|
return str;
|
|
|
|
FAIL_CAPACITY:
|
|
str->base[str->length] = '\0';
|
|
va_end(args);
|
|
return NULL;
|
|
}
|
|
```
|